pam_tacplus.c revision 87398
141227Sjdp/*-
241227Sjdp * Copyright 1998 Juniper Networks, Inc.
341227Sjdp * All rights reserved.
487398Sdes * Copyright (c) 2001 Networks Associates Technologies, 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: head/lib/libpam/modules/pam_tacplus/pam_tacplus.c 87398 2001-12-05 16:06:35Z des $");
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#define PAM_SM_ACCOUNT
5187398Sdes#define PAM_SM_SESSION
5287398Sdes#define PAM_SM_PASSWORD
5387398Sdes
5441227Sjdp#include <security/pam_modules.h>
5541227Sjdp
5641227Sjdp#include "pam_mod_misc.h"
5741227Sjdp
5879476Smarkmenum { PAM_OPT_CONF=PAM_OPT_STD_MAX, PAM_OPT_TEMPLATE_USER };
5941227Sjdp
6079476Smarkmstatic struct opttab other_options[] = {
6179476Smarkm	{ "conf",		PAM_OPT_CONF },
6279476Smarkm	{ "template_user",	PAM_OPT_TEMPLATE_USER },
6379476Smarkm	{ NULL, 0 }
6479476Smarkm};
6579476Smarkm
6641227Sjdptypedef int (*set_func)(struct tac_handle *, const char *);
6741227Sjdp
6841227Sjdpstatic int	 do_item(pam_handle_t *, struct tac_handle *, int,
6941227Sjdp		    set_func, const char *);
7041227Sjdpstatic char	*get_msg(struct tac_handle *);
7141227Sjdpstatic int	 set_msg(struct tac_handle *, const char *);
7241227Sjdp
7341227Sjdpstatic int
7441227Sjdpdo_item(pam_handle_t *pamh, struct tac_handle *tach, int item,
7541227Sjdp    set_func func, const char *funcname)
7641227Sjdp{
7741227Sjdp	int retval;
7841227Sjdp	const void *value;
7941227Sjdp
8079476Smarkm	retval = pam_get_item(pamh, item, &value);
8179476Smarkm	if (retval != PAM_SUCCESS)
8241227Sjdp	    return retval;
8341227Sjdp	if (value != NULL && (*func)(tach, (const char *)value) == -1) {
8441227Sjdp		syslog(LOG_CRIT, "%s: %s", funcname, tac_strerror(tach));
8541227Sjdp		tac_close(tach);
8641227Sjdp		return PAM_SERVICE_ERR;
8741227Sjdp	}
8841227Sjdp	return PAM_SUCCESS;
8941227Sjdp}
9041227Sjdp
9141227Sjdpstatic char *
9241227Sjdpget_msg(struct tac_handle *tach)
9341227Sjdp{
9441227Sjdp	char *msg;
9541227Sjdp
9679476Smarkm	msg = tac_get_msg(tach);
9779476Smarkm	if (msg == NULL) {
9841227Sjdp		syslog(LOG_CRIT, "tac_get_msg: %s", tac_strerror(tach));
9941227Sjdp		tac_close(tach);
10041227Sjdp		return NULL;
10141227Sjdp	}
10241227Sjdp	return msg;
10341227Sjdp}
10441227Sjdp
10541227Sjdpstatic int
10641227Sjdpset_msg(struct tac_handle *tach, const char *msg)
10741227Sjdp{
10841227Sjdp	if (tac_set_msg(tach, msg) == -1) {
10941227Sjdp		syslog(LOG_CRIT, "tac_set_msg: %s", tac_strerror(tach));
11041227Sjdp		tac_close(tach);
11141227Sjdp		return -1;
11241227Sjdp	}
11341227Sjdp	return 0;
11441227Sjdp}
11541227Sjdp
11641227SjdpPAM_EXTERN int
11741227Sjdppam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
11841227Sjdp    const char **argv)
11941227Sjdp{
12079476Smarkm	struct options options;
12141227Sjdp	int retval;
12241227Sjdp	struct tac_handle *tach;
12379476Smarkm	char *conf_file;
12479476Smarkm	char *template_user;
12541227Sjdp
12679476Smarkm	pam_std_option(&options, other_options, argc, argv);
12741227Sjdp
12879476Smarkm	PAM_LOG("Options processed");
12941227Sjdp
13079476Smarkm	conf_file = NULL;
13179476Smarkm	pam_test_option(&options, PAM_OPT_CONF, &conf_file);
13279476Smarkm	template_user = NULL;
13379476Smarkm	pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user);
13479476Smarkm
13579476Smarkm	tach = tac_open();
13679476Smarkm	if (tach == NULL) {
13741227Sjdp		syslog(LOG_CRIT, "tac_open failed");
13879476Smarkm		PAM_RETURN(PAM_SERVICE_ERR);
13941227Sjdp	}
14041227Sjdp	if (tac_config(tach, conf_file) == -1) {
14141227Sjdp		syslog(LOG_ALERT, "tac_config: %s", tac_strerror(tach));
14241227Sjdp		tac_close(tach);
14379476Smarkm		PAM_RETURN(PAM_SERVICE_ERR);
14441227Sjdp	}
14541227Sjdp	if (tac_create_authen(tach, TAC_AUTHEN_LOGIN, TAC_AUTHEN_TYPE_ASCII,
14641227Sjdp	    TAC_AUTHEN_SVC_LOGIN) == -1) {
14741227Sjdp		syslog(LOG_CRIT, "tac_create_authen: %s", tac_strerror(tach));
14841227Sjdp		tac_close(tach);
14979476Smarkm		PAM_RETURN(PAM_SERVICE_ERR);
15041227Sjdp	}
15179476Smarkm
15279476Smarkm	PAM_LOG("Done tac_open() ... tac_close()");
15379476Smarkm
15479476Smarkm	retval = do_item(pamh, tach, PAM_USER, tac_set_user, "tac_set_user");
15579476Smarkm	if (retval != PAM_SUCCESS)
15679476Smarkm		PAM_RETURN(retval);
15779476Smarkm
15879476Smarkm	PAM_LOG("Done user");
15979476Smarkm
16079476Smarkm	retval = do_item(pamh, tach, PAM_TTY, tac_set_port, "tac_set_port");
16179476Smarkm	if (retval != PAM_SUCCESS)
16279476Smarkm		PAM_RETURN(retval);
16379476Smarkm
16479476Smarkm	PAM_LOG("Done tty");
16579476Smarkm
16679476Smarkm	retval = do_item(pamh, tach, PAM_RHOST, tac_set_rem_addr,
16779476Smarkm	    "tac_set_rem_addr");
16879476Smarkm	if (retval != PAM_SUCCESS)
16979476Smarkm		PAM_RETURN(retval);
17079476Smarkm
17141227Sjdp	for ( ; ; ) {
17241227Sjdp		char *srvr_msg;
17341227Sjdp		size_t msg_len;
17441227Sjdp		const char *user_msg;
17541227Sjdp		char *data_msg;
17641227Sjdp		int sflags;
17741227Sjdp		int status;
17841227Sjdp
17979476Smarkm		sflags = tac_send_authen(tach);
18079476Smarkm		if (sflags == -1) {
18141227Sjdp			syslog(LOG_CRIT, "tac_send_authen: %s",
18241227Sjdp			    tac_strerror(tach));
18341227Sjdp			tac_close(tach);
18479476Smarkm			PAM_RETURN(PAM_AUTHINFO_UNAVAIL);
18541227Sjdp		}
18641227Sjdp		status = TAC_AUTHEN_STATUS(sflags);
18779476Smarkm		if (!TAC_AUTHEN_NOECHO(sflags))
18879476Smarkm			pam_set_option(&options, PAM_OPT_ECHO_PASS);
18941227Sjdp		switch (status) {
19041227Sjdp
19141227Sjdp		case TAC_AUTHEN_STATUS_PASS:
19241227Sjdp			tac_close(tach);
19341227Sjdp			if (template_user != NULL) {
19441227Sjdp				const void *item;
19541227Sjdp				const char *user;
19641227Sjdp
19779476Smarkm				PAM_LOG("Trying template user: %s",
19879476Smarkm				    template_user);
19979476Smarkm
20041227Sjdp				/*
20141227Sjdp				 * If the given user name doesn't exist in
20241227Sjdp				 * the local password database, change it
20341227Sjdp				 * to the value given in the "template_user"
20441227Sjdp				 * option.
20541227Sjdp				 */
20641227Sjdp				retval = pam_get_item(pamh, PAM_USER, &item);
20741227Sjdp				if (retval != PAM_SUCCESS)
20879476Smarkm					PAM_RETURN(retval);
20941227Sjdp				user = (const char *)item;
21079476Smarkm				if (getpwnam(user) == NULL) {
21141227Sjdp					pam_set_item(pamh, PAM_USER,
21241227Sjdp					    template_user);
21379476Smarkm					PAM_LOG("Using template user");
21479476Smarkm				}
21541227Sjdp			}
21679476Smarkm			PAM_RETURN(PAM_SUCCESS);
21741227Sjdp
21841227Sjdp		case TAC_AUTHEN_STATUS_FAIL:
21941227Sjdp			tac_close(tach);
22081473Smarkm			PAM_VERBOSE_ERROR("TACACS+ authentication failed");
22179476Smarkm			PAM_RETURN(PAM_AUTH_ERR);
22241227Sjdp
22341227Sjdp		case TAC_AUTHEN_STATUS_GETUSER:
22441227Sjdp		case TAC_AUTHEN_STATUS_GETPASS:
22541227Sjdp			if ((srvr_msg = get_msg(tach)) == NULL)
22679476Smarkm				PAM_RETURN(PAM_SERVICE_ERR);
22741227Sjdp			if (status == TAC_AUTHEN_STATUS_GETUSER)
22841227Sjdp				retval = pam_get_user(pamh, &user_msg,
22941227Sjdp				    srvr_msg[0] != '\0' ? srvr_msg : NULL);
23041227Sjdp			else if (status == TAC_AUTHEN_STATUS_GETPASS)
23141227Sjdp				retval = pam_get_pass(pamh, &user_msg,
23241227Sjdp				    srvr_msg[0] != '\0' ? srvr_msg :
23379476Smarkm				    "Password:", &options);
23441227Sjdp			free(srvr_msg);
23541227Sjdp			if (retval != PAM_SUCCESS) {
23641227Sjdp				/* XXX - send a TACACS+ abort packet */
23741227Sjdp				tac_close(tach);
23879476Smarkm				PAM_RETURN(retval);
23941227Sjdp			}
24041227Sjdp			if (set_msg(tach, user_msg) == -1)
24179476Smarkm				PAM_RETURN(PAM_SERVICE_ERR);
24241227Sjdp			break;
24341227Sjdp
24441227Sjdp		case TAC_AUTHEN_STATUS_GETDATA:
24541227Sjdp			if ((srvr_msg = get_msg(tach)) == NULL)
24679476Smarkm				PAM_RETURN(PAM_SERVICE_ERR);
24741227Sjdp			retval = pam_prompt(pamh,
24879476Smarkm			    pam_test_option(&options, PAM_OPT_ECHO_PASS, NULL)
24979476Smarkm				? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF,
25041227Sjdp			    srvr_msg[0] != '\0' ? srvr_msg : "Data:",
25141227Sjdp			    &data_msg);
25241227Sjdp			free(srvr_msg);
25341227Sjdp			if (retval != PAM_SUCCESS) {
25441227Sjdp				/* XXX - send a TACACS+ abort packet */
25541227Sjdp				tac_close(tach);
25679476Smarkm				PAM_RETURN(retval);
25741227Sjdp			}
25841227Sjdp			retval = set_msg(tach, data_msg);
25941227Sjdp			memset(data_msg, 0, strlen(data_msg));
26041227Sjdp			free(data_msg);
26141227Sjdp			if (retval == -1)
26279476Smarkm				PAM_RETURN(PAM_SERVICE_ERR);
26341227Sjdp			break;
26441227Sjdp
26541227Sjdp		case TAC_AUTHEN_STATUS_ERROR:
26641227Sjdp			srvr_msg = (char *)tac_get_data(tach, &msg_len);
26741227Sjdp			if (srvr_msg != NULL && msg_len != 0) {
26841227Sjdp				syslog(LOG_CRIT, "tac_send_authen:"
26941227Sjdp				    " server detected error: %s", srvr_msg);
27041227Sjdp				free(srvr_msg);
27179476Smarkm			}
27279476Smarkm			else
27341227Sjdp				syslog(LOG_CRIT,
27441227Sjdp				    "tac_send_authen: server detected error");
27541227Sjdp			tac_close(tach);
27679476Smarkm			PAM_RETURN(PAM_AUTHINFO_UNAVAIL);
27741227Sjdp			break;
27841227Sjdp
27941227Sjdp		case TAC_AUTHEN_STATUS_RESTART:
28041227Sjdp		case TAC_AUTHEN_STATUS_FOLLOW:
28141227Sjdp		default:
28241227Sjdp			syslog(LOG_CRIT,
28341227Sjdp			    "tac_send_authen: unexpected status %#x", status);
28441227Sjdp			tac_close(tach);
28579476Smarkm			PAM_RETURN(PAM_AUTHINFO_UNAVAIL);
28641227Sjdp		}
28741227Sjdp	}
28841227Sjdp}
28941227Sjdp
29041227SjdpPAM_EXTERN int
29141227Sjdppam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
29241227Sjdp{
29387398Sdes	struct options options;
29487398Sdes
29587398Sdes	pam_std_option(&options, NULL, argc, argv);
29687398Sdes
29787398Sdes	PAM_LOG("Options processed");
29887398Sdes
29987398Sdes	PAM_RETURN(PAM_IGNORE);
30041227Sjdp}
30142917Sjdp
30287398SdesPAM_EXTERN int
30387398Sdespam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc ,const char **argv)
30487398Sdes{
30587398Sdes	struct options options;
30687398Sdes
30787398Sdes	pam_std_option(&options, NULL, argc, argv);
30887398Sdes
30987398Sdes	PAM_LOG("Options processed");
31087398Sdes
31187398Sdes	PAM_RETURN(PAM_IGNORE);
31287398Sdes}
31387398Sdes
31487398SdesPAM_EXTERN int
31587398Sdespam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
31687398Sdes{
31787398Sdes	struct options options;
31887398Sdes
31987398Sdes	pam_std_option(&options, NULL, argc, argv);
32087398Sdes
32187398Sdes	PAM_LOG("Options processed");
32287398Sdes
32387398Sdes	PAM_RETURN(PAM_IGNORE);
32487398Sdes}
32587398Sdes
32687398SdesPAM_EXTERN int
32787398Sdespam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
32887398Sdes{
32987398Sdes	struct options options;
33087398Sdes
33187398Sdes	pam_std_option(&options, NULL, argc, argv);
33287398Sdes
33387398Sdes	PAM_LOG("Options processed");
33487398Sdes
33587398Sdes	PAM_RETURN(PAM_IGNORE);
33687398Sdes}
33787398Sdes
33887398SdesPAM_EXTERN int
33987398Sdespam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
34087398Sdes{
34187398Sdes	struct options options;
34287398Sdes
34387398Sdes	pam_std_option(&options, NULL, argc, argv);
34487398Sdes
34587398Sdes	PAM_LOG("Options processed");
34687398Sdes
34787398Sdes	PAM_RETURN(PAM_IGNORE);
34887398Sdes}
34987398Sdes
35042917SjdpPAM_MODULE_ENTRY("pam_tacplus");
351