tacacs-F4.0.4.28/enable.c (161 lines of code) (raw):
/*
* 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"
/* internal state variables */
#define STATE_AUTHEN_START 0 /* no requests issued */
#define STATE_AUTHEN_GETUSER 1 /* username has been requested */
#define STATE_AUTHEN_GETPASS 2 /* password has been requested */
struct private_data {
char password[MAX_PASSWD_LEN + 1];
int state;
};
static void
enable(char *passwd, struct authen_data *data)
{
int level = data->NAS_id->priv_lvl;
char *username = data->NAS_id->username;
char *cfg_passwd;
#ifdef UENABLE
char *exp_date;
#endif
/* sanity check */
if (level < TAC_PLUS_PRIV_LVL_MIN || level > TAC_PLUS_PRIV_LVL_MAX) {
data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
data->server_msg = tac_strdup("Invalid privilege level in packet");
report(LOG_ERR, "%s level=%d %s", session.peer, level,
data->server_msg);
return;
}
#ifdef UENABLE
/* if the user has a user-specific enable password, check it */
cfg_passwd = cfg_get_enable_secret(username, TAC_PLUS_RECURSE);
if (cfg_passwd != NULL) {
if ((verify_pwd(username, passwd, data, cfg_passwd))) {
exp_date = cfg_get_expires(username, TAC_PLUS_RECURSE);
set_expiration_status(exp_date, data);
goto SUCCESS;
} else
goto FAIL;
}
#endif
/* if a host-specific enable password exists, check it */
cfg_passwd = cfg_get_host_enable(data->NAS_id->NAS_ip);
if (cfg_passwd == NULL &&
!STREQ(data->NAS_id->NAS_name, data->NAS_id->NAS_ip)) {
cfg_passwd = cfg_get_host_enable(data->NAS_id->NAS_name);
}
if (cfg_passwd != NULL) {
if ((verify_pwd(username, passwd, data, cfg_passwd)))
goto SUCCESS;
else
goto FAIL;
}
/* 0 <= level <= 14: look for $enab<n>$ and verify */
if (level < TAC_PLUS_PRIV_LVL_MAX) {
char buf[11];
snprintf(buf, sizeof(buf), "$enab%d$", level);
if (!verify(buf, passwd, data, TAC_PLUS_NORECURSE))
data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
goto SUCCESS;
}
/*
* 2). level=15. Try user/group enable password, then host password, then
* $enab15$ or $enable$ (for backwards compatibility) and verify.
*/
if (verify("$enable$", passwd, data, TAC_PLUS_NORECURSE) ||
verify("$enab15$", passwd, data, TAC_PLUS_NORECURSE)) {
goto SUCCESS;
}
FAIL:
/* return fail */
data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
SUCCESS:
return;
}
/*
* Tacacs enable authentication function. Wants an enable
* password, and tries to verify it.
*
* Choose_authen will ensure that we already have a username before this
* gets called.
*
* We will query for a password and keep it in the method_data.
*
* Any strings returned via pointers in authen_data must come from the
* heap. They will get freed by the caller.
*
* Return 0 if data->status is valid, otherwise 1
*/
int
enable_fn(struct authen_data *data)
{
char *passwd;
struct private_data *p;
int pwlen;
p = (struct private_data *)data->method_data;
/* An abort has been received. Clean up and return */
if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
if (data->method_data)
free(data->method_data);
data->method_data = NULL;
return(1);
}
/* Initialise method_data if first time through */
if (!p) {
p = (struct private_data *)tac_malloc(sizeof(struct private_data));
memset(p, 0, sizeof(struct private_data));
data->method_data = p;
p->state = STATE_AUTHEN_START;
}
/* As we are enabling, we do not need a username, but do we have a
* password? XXX unless we have to lookup a user-specific enable pass.
* does lack of a username have to be handled (like in choose_authen())?
*/
passwd = p->password;
if (!passwd[0]) {
/*
* No password. Either we need to ask for one and expect to get
* called again, or we asked but nothing came back, which is fatal
*/
switch (p->state) {
case STATE_AUTHEN_GETPASS:
/* We already asked for a password. This should be the reply */
if (data->client_msg) {
pwlen = MIN((int)strlen(data->client_msg), MAX_PASSWD_LEN);
} else {
pwlen = 0;
}
strncpy(passwd, data->client_msg, pwlen);
passwd[pwlen] = '\0';
/* We have a password, now try validating. Assume the worst */
data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
break;
default:
#ifdef UENABLE
/* does the user or NAS have 'nopasswd' for the enable password? */
if (cfg_get_user_noenablepwd(data->NAS_id->username,
TAC_PLUS_RECURSE)) {
data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
break;
}
if (cfg_get_host_noenablepwd(data->NAS_id->NAS_name)) {
data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
break;
}
#endif
/* Request a password */
data->flags = TAC_PLUS_AUTHEN_FLAG_NOECHO;
data->server_msg = tac_strdup("Password: ");
data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
p->state = STATE_AUTHEN_GETPASS;
return(0);
}
} else {
/* We have a password, now try validating. Assume the worst */
data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
}
#ifdef UENABLE
/* check enableacl */
if (verify_host(data->NAS_id->username, data, S_enableacl,
TAC_PLUS_RECURSE) != S_permit) {
data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
} else {
#endif
switch (data->service) {
case TAC_PLUS_AUTHEN_SVC_ENABLE:
if (data->status != TAC_PLUS_AUTHEN_STATUS_PASS)
enable(passwd, data);
if (debug) {
char *name = data->NAS_id->username;
report(LOG_INFO, "enable query for '%s' %s from %s %s",
name && name[0] ? name : "unknown",
data->NAS_id->NAS_port && data->NAS_id->NAS_port[0] ?
data->NAS_id->NAS_port : "unknown",
session.peer,
(data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
"accepted" : "rejected");
}
break;
default:
data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
report(LOG_ERR, "%s: Bogus service value %d from packet",
session.peer, data->service);
break;
}
#ifdef UENABLE
}
#endif
if (data->method_data)
free(data->method_data);
data->method_data = NULL;
switch (data->status) {
case TAC_PLUS_AUTHEN_STATUS_ERROR:
case TAC_PLUS_AUTHEN_STATUS_FAIL:
case TAC_PLUS_AUTHEN_STATUS_PASS:
return(0);
default:
report(LOG_ERR, "%s: authenticate_fn can't set status %d",
session.peer, data->status);
data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
return(1);
}
}