tacacs-F4.0.4.28/aceclnt_fn.c (156 lines of code) (raw):

/* * $Id: aceclnt_fn.c,v 1.00 2012-02-24 18:40:20 maddison 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" /* 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 */ #include <acexport.h> struct private_data { SDI_HANDLE SdiHandle; char password[MAX_PASSWD_LEN + 1]; int state; }; /* * Use aceclnt to verify a supplied password using state set up earlier * when the username was supplied. */ static int aceclnt_verify(char *name, char *passwd, struct authen_data *data) { struct private_data *p = data->method_data; SDI_HANDLE SdiHandle = p->SdiHandle; int acmRet; data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; /* if (aceclntverify(aceclntp, passwd) == 0) { *//* S/Key authentication succeeded *//* data->status = TAC_PLUS_AUTHEN_STATUS_PASS; if (aceclntp->n < 5) { data->server_msg = tac_strdup("Password will expire soon"); return(1); } } */ acmRet = SD_Check(SdiHandle, passwd, name); if (acmRet == ACM_OK) data->status = TAC_PLUS_AUTHEN_STATUS_PASS; return(0); } /* * aceclnt tacacs login authentication function. Wants a username * and a password, and tries to verify them via aceclnt. * * 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 aceclnt_fn(struct authen_data *data) { #define ACEBUFSZ 256 char buf[ACEBUFSZ]; char *name, *passwd; struct private_data *p; char *prompt; int pwlen, acmRet; 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 (p->SdiHandle) { SD_Close(p->SdiHandle); } 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; p->SdiHandle = SDI_HANDLE_NONE; acmRet = SD_Init(&(p->SdiHandle)); if (acmRet != ACM_OK) { report(LOG_ERR, "%s: aceclnt_fn unable to contact ACE %d", session.peer, p->state); if (p->SdiHandle) { SD_Close(p->SdiHandle); } return(1); } } /* Unless we are enabling, we need a username */ if (data->service != TAC_PLUS_AUTHEN_SVC_ENABLE && (char)data->NAS_id->username[0] == '\0') { switch (p->state) { case STATE_AUTHEN_GETUSER: /* we have previously asked for a username but none came back. * This is a gross error */ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; report(LOG_ERR, "%s: No username supplied after GETUSER", session.peer); return(0); case STATE_AUTHEN_START: /* No username. Try requesting one */ data->status = TAC_PLUS_AUTHEN_STATUS_GETUSER; if (data->service == TAC_PLUS_AUTHEN_SVC_LOGIN) { prompt = "\nUser Access Verification\n\nUsername: "; } else { prompt = "Username: "; } data->server_msg = tac_strdup(prompt); p->state = STATE_AUTHEN_GETUSER; return(0); default: /* something awful has happened. Give up and die */ report(LOG_ERR, "%s: aceclnt_fn bad state %d", session.peer, p->state); SD_Close(p->SdiHandle); return(1); } } /* we now have a username if we needed one */ name = data->NAS_id->username; /* Do we have a password? */ passwd = p->password; if (passwd[0] == '\0') { /* no password yet. 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(strlen(data->client_msg), MAX_PASSWD_LEN); } else { pwlen = 0; } strncpy(passwd, data->client_msg, pwlen); passwd[pwlen] = '\0'; break; default: /* Request a password */ passwd = cfg_get_login_secret(name, TAC_PLUS_RECURSE); if (!passwd && !STREQ(passwd, "aceclnt")) { report(LOG_ERR, "Cannot find aceclnt password declaration for" " %s", name); data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; SD_Close(p->SdiHandle); return(1); } /* lock the ACE User */ acmRet = SD_Lock(p->SdiHandle, name); if (acmRet != ACM_OK) { report(LOG_ERR, "ACE Server name lock failed for %s", name); data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; SD_Close(p->SdiHandle); return(1); } snprintf(buf, ACEBUFSZ, "Enter PASSCODE: "); data->server_msg = tac_strdup(buf); data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS; p->state = STATE_AUTHEN_GETPASS; return(0); } } /* We have a username and password. Try validating */ /* Assume the worst */ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; switch (data->service) { case TAC_PLUS_AUTHEN_SVC_LOGIN: case TAC_PLUS_AUTHEN_SVC_ENABLE: aceclnt_verify(name, passwd, data); if (debug) report(LOG_INFO, "login/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; } if (p->SdiHandle) SD_Close(p->SdiHandle); 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: aceclnt_fn couldn't set recognizable status %d", session.peer, data->status); data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; return(1); } }