tacacs-F4.0.4.28/choose_authen.c (222 lines of code) (raw):

/* * $Id: choose_authen.c,v 1.8 2009-03-18 18:59:17 heas Exp $ * * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved * 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 "expire.h" static int choose_login(struct authen_data *, struct authen_type *); static int choose_sendpass(struct authen_data *, struct authen_type *); static int choose_sendauth(struct authen_data *, struct authen_type *); /* * Choose an authentication function. Return CHOOSE_OK if chosen, * CHOOSE_GETUSER if we need a username, CHOOSE_FAILED on failure */ int choose_authen(struct authen_data *data, struct authen_type *type) { #if defined(SKEY) || defined(ACECLNT) char *cfg_passwd; #endif 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; } #if defined(SKEY) || defined(ACECLNT) if (name[0] == '\0') return(CHOOSE_GETUSER); cfg_passwd = cfg_get_enable_secret(name, TAC_PLUS_RECURSE); #endif #ifdef SKEY if (cfg_passwd != NULL && STREQ(cfg_passwd, "skey")) { type->authen_func = skey_fn; strcpy(type->authen_name, "skey_fn"); return(CHOOSE_OK); } #endif #ifdef ACECLNT if (cfg_passwd != NULL && STREQ(cfg_passwd, "aceclnt")) { type->authen_func = aceclnt_fn; strcpy(type->authen_name, "aceclnt_fn"); return(CHOOSE_OK); } #endif 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 : "<unknown>", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); } /* Choose an authentication function for action == LOGIN, service != enable */ static int choose_login(struct authen_data *data, struct authen_type *type) { char *name = data->NAS_id->username; 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 : "<unknown>", session.peer, session.port); return(CHOOSE_FAILED); #endif /* SKEY */ } /* Does this user require aceclnt */ cfg_passwd = cfg_get_login_secret(name, TAC_PLUS_RECURSE); if (cfg_passwd && STREQ(cfg_passwd, "aceclnt")) { if (debug & DEBUG_PASSWD_FLAG) report(LOG_DEBUG, "%s %s: user %s requires aceclnt", session.peer, session.port, name); #ifdef ACECLNT type->authen_func = aceclnt_fn; strcpy(type->authen_name, "aceclnt_fn"); return(CHOOSE_OK); #else /* ACECLNT */ report(LOG_ERR, "%s %s: user %s aceclnt support has not been compiled in", name ? name : "<unknown>", session.peer, session.port); return(CHOOSE_FAILED); #endif /* ACECLNT */ } /* * Not an skey or aceclnt 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 : "<unknown>", 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 : "<unknown>", 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 be 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 : "<unknown>", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); } static int choose_sendauth(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 : "<unknown>", 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 : "<unknown>", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); } /* Compatibility routine for (obsolete) minor version == 0 */ static int choose_sendpass(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 : "<unknown>", session.version, data->action, type->authen_type); return(CHOOSE_FAILED); }