mirror of https://github.com/facebook/tac_plus
327 lines
8.9 KiB
C
327 lines
8.9 KiB
C
/*
|
|
* $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);
|
|
}
|