pam_tacplus.c revision 84218
1/*-
2 * Copyright 1998 Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_tacplus/pam_tacplus.c 84218 2001-09-30 22:11:06Z dillon $");
29
30#include <sys/param.h>
31
32#include <pwd.h>
33#include <stdlib.h>
34#include <string.h>
35#include <syslog.h>
36#include <taclib.h>
37#include <unistd.h>
38
39#define PAM_SM_AUTH
40#include <security/pam_modules.h>
41
42#include "pam_mod_misc.h"
43
44enum { PAM_OPT_CONF=PAM_OPT_STD_MAX, PAM_OPT_TEMPLATE_USER };
45
46static struct opttab other_options[] = {
47	{ "conf",		PAM_OPT_CONF },
48	{ "template_user",	PAM_OPT_TEMPLATE_USER },
49	{ NULL, 0 }
50};
51
52typedef int (*set_func)(struct tac_handle *, const char *);
53
54static int	 do_item(pam_handle_t *, struct tac_handle *, int,
55		    set_func, const char *);
56static char	*get_msg(struct tac_handle *);
57static int	 set_msg(struct tac_handle *, const char *);
58
59static int
60do_item(pam_handle_t *pamh, struct tac_handle *tach, int item,
61    set_func func, const char *funcname)
62{
63	int retval;
64	const void *value;
65
66	retval = pam_get_item(pamh, item, &value);
67	if (retval != PAM_SUCCESS)
68	    return retval;
69	if (value != NULL && (*func)(tach, (const char *)value) == -1) {
70		syslog(LOG_CRIT, "%s: %s", funcname, tac_strerror(tach));
71		tac_close(tach);
72		return PAM_SERVICE_ERR;
73	}
74	return PAM_SUCCESS;
75}
76
77static char *
78get_msg(struct tac_handle *tach)
79{
80	char *msg;
81
82	msg = tac_get_msg(tach);
83	if (msg == NULL) {
84		syslog(LOG_CRIT, "tac_get_msg: %s", tac_strerror(tach));
85		tac_close(tach);
86		return NULL;
87	}
88	return msg;
89}
90
91static int
92set_msg(struct tac_handle *tach, const char *msg)
93{
94	if (tac_set_msg(tach, msg) == -1) {
95		syslog(LOG_CRIT, "tac_set_msg: %s", tac_strerror(tach));
96		tac_close(tach);
97		return -1;
98	}
99	return 0;
100}
101
102PAM_EXTERN int
103pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
104    const char **argv)
105{
106	struct options options;
107	int retval;
108	struct tac_handle *tach;
109	char *conf_file;
110	char *template_user;
111
112	pam_std_option(&options, other_options, argc, argv);
113
114	PAM_LOG("Options processed");
115
116	conf_file = NULL;
117	pam_test_option(&options, PAM_OPT_CONF, &conf_file);
118	template_user = NULL;
119	pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user);
120
121	tach = tac_open();
122	if (tach == NULL) {
123		syslog(LOG_CRIT, "tac_open failed");
124		PAM_RETURN(PAM_SERVICE_ERR);
125	}
126	if (tac_config(tach, conf_file) == -1) {
127		syslog(LOG_ALERT, "tac_config: %s", tac_strerror(tach));
128		tac_close(tach);
129		PAM_RETURN(PAM_SERVICE_ERR);
130	}
131	if (tac_create_authen(tach, TAC_AUTHEN_LOGIN, TAC_AUTHEN_TYPE_ASCII,
132	    TAC_AUTHEN_SVC_LOGIN) == -1) {
133		syslog(LOG_CRIT, "tac_create_authen: %s", tac_strerror(tach));
134		tac_close(tach);
135		PAM_RETURN(PAM_SERVICE_ERR);
136	}
137
138	PAM_LOG("Done tac_open() ... tac_close()");
139
140	retval = do_item(pamh, tach, PAM_USER, tac_set_user, "tac_set_user");
141	if (retval != PAM_SUCCESS)
142		PAM_RETURN(retval);
143
144	PAM_LOG("Done user");
145
146	retval = do_item(pamh, tach, PAM_TTY, tac_set_port, "tac_set_port");
147	if (retval != PAM_SUCCESS)
148		PAM_RETURN(retval);
149
150	PAM_LOG("Done tty");
151
152	retval = do_item(pamh, tach, PAM_RHOST, tac_set_rem_addr,
153	    "tac_set_rem_addr");
154	if (retval != PAM_SUCCESS)
155		PAM_RETURN(retval);
156
157	for ( ; ; ) {
158		char *srvr_msg;
159		size_t msg_len;
160		const char *user_msg;
161		char *data_msg;
162		int sflags;
163		int status;
164
165		sflags = tac_send_authen(tach);
166		if (sflags == -1) {
167			syslog(LOG_CRIT, "tac_send_authen: %s",
168			    tac_strerror(tach));
169			tac_close(tach);
170			PAM_RETURN(PAM_AUTHINFO_UNAVAIL);
171		}
172		status = TAC_AUTHEN_STATUS(sflags);
173		if (!TAC_AUTHEN_NOECHO(sflags))
174			pam_set_option(&options, PAM_OPT_ECHO_PASS);
175		switch (status) {
176
177		case TAC_AUTHEN_STATUS_PASS:
178			tac_close(tach);
179			if (template_user != NULL) {
180				const void *item;
181				const char *user;
182
183				PAM_LOG("Trying template user: %s",
184				    template_user);
185
186				/*
187				 * If the given user name doesn't exist in
188				 * the local password database, change it
189				 * to the value given in the "template_user"
190				 * option.
191				 */
192				retval = pam_get_item(pamh, PAM_USER, &item);
193				if (retval != PAM_SUCCESS)
194					PAM_RETURN(retval);
195				user = (const char *)item;
196				if (getpwnam(user) == NULL) {
197					pam_set_item(pamh, PAM_USER,
198					    template_user);
199					PAM_LOG("Using template user");
200				}
201			}
202			PAM_RETURN(PAM_SUCCESS);
203
204		case TAC_AUTHEN_STATUS_FAIL:
205			tac_close(tach);
206			PAM_VERBOSE_ERROR("TACACS+ authentication failed");
207			PAM_RETURN(PAM_AUTH_ERR);
208
209		case TAC_AUTHEN_STATUS_GETUSER:
210		case TAC_AUTHEN_STATUS_GETPASS:
211			if ((srvr_msg = get_msg(tach)) == NULL)
212				PAM_RETURN(PAM_SERVICE_ERR);
213			if (status == TAC_AUTHEN_STATUS_GETUSER)
214				retval = pam_get_user(pamh, &user_msg,
215				    srvr_msg[0] != '\0' ? srvr_msg : NULL);
216			else if (status == TAC_AUTHEN_STATUS_GETPASS)
217				retval = pam_get_pass(pamh, &user_msg,
218				    srvr_msg[0] != '\0' ? srvr_msg :
219				    "Password:", &options);
220			free(srvr_msg);
221			if (retval != PAM_SUCCESS) {
222				/* XXX - send a TACACS+ abort packet */
223				tac_close(tach);
224				PAM_RETURN(retval);
225			}
226			if (set_msg(tach, user_msg) == -1)
227				PAM_RETURN(PAM_SERVICE_ERR);
228			break;
229
230		case TAC_AUTHEN_STATUS_GETDATA:
231			if ((srvr_msg = get_msg(tach)) == NULL)
232				PAM_RETURN(PAM_SERVICE_ERR);
233			retval = pam_prompt(pamh,
234			    pam_test_option(&options, PAM_OPT_ECHO_PASS, NULL)
235				? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF,
236			    srvr_msg[0] != '\0' ? srvr_msg : "Data:",
237			    &data_msg);
238			free(srvr_msg);
239			if (retval != PAM_SUCCESS) {
240				/* XXX - send a TACACS+ abort packet */
241				tac_close(tach);
242				PAM_RETURN(retval);
243			}
244			retval = set_msg(tach, data_msg);
245			memset(data_msg, 0, strlen(data_msg));
246			free(data_msg);
247			if (retval == -1)
248				PAM_RETURN(PAM_SERVICE_ERR);
249			break;
250
251		case TAC_AUTHEN_STATUS_ERROR:
252			srvr_msg = (char *)tac_get_data(tach, &msg_len);
253			if (srvr_msg != NULL && msg_len != 0) {
254				syslog(LOG_CRIT, "tac_send_authen:"
255				    " server detected error: %s", srvr_msg);
256				free(srvr_msg);
257			}
258			else
259				syslog(LOG_CRIT,
260				    "tac_send_authen: server detected error");
261			tac_close(tach);
262			PAM_RETURN(PAM_AUTHINFO_UNAVAIL);
263			break;
264
265		case TAC_AUTHEN_STATUS_RESTART:
266		case TAC_AUTHEN_STATUS_FOLLOW:
267		default:
268			syslog(LOG_CRIT,
269			    "tac_send_authen: unexpected status %#x", status);
270			tac_close(tach);
271			PAM_RETURN(PAM_AUTHINFO_UNAVAIL);
272		}
273	}
274}
275
276PAM_EXTERN int
277pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
278{
279	return PAM_SUCCESS;
280}
281
282PAM_MODULE_ENTRY("pam_tacplus");
283