/* Copyright (c) 1995-1998 by Cisco systems, Inc. Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies of the software and supporting documentation, the name of Cisco Systems, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that modification, copying and distribution is by permission of Cisco Systems, Inc. Cisco Systems, Inc. makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "tac_plus.h" #include "choose_authen.h" #include "expire.h" #include "enable.h" #include "report.h" #include "cfgfile.h" #include "default_fn.h" #include "default_v0_fn.h" #include "sendauth.h" #include "sendpass.h" #include "packet.h" #include "main.h" #include "do_author.h" /* for "struct identity" */ #ifdef SKEY #include "skey_fn.h" #endif static int choose_sendpass TAC_ARGS((struct authen_data *data, struct authen_type *type)); static int choose_sendauth TAC_ARGS((struct authen_data *data, struct authen_type *type)); static int choose_login TAC_ARGS((struct authen_data *data, struct authen_type *type)); #if 0 /* unused */ static int get_minor_version() { return(session.version & ~TAC_PLUS_MAJOR_VER_MASK); } #endif /* unused */ /* * Choose an authentication function. Return CHOOSE_OK if chosen, * CHOOSE_GETUSER if we need a username, CHOOSE_FAILED on failure */ int choose_authen TAC_ARGS((struct authen_data *data, struct authen_type *type)); int choose_authen(data, type) struct authen_data *data; struct authen_type *type; { char *name = data->NAS_id->username; switch (data->action) { case TAC_PLUS_AUTHEN_SENDPASS: return(choose_sendpass(data, type)); case TAC_PLUS_AUTHEN_SENDAUTH: return(choose_sendauth(data, type)); case TAC_PLUS_AUTHEN_LOGIN: /* For enabling, enable_fn handles everything. Must be minor * version zero */ if (data->service == TAC_PLUS_AUTHEN_SVC_ENABLE) { if (session.version != TAC_PLUS_VER_0) { /* must be version 0 */ break; } type->authen_func = enable_fn; strcpy(type->authen_name, "enable_fn"); return (CHOOSE_OK); } return(choose_login(data, type)); case TAC_PLUS_AUTHEN_CHPASS: /* we don't support chpass */ return(CHOOSE_FAILED); default: break; } /* never heard of this lot */ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d", session.peer, session.port, name ? name : "", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); } static int choose_login TAC_ARGS((struct authen_data *data, struct authen_type *type)); /* Choose an authentication function for action == LOGIN, service != enable */ static int choose_login(data, type) struct authen_data *data; struct authen_type *type; { const char *name = data->NAS_id->username; const char *cfg_passwd; switch(type->authen_type) { case TAC_PLUS_AUTHEN_TYPE_ASCII: if (session.version != TAC_PLUS_VER_0) { break; } if (!name[0]) { /* request a user name if not already supplied */ return (CHOOSE_GETUSER); } /* Does this user require s/key? */ cfg_passwd = cfg_get_login_secret(name, TAC_PLUS_RECURSE); if (cfg_passwd && STREQ(cfg_passwd, "skey")) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "%s %s: user %s requires skey", session.peer, session.port, name); #ifdef SKEY type->authen_func = skey_fn; strcpy(type->authen_name, "skey_fn"); return (CHOOSE_OK); #else /* SKEY */ report(LOG_ERR, "%s %s: user %s s/key support has not been compiled in", name ? name : "", session.peer, session.port); return(CHOOSE_FAILED); #endif /* SKEY */ } /* Not an skey user. Must be none, des, cleartext or file password */ type->authen_func = default_fn; strcpy(type->authen_name, "default_fn"); return (CHOOSE_OK); case TAC_PLUS_AUTHEN_TYPE_ARAP: #ifndef ARAP_DES /* * If we have no des code we can't do ARAP via SENDAUTH. We'll * have to do it via SENDPASS. Return a down-rev reply * packet and hope the NAS is smart enough to deal with it. */ session.version = TAC_PLUS_VER_0; report(LOG_ERR, "%s %s: user %s DES is unavailable", name ? name : "", session.peer, session.port); return (CHOOSE_FAILED); #endif /* ARAP_DES */ /* FALLTHROUGH */ #ifdef MSCHAP case TAC_PLUS_AUTHEN_TYPE_MSCHAP: #ifndef MSCHAP_DES /* * If we have no des code we can't do MSCHAP via LOGIN. We'll * have to do it via SENDPASS. Return a down-rev reply * packet and hope the NAS is smart enough to deal with it. */ session.version = TAC_PLUS_VER_0; report(LOG_ERR, "%s %s: user %s DES is unavailable", name ? name : "", session.peer, session.port); return (CHOOSE_FAILED); #endif /* MSCHAP_DES */ /* FALLTHROUGH */ #endif /* MSCHAP */ case TAC_PLUS_AUTHEN_TYPE_PAP: case TAC_PLUS_AUTHEN_TYPE_CHAP: if (session.version == TAC_PLUS_VER_0) { type->authen_func = default_v0_fn; strcpy(type->authen_name, "default_v0_fn"); return (CHOOSE_OK); } /* Version 1 login/[pap|chap|arap]. * The username must in the initial START packet */ if (!name[0]) { report(LOG_ERR, "%s %s: No user in START packet for PAP/CHAP/ARAP", session.peer, session.port); return (CHOOSE_FAILED); } type->authen_func = default_fn; strcpy(type->authen_name, "default_fn"); return (CHOOSE_OK); default: break; } /* Illegal value combination */ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d", session.peer, session.port, name ? name : "", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); } static int choose_sendauth TAC_ARGS((struct authen_data *data, struct authen_type *type)); static int choose_sendauth(data, type) struct authen_data *data; struct authen_type *type; { char *name = data->NAS_id->username; switch (type->authen_type) { #ifdef MSCHAP case TAC_PLUS_AUTHEN_TYPE_MSCHAP: #ifndef MSCHAP_DES /* * If we have no des code we can't do MSCHAP via SENDAUTH. We'll * have to do it via SENDPASS. Return a down-rev reply * packet and hope the NAS is smart enough to deal with it. */ session.version = TAC_PLUS_VER_0; report(LOG_ERR, "%s %s: user %s DES is unavailable", name ? name : "", session.peer, session.port); return (CHOOSE_FAILED); #endif /* MSCHAP_DES */ /* FALLTHROUGH */ #endif /* MSCHAP */ case TAC_PLUS_AUTHEN_TYPE_CHAP: case TAC_PLUS_AUTHEN_TYPE_PAP: /* Must be minor version 1 */ if (session.version != TAC_PLUS_VER_1) { break; } /* The start packet must contain the username */ if (!name[0]) { return (CHOOSE_FAILED); } type->authen_func = sendauth_fn; strcpy(type->authen_name, "sendauth_fn"); return (CHOOSE_OK); default: break; } /* Illegal value combination */ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d", session.peer, session.port, name ? name : "", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); } static int choose_sendpass TAC_ARGS((struct authen_data *data, struct authen_type *type)); /* Compatibility routine for (obsolete) minor version == 0 */ static int choose_sendpass(data, type) struct authen_data *data; struct authen_type *type; { char *name = data->NAS_id->username; switch (type->authen_type) { case TAC_PLUS_AUTHEN_TYPE_CHAP: #ifdef MSCHAP case TAC_PLUS_AUTHEN_TYPE_MSCHAP: #endif /* MSCHAP */ case TAC_PLUS_AUTHEN_TYPE_PAP: case TAC_PLUS_AUTHEN_TYPE_ARAP: /* must be minor version 0 */ if (TAC_PLUS_VER_0 != session.version) { break; } /* We need a username */ if (!name[0]) { return (CHOOSE_GETUSER); } type->authen_func = sendpass_fn; strcpy(type->authen_name, "sendpass_fn"); return (CHOOSE_OK); default: break; } /* Illegal value combination */ report(LOG_ERR, "%s: %s %s Illegal packet ver=%d action=%d type=%d", session.peer, session.port, name ? name : "", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); }