pam_radius.c revision 41228
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 *	$FreeBSD: head/lib/libpam/modules/pam_radius/pam_radius.c 41228 1998-11-18 01:44:37Z jdp $
27 */
28
29#include <sys/param.h>
30#include <pwd.h>
31#include <radlib.h>
32#include <stdlib.h>
33#include <string.h>
34#include <syslog.h>
35#include <unistd.h>
36
37#define PAM_SM_AUTH
38#include <security/pam_modules.h>
39
40#include "pam_mod_misc.h"
41
42#define MAX_CHALLENGE_MSGS	10
43#define PASSWORD_PROMPT	"RADIUS password:"
44
45/* Option names, including the "=" sign. */
46#define OPT_CONF		"conf="
47#define OPT_TMPL		"template_user="
48
49static int	 build_access_request(struct rad_handle *, const char *,
50		    const char *, const void *, size_t);
51static int	 do_accept(pam_handle_t *, struct rad_handle *);
52static int	 do_challenge(pam_handle_t *, struct rad_handle *,
53		    const char *);
54
55/*
56 * Construct an access request, but don't send it.  Returns 0 on success,
57 * -1 on failure.
58 */
59static int
60build_access_request(struct rad_handle *radh, const char *user,
61    const char *pass, const void *state, size_t state_len)
62{
63	char	 host[MAXHOSTNAMELEN];
64
65	if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) {
66		syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh));
67		return -1;
68	}
69	if ((user != NULL &&
70	    rad_put_string(radh, RAD_USER_NAME, user) == -1) ||
71	    (pass != NULL &&
72	    rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) ||
73	    (gethostname(host, sizeof host) != -1 &&
74	    rad_put_string(radh, RAD_NAS_IDENTIFIER, host) == -1)) {
75		syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh));
76		return -1;
77	}
78	if (state != NULL && rad_put_attr(radh, RAD_STATE, state,
79	    state_len) == -1) {
80		syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh));
81		return -1;
82	}
83	if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) {
84		syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh));
85		return -1;
86	}
87	return 0;
88}
89
90static int
91do_accept(pam_handle_t *pamh, struct rad_handle *radh)
92{
93	int attrtype;
94	const void *attrval;
95	size_t attrlen;
96	char *s;
97
98	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
99		if (attrtype == RAD_USER_NAME) {
100			s = rad_cvt_string(attrval, attrlen);
101			if (s == NULL) {
102				syslog(LOG_CRIT,
103				    "rad_cvt_string: out of memory");
104				return -1;
105			}
106			pam_set_item(pamh, PAM_USER, s);
107			free(s);
108		}
109	}
110	if (attrtype == -1) {
111		syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh));
112		return -1;
113	}
114	return 0;
115}
116
117static int
118do_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user)
119{
120	int retval;
121	int attrtype;
122	const void *attrval;
123	size_t attrlen;
124	const void *state;
125	size_t statelen;
126	struct pam_message msgs[MAX_CHALLENGE_MSGS];
127	const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS];
128	struct pam_response *resp;
129	int num_msgs;
130	const void *item;
131	const struct pam_conv *conv;
132
133	state = NULL;
134	statelen = 0;
135	num_msgs = 0;
136	while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) {
137		switch (attrtype) {
138
139		case RAD_STATE:
140			state = attrval;
141			statelen = attrlen;
142			break;
143
144		case RAD_REPLY_MESSAGE:
145			if (num_msgs >= MAX_CHALLENGE_MSGS) {
146				syslog(LOG_CRIT,
147				    "Too many RADIUS challenge messages");
148				return PAM_SERVICE_ERR;
149			}
150			msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen);
151			if (msgs[num_msgs].msg == NULL) {
152				syslog(LOG_CRIT,
153				    "rad_cvt_string: out of memory");
154				return PAM_SERVICE_ERR;
155			}
156			msgs[num_msgs].msg_style = PAM_TEXT_INFO;
157			msg_ptrs[num_msgs] = &msgs[num_msgs];
158			num_msgs++;
159			break;
160		}
161	}
162	if (attrtype == -1) {
163		syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh));
164		return PAM_SERVICE_ERR;
165	}
166	if (num_msgs == 0) {
167		msgs[num_msgs].msg = strdup("(null RADIUS challenge): ");
168		if (msgs[num_msgs].msg == NULL) {
169			syslog(LOG_CRIT, "Out of memory");
170			return PAM_SERVICE_ERR;
171		}
172		msgs[num_msgs].msg_style = PAM_TEXT_INFO;
173		msg_ptrs[num_msgs] = &msgs[num_msgs];
174		num_msgs++;
175	}
176	msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON;
177	if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) {
178		syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV");
179		return retval;
180	}
181	conv = (const struct pam_conv *)item;
182	if ((retval = conv->conv(num_msgs, msg_ptrs, &resp,
183	    conv->appdata_ptr)) != PAM_SUCCESS)
184		return retval;
185	if (build_access_request(radh, user, resp[num_msgs-1].resp, state,
186	    statelen) == -1)
187		return PAM_SERVICE_ERR;
188	memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp));
189	free(resp[num_msgs-1].resp);
190	free(resp);
191	while (num_msgs > 0)
192		free((void *)msgs[--num_msgs].msg);
193	return PAM_SUCCESS;
194}
195
196PAM_EXTERN int
197pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
198    const char **argv)
199{
200	struct rad_handle *radh;
201	const char *user;
202	const char *pass;
203	const char *conf_file = NULL;
204	const char *template_user = NULL;
205	int options = 0;
206	int retval;
207	int i;
208	int e;
209
210	for (i = 0;  i < argc;  i++) {
211		size_t len;
212
213		pam_std_option(&options, argv[i]);
214		if (strncmp(argv[i], OPT_CONF, (len = strlen(OPT_CONF))) == 0)
215			conf_file = argv[i] + len;
216		else if (strncmp(argv[i], OPT_TMPL,
217		    (len = strlen(OPT_TMPL))) == 0)
218			template_user = argv[i] + len;
219	}
220	if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
221		return retval;
222	if ((retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT,
223	    options)) != PAM_SUCCESS)
224		return retval;
225
226	if ((radh = rad_open()) == NULL) {
227		syslog(LOG_CRIT, "rad_open failed");
228		return PAM_SERVICE_ERR;
229	}
230	if (rad_config(radh, conf_file) == -1) {
231		syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh));
232		rad_close(radh);
233		return PAM_SERVICE_ERR;
234	}
235	if (build_access_request(radh, user, pass, NULL, 0) == -1) {
236		rad_close(radh);
237		return PAM_SERVICE_ERR;
238	}
239	for ( ; ; ) {
240		switch (rad_send_request(radh)) {
241
242		case RAD_ACCESS_ACCEPT:
243			e = do_accept(pamh, radh);
244			rad_close(radh);
245			if (e == -1)
246				return PAM_SERVICE_ERR;
247			if (template_user != NULL) {
248				const void *item;
249				const char *user;
250
251				/*
252				 * If the given user name doesn't exist in
253				 * the local password database, change it
254				 * to the value given in the "template_user"
255				 * option.
256				 */
257				retval = pam_get_item(pamh, PAM_USER, &item);
258				if (retval != PAM_SUCCESS)
259					return retval;
260				user = (const char *)item;
261				if (getpwnam(user) == NULL)
262					pam_set_item(pamh, PAM_USER,
263					    template_user);
264			}
265			return PAM_SUCCESS;
266
267		case RAD_ACCESS_REJECT:
268			rad_close(radh);
269			return PAM_AUTH_ERR;
270
271		case RAD_ACCESS_CHALLENGE:
272			if ((retval = do_challenge(pamh, radh, user)) !=
273			    PAM_SUCCESS) {
274				rad_close(radh);
275				return retval;
276			}
277			break;
278
279		case -1:
280			syslog(LOG_CRIT, "rad_send_request: %s",
281			    rad_strerror(radh));
282			rad_close(radh);
283			return PAM_AUTHINFO_UNAVAIL;
284
285		default:
286			syslog(LOG_CRIT,
287			    "rad_send_request: unexpected return value");
288			rad_close(radh);
289			return PAM_SERVICE_ERR;
290		}
291	}
292}
293
294PAM_EXTERN int
295pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
296{
297	return PAM_SUCCESS;
298}
299