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);
}