141227Sjdp/*- 241227Sjdp * Copyright 1998 Juniper Networks, Inc. 341227Sjdp * All rights reserved. 4115470Sdes * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 587398Sdes * All rights reserved. 641227Sjdp * 787398Sdes * Portions of this software were developed for the FreeBSD Project by 887398Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 987398Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1087398Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1187398Sdes * 1241227Sjdp * Redistribution and use in source and binary forms, with or without 1341227Sjdp * modification, are permitted provided that the following conditions 1441227Sjdp * are met: 1541227Sjdp * 1. Redistributions of source code must retain the above copyright 1641227Sjdp * notice, this list of conditions and the following disclaimer. 1741227Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1841227Sjdp * notice, this list of conditions and the following disclaimer in the 1941227Sjdp * documentation and/or other materials provided with the distribution. 2087398Sdes * 3. The name of the author may not be used to endorse or promote 2187398Sdes * products derived from this software without specific prior written 2287398Sdes * permission. 2341227Sjdp * 2441227Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2541227Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2641227Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2741227Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2841227Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2941227Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3041227Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3141227Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3241227Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3341227Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3441227Sjdp * SUCH DAMAGE. 3541227Sjdp */ 3641227Sjdp 3784218Sdillon#include <sys/cdefs.h> 3884218Sdillon__FBSDID("$FreeBSD$"); 3984218Sdillon 4041227Sjdp#include <sys/param.h> 4141227Sjdp 4241227Sjdp#include <pwd.h> 4341227Sjdp#include <stdlib.h> 4441227Sjdp#include <string.h> 4541227Sjdp#include <syslog.h> 4641227Sjdp#include <taclib.h> 4741227Sjdp#include <unistd.h> 4841227Sjdp 4941227Sjdp#define PAM_SM_AUTH 5087398Sdes 5190229Sdes#include <security/pam_appl.h> 5241227Sjdp#include <security/pam_modules.h> 5390229Sdes#include <security/pam_mod_misc.h> 5441227Sjdp 55115465Sdes#define PAM_OPT_CONF "conf" 56115465Sdes#define PAM_OPT_TEMPLATE_USER "template_user" 5741227Sjdp 5841227Sjdptypedef int (*set_func)(struct tac_handle *, const char *); 5941227Sjdp 6041227Sjdpstatic int do_item(pam_handle_t *, struct tac_handle *, int, 6141227Sjdp set_func, const char *); 6241227Sjdpstatic char *get_msg(struct tac_handle *); 6341227Sjdpstatic int set_msg(struct tac_handle *, const char *); 6441227Sjdp 6541227Sjdpstatic int 6641227Sjdpdo_item(pam_handle_t *pamh, struct tac_handle *tach, int item, 6741227Sjdp set_func func, const char *funcname) 6841227Sjdp{ 6941227Sjdp int retval; 7041227Sjdp const void *value; 7141227Sjdp 7279476Smarkm retval = pam_get_item(pamh, item, &value); 7379476Smarkm if (retval != PAM_SUCCESS) 7441227Sjdp return retval; 7541227Sjdp if (value != NULL && (*func)(tach, (const char *)value) == -1) { 7641227Sjdp syslog(LOG_CRIT, "%s: %s", funcname, tac_strerror(tach)); 7741227Sjdp tac_close(tach); 7841227Sjdp return PAM_SERVICE_ERR; 7941227Sjdp } 8041227Sjdp return PAM_SUCCESS; 8141227Sjdp} 8241227Sjdp 8341227Sjdpstatic char * 8441227Sjdpget_msg(struct tac_handle *tach) 8541227Sjdp{ 8641227Sjdp char *msg; 8741227Sjdp 8879476Smarkm msg = tac_get_msg(tach); 8979476Smarkm if (msg == NULL) { 9041227Sjdp syslog(LOG_CRIT, "tac_get_msg: %s", tac_strerror(tach)); 9141227Sjdp tac_close(tach); 9241227Sjdp return NULL; 9341227Sjdp } 9441227Sjdp return msg; 9541227Sjdp} 9641227Sjdp 9741227Sjdpstatic int 9841227Sjdpset_msg(struct tac_handle *tach, const char *msg) 9941227Sjdp{ 10041227Sjdp if (tac_set_msg(tach, msg) == -1) { 10141227Sjdp syslog(LOG_CRIT, "tac_set_msg: %s", tac_strerror(tach)); 10241227Sjdp tac_close(tach); 10341227Sjdp return -1; 10441227Sjdp } 10541227Sjdp return 0; 10641227Sjdp} 10741227Sjdp 10841227SjdpPAM_EXTERN int 10994564Sdespam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 110115465Sdes int argc __unused, const char *argv[] __unused) 11141227Sjdp{ 11241227Sjdp int retval; 11341227Sjdp struct tac_handle *tach; 114115465Sdes const char *conf_file, *template_user; 11541227Sjdp 116115465Sdes conf_file = openpam_get_option(pamh, PAM_OPT_CONF); 117115465Sdes template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); 11841227Sjdp 11979476Smarkm tach = tac_open(); 12079476Smarkm if (tach == NULL) { 12141227Sjdp syslog(LOG_CRIT, "tac_open failed"); 12294564Sdes return (PAM_SERVICE_ERR); 12341227Sjdp } 12441227Sjdp if (tac_config(tach, conf_file) == -1) { 12541227Sjdp syslog(LOG_ALERT, "tac_config: %s", tac_strerror(tach)); 12641227Sjdp tac_close(tach); 12794564Sdes return (PAM_SERVICE_ERR); 12841227Sjdp } 12941227Sjdp if (tac_create_authen(tach, TAC_AUTHEN_LOGIN, TAC_AUTHEN_TYPE_ASCII, 13041227Sjdp TAC_AUTHEN_SVC_LOGIN) == -1) { 13141227Sjdp syslog(LOG_CRIT, "tac_create_authen: %s", tac_strerror(tach)); 13241227Sjdp tac_close(tach); 13394564Sdes return (PAM_SERVICE_ERR); 13441227Sjdp } 13579476Smarkm 13679476Smarkm PAM_LOG("Done tac_open() ... tac_close()"); 13779476Smarkm 13879476Smarkm retval = do_item(pamh, tach, PAM_USER, tac_set_user, "tac_set_user"); 13979476Smarkm if (retval != PAM_SUCCESS) 14094564Sdes return (retval); 14179476Smarkm 14279476Smarkm PAM_LOG("Done user"); 14379476Smarkm 14479476Smarkm retval = do_item(pamh, tach, PAM_TTY, tac_set_port, "tac_set_port"); 14579476Smarkm if (retval != PAM_SUCCESS) 14694564Sdes return (retval); 14779476Smarkm 14879476Smarkm PAM_LOG("Done tty"); 14979476Smarkm 15079476Smarkm retval = do_item(pamh, tach, PAM_RHOST, tac_set_rem_addr, 15179476Smarkm "tac_set_rem_addr"); 15279476Smarkm if (retval != PAM_SUCCESS) 15394564Sdes return (retval); 15479476Smarkm 15594564Sdes for (;;) { 15641227Sjdp char *srvr_msg; 15741227Sjdp size_t msg_len; 15841227Sjdp const char *user_msg; 15941227Sjdp char *data_msg; 16041227Sjdp int sflags; 16141227Sjdp int status; 16241227Sjdp 16379476Smarkm sflags = tac_send_authen(tach); 16479476Smarkm if (sflags == -1) { 16541227Sjdp syslog(LOG_CRIT, "tac_send_authen: %s", 16641227Sjdp tac_strerror(tach)); 16741227Sjdp tac_close(tach); 16894564Sdes return (PAM_AUTHINFO_UNAVAIL); 16941227Sjdp } 17041227Sjdp status = TAC_AUTHEN_STATUS(sflags); 171115465Sdes openpam_set_option(pamh, PAM_OPT_ECHO_PASS, 172115465Sdes TAC_AUTHEN_NOECHO(sflags) ? NULL : ""); 17341227Sjdp switch (status) { 17441227Sjdp 17541227Sjdp case TAC_AUTHEN_STATUS_PASS: 17641227Sjdp tac_close(tach); 17741227Sjdp if (template_user != NULL) { 17841227Sjdp const void *item; 17941227Sjdp const char *user; 18041227Sjdp 18179476Smarkm PAM_LOG("Trying template user: %s", 18279476Smarkm template_user); 18379476Smarkm 18441227Sjdp /* 18541227Sjdp * If the given user name doesn't exist in 18641227Sjdp * the local password database, change it 18741227Sjdp * to the value given in the "template_user" 18841227Sjdp * option. 18941227Sjdp */ 19041227Sjdp retval = pam_get_item(pamh, PAM_USER, &item); 19141227Sjdp if (retval != PAM_SUCCESS) 19294564Sdes return (retval); 19341227Sjdp user = (const char *)item; 19479476Smarkm if (getpwnam(user) == NULL) { 19541227Sjdp pam_set_item(pamh, PAM_USER, 19641227Sjdp template_user); 19779476Smarkm PAM_LOG("Using template user"); 19879476Smarkm } 19941227Sjdp } 20094564Sdes return (PAM_SUCCESS); 20141227Sjdp 20241227Sjdp case TAC_AUTHEN_STATUS_FAIL: 20341227Sjdp tac_close(tach); 20481473Smarkm PAM_VERBOSE_ERROR("TACACS+ authentication failed"); 20594564Sdes return (PAM_AUTH_ERR); 20641227Sjdp 20741227Sjdp case TAC_AUTHEN_STATUS_GETUSER: 20841227Sjdp case TAC_AUTHEN_STATUS_GETPASS: 20941227Sjdp if ((srvr_msg = get_msg(tach)) == NULL) 21094564Sdes return (PAM_SERVICE_ERR); 21141227Sjdp if (status == TAC_AUTHEN_STATUS_GETUSER) 21241227Sjdp retval = pam_get_user(pamh, &user_msg, 21393984Sdes *srvr_msg ? srvr_msg : NULL); 21441227Sjdp else if (status == TAC_AUTHEN_STATUS_GETPASS) 21593984Sdes retval = pam_get_authtok(pamh, 21693984Sdes PAM_AUTHTOK, &user_msg, 21793984Sdes *srvr_msg ? srvr_msg : "Password:"); 21841227Sjdp free(srvr_msg); 21941227Sjdp if (retval != PAM_SUCCESS) { 22041227Sjdp /* XXX - send a TACACS+ abort packet */ 22141227Sjdp tac_close(tach); 22294564Sdes return (retval); 22341227Sjdp } 22441227Sjdp if (set_msg(tach, user_msg) == -1) 22594564Sdes return (PAM_SERVICE_ERR); 22641227Sjdp break; 22741227Sjdp 22841227Sjdp case TAC_AUTHEN_STATUS_GETDATA: 22941227Sjdp if ((srvr_msg = get_msg(tach)) == NULL) 23094564Sdes return (PAM_SERVICE_ERR); 23141227Sjdp retval = pam_prompt(pamh, 232115465Sdes openpam_get_option(pamh, PAM_OPT_ECHO_PASS) ? 233115465Sdes PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF, 23493984Sdes &data_msg, "%s", *srvr_msg ? srvr_msg : "Data:"); 23541227Sjdp free(srvr_msg); 23641227Sjdp if (retval != PAM_SUCCESS) { 23741227Sjdp /* XXX - send a TACACS+ abort packet */ 23841227Sjdp tac_close(tach); 23994564Sdes return (retval); 24041227Sjdp } 24141227Sjdp retval = set_msg(tach, data_msg); 24241227Sjdp memset(data_msg, 0, strlen(data_msg)); 24341227Sjdp free(data_msg); 24441227Sjdp if (retval == -1) 24594564Sdes return (PAM_SERVICE_ERR); 24641227Sjdp break; 24741227Sjdp 24841227Sjdp case TAC_AUTHEN_STATUS_ERROR: 24941227Sjdp srvr_msg = (char *)tac_get_data(tach, &msg_len); 25041227Sjdp if (srvr_msg != NULL && msg_len != 0) { 25141227Sjdp syslog(LOG_CRIT, "tac_send_authen:" 25241227Sjdp " server detected error: %s", srvr_msg); 25341227Sjdp free(srvr_msg); 25479476Smarkm } 25579476Smarkm else 25641227Sjdp syslog(LOG_CRIT, 25741227Sjdp "tac_send_authen: server detected error"); 25841227Sjdp tac_close(tach); 25994564Sdes return (PAM_AUTHINFO_UNAVAIL); 26041227Sjdp break; 26141227Sjdp 26241227Sjdp case TAC_AUTHEN_STATUS_RESTART: 26341227Sjdp case TAC_AUTHEN_STATUS_FOLLOW: 26441227Sjdp default: 26541227Sjdp syslog(LOG_CRIT, 26641227Sjdp "tac_send_authen: unexpected status %#x", status); 26741227Sjdp tac_close(tach); 26894564Sdes return (PAM_AUTHINFO_UNAVAIL); 26941227Sjdp } 27041227Sjdp } 27141227Sjdp} 27241227Sjdp 27341227SjdpPAM_EXTERN int 27494564Sdespam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 27594564Sdes int argc __unused, const char *argv[] __unused) 27641227Sjdp{ 27787398Sdes 27894564Sdes return (PAM_IGNORE); 27941227Sjdp} 28042917Sjdp 28142917SjdpPAM_MODULE_ENTRY("pam_tacplus"); 282