pam_radius.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_radius/pam_radius.c 87398 2001-12-05 16:06:35Z des $"); 3984218Sdillon 4041227Sjdp#include <sys/param.h> 4141227Sjdp#include <pwd.h> 4241227Sjdp#include <radlib.h> 4341227Sjdp#include <stdlib.h> 4441227Sjdp#include <string.h> 4541227Sjdp#include <syslog.h> 4641227Sjdp#include <unistd.h> 4741227Sjdp 4841227Sjdp#define PAM_SM_AUTH 4987398Sdes#define PAM_SM_ACCOUNT 5087398Sdes#define PAM_SM_SESSION 5187398Sdes#define PAM_SM_PASSWORD 5287398Sdes 5341227Sjdp#include <security/pam_modules.h> 5441227Sjdp 5541227Sjdp#include "pam_mod_misc.h" 5641227Sjdp 5779476Smarkmenum { PAM_OPT_CONF=PAM_OPT_STD_MAX, PAM_OPT_TEMPLATE_USER }; 5841227Sjdp 5979476Smarkmstatic struct opttab other_options[] = { 6079476Smarkm { "conf", PAM_OPT_CONF }, 6179476Smarkm { "template_user", PAM_OPT_TEMPLATE_USER }, 6279476Smarkm { NULL, 0 } 6379476Smarkm}; 6441227Sjdp 6579476Smarkm#define MAX_CHALLENGE_MSGS 10 6679476Smarkm#define PASSWORD_PROMPT "RADIUS password:" 6779476Smarkm 6841227Sjdpstatic int build_access_request(struct rad_handle *, const char *, 6941227Sjdp const char *, const void *, size_t); 7041227Sjdpstatic int do_accept(pam_handle_t *, struct rad_handle *); 7141227Sjdpstatic int do_challenge(pam_handle_t *, struct rad_handle *, 7241227Sjdp const char *); 7341227Sjdp 7441227Sjdp/* 7541227Sjdp * Construct an access request, but don't send it. Returns 0 on success, 7641227Sjdp * -1 on failure. 7741227Sjdp */ 7841227Sjdpstatic int 7941227Sjdpbuild_access_request(struct rad_handle *radh, const char *user, 8041227Sjdp const char *pass, const void *state, size_t state_len) 8141227Sjdp{ 8241227Sjdp char host[MAXHOSTNAMELEN]; 8341227Sjdp 8441227Sjdp if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 8541227Sjdp syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 8641227Sjdp return -1; 8741227Sjdp } 8841227Sjdp if ((user != NULL && 8941227Sjdp rad_put_string(radh, RAD_USER_NAME, user) == -1) || 9041227Sjdp (pass != NULL && 9141227Sjdp rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 9241227Sjdp (gethostname(host, sizeof host) != -1 && 9341227Sjdp rad_put_string(radh, RAD_NAS_IDENTIFIER, host) == -1)) { 9441227Sjdp syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 9541227Sjdp return -1; 9641227Sjdp } 9741227Sjdp if (state != NULL && rad_put_attr(radh, RAD_STATE, state, 9841227Sjdp state_len) == -1) { 9941227Sjdp syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 10041227Sjdp return -1; 10141227Sjdp } 10241227Sjdp if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 10341227Sjdp syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 10441227Sjdp return -1; 10541227Sjdp } 10641227Sjdp return 0; 10741227Sjdp} 10841227Sjdp 10941227Sjdpstatic int 11041227Sjdpdo_accept(pam_handle_t *pamh, struct rad_handle *radh) 11141227Sjdp{ 11241227Sjdp int attrtype; 11341227Sjdp const void *attrval; 11441227Sjdp size_t attrlen; 11541227Sjdp char *s; 11641227Sjdp 11741227Sjdp while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 11841227Sjdp if (attrtype == RAD_USER_NAME) { 11941227Sjdp s = rad_cvt_string(attrval, attrlen); 12041227Sjdp if (s == NULL) { 12141227Sjdp syslog(LOG_CRIT, 12241227Sjdp "rad_cvt_string: out of memory"); 12341227Sjdp return -1; 12441227Sjdp } 12541227Sjdp pam_set_item(pamh, PAM_USER, s); 12641227Sjdp free(s); 12741227Sjdp } 12841227Sjdp } 12941227Sjdp if (attrtype == -1) { 13041227Sjdp syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 13141227Sjdp return -1; 13241227Sjdp } 13341227Sjdp return 0; 13441227Sjdp} 13541227Sjdp 13641227Sjdpstatic int 13741227Sjdpdo_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) 13841227Sjdp{ 13941227Sjdp int retval; 14041227Sjdp int attrtype; 14141227Sjdp const void *attrval; 14241227Sjdp size_t attrlen; 14341227Sjdp const void *state; 14441227Sjdp size_t statelen; 14541227Sjdp struct pam_message msgs[MAX_CHALLENGE_MSGS]; 14641227Sjdp const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 14741227Sjdp struct pam_response *resp; 14841227Sjdp int num_msgs; 14941227Sjdp const void *item; 15041227Sjdp const struct pam_conv *conv; 15141227Sjdp 15241227Sjdp state = NULL; 15341227Sjdp statelen = 0; 15441227Sjdp num_msgs = 0; 15541227Sjdp while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 15641227Sjdp switch (attrtype) { 15741227Sjdp 15841227Sjdp case RAD_STATE: 15941227Sjdp state = attrval; 16041227Sjdp statelen = attrlen; 16141227Sjdp break; 16241227Sjdp 16341227Sjdp case RAD_REPLY_MESSAGE: 16441227Sjdp if (num_msgs >= MAX_CHALLENGE_MSGS) { 16541227Sjdp syslog(LOG_CRIT, 16641227Sjdp "Too many RADIUS challenge messages"); 16741227Sjdp return PAM_SERVICE_ERR; 16841227Sjdp } 16941227Sjdp msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 17041227Sjdp if (msgs[num_msgs].msg == NULL) { 17141227Sjdp syslog(LOG_CRIT, 17241227Sjdp "rad_cvt_string: out of memory"); 17341227Sjdp return PAM_SERVICE_ERR; 17441227Sjdp } 17541227Sjdp msgs[num_msgs].msg_style = PAM_TEXT_INFO; 17641227Sjdp msg_ptrs[num_msgs] = &msgs[num_msgs]; 17741227Sjdp num_msgs++; 17841227Sjdp break; 17941227Sjdp } 18041227Sjdp } 18141227Sjdp if (attrtype == -1) { 18241227Sjdp syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 18341227Sjdp return PAM_SERVICE_ERR; 18441227Sjdp } 18541227Sjdp if (num_msgs == 0) { 18641227Sjdp msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 18741227Sjdp if (msgs[num_msgs].msg == NULL) { 18841227Sjdp syslog(LOG_CRIT, "Out of memory"); 18941227Sjdp return PAM_SERVICE_ERR; 19041227Sjdp } 19141227Sjdp msgs[num_msgs].msg_style = PAM_TEXT_INFO; 19241227Sjdp msg_ptrs[num_msgs] = &msgs[num_msgs]; 19341227Sjdp num_msgs++; 19441227Sjdp } 19541227Sjdp msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 19641227Sjdp if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 19741227Sjdp syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 19841227Sjdp return retval; 19941227Sjdp } 20041227Sjdp conv = (const struct pam_conv *)item; 20141227Sjdp if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 20241227Sjdp conv->appdata_ptr)) != PAM_SUCCESS) 20341227Sjdp return retval; 20441227Sjdp if (build_access_request(radh, user, resp[num_msgs-1].resp, state, 20541227Sjdp statelen) == -1) 20641227Sjdp return PAM_SERVICE_ERR; 20741227Sjdp memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 20841227Sjdp free(resp[num_msgs-1].resp); 20941227Sjdp free(resp); 21041227Sjdp while (num_msgs > 0) 21141227Sjdp free((void *)msgs[--num_msgs].msg); 21241227Sjdp return PAM_SUCCESS; 21341227Sjdp} 21441227Sjdp 21541227SjdpPAM_EXTERN int 21679476Smarkmpam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 21741227Sjdp{ 21879476Smarkm struct options options; 21941227Sjdp struct rad_handle *radh; 22079476Smarkm const char *user, *tmpuser, *pass; 22179476Smarkm char *conf_file, *template_user; 22241227Sjdp int retval; 22341227Sjdp int e; 22441227Sjdp 22579476Smarkm pam_std_option(&options, other_options, argc, argv); 22641227Sjdp 22779476Smarkm PAM_LOG("Options processed"); 22841227Sjdp 22979476Smarkm conf_file = NULL; 23079476Smarkm pam_test_option(&options, PAM_OPT_CONF, &conf_file); 23179476Smarkm template_user = NULL; 23279476Smarkm pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user); 23379476Smarkm 23479476Smarkm retval = pam_get_user(pamh, &user, NULL); 23579476Smarkm if (retval != PAM_SUCCESS) 23679476Smarkm PAM_RETURN(retval); 23779476Smarkm 23879476Smarkm PAM_LOG("Got user: %s", user); 23979476Smarkm 24079476Smarkm retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options); 24179476Smarkm if (retval != PAM_SUCCESS) 24279476Smarkm PAM_RETURN(retval); 24379476Smarkm 24479476Smarkm PAM_LOG("Got password"); 24579476Smarkm 24679476Smarkm radh = rad_open(); 24779476Smarkm if (radh == NULL) { 24841227Sjdp syslog(LOG_CRIT, "rad_open failed"); 24979476Smarkm PAM_RETURN(PAM_SERVICE_ERR); 25041227Sjdp } 25179476Smarkm 25279476Smarkm PAM_LOG("Radius opened"); 25379476Smarkm 25441227Sjdp if (rad_config(radh, conf_file) == -1) { 25541227Sjdp syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 25641227Sjdp rad_close(radh); 25779476Smarkm PAM_RETURN(PAM_SERVICE_ERR); 25841227Sjdp } 25979476Smarkm 26079476Smarkm PAM_LOG("Radius config file read"); 26179476Smarkm 26241227Sjdp if (build_access_request(radh, user, pass, NULL, 0) == -1) { 26341227Sjdp rad_close(radh); 26479476Smarkm PAM_RETURN(PAM_SERVICE_ERR); 26541227Sjdp } 26679476Smarkm 26779476Smarkm PAM_LOG("Radius build access done"); 26879476Smarkm 26987398Sdes for (;;) { 27041227Sjdp switch (rad_send_request(radh)) { 27141227Sjdp 27241227Sjdp case RAD_ACCESS_ACCEPT: 27341227Sjdp e = do_accept(pamh, radh); 27441227Sjdp rad_close(radh); 27541227Sjdp if (e == -1) 27679476Smarkm PAM_RETURN(PAM_SERVICE_ERR); 27741227Sjdp if (template_user != NULL) { 27841227Sjdp 27979476Smarkm PAM_LOG("Trying template user: %s", 28079476Smarkm template_user); 28179476Smarkm 28241227Sjdp /* 28341227Sjdp * If the given user name doesn't exist in 28441227Sjdp * the local password database, change it 28541227Sjdp * to the value given in the "template_user" 28641227Sjdp * option. 28741227Sjdp */ 28879476Smarkm retval = pam_get_item(pamh, PAM_USER, 28979476Smarkm (void *)&tmpuser); 29041227Sjdp if (retval != PAM_SUCCESS) 29179476Smarkm PAM_RETURN(retval); 29279476Smarkm if (getpwnam(tmpuser) == NULL) { 29341227Sjdp pam_set_item(pamh, PAM_USER, 29441227Sjdp template_user); 29579476Smarkm PAM_LOG("Using template user"); 29679476Smarkm } 29779476Smarkm 29841227Sjdp } 29979476Smarkm PAM_RETURN(PAM_SUCCESS); 30041227Sjdp 30141227Sjdp case RAD_ACCESS_REJECT: 30241227Sjdp rad_close(radh); 30381473Smarkm PAM_VERBOSE_ERROR("Radius rejection"); 30479476Smarkm PAM_RETURN(PAM_AUTH_ERR); 30541227Sjdp 30641227Sjdp case RAD_ACCESS_CHALLENGE: 30779476Smarkm retval = do_challenge(pamh, radh, user); 30879476Smarkm if (retval != PAM_SUCCESS) { 30941227Sjdp rad_close(radh); 31079476Smarkm PAM_RETURN(retval); 31141227Sjdp } 31241227Sjdp break; 31341227Sjdp 31441227Sjdp case -1: 31541227Sjdp syslog(LOG_CRIT, "rad_send_request: %s", 31641227Sjdp rad_strerror(radh)); 31741227Sjdp rad_close(radh); 31881473Smarkm PAM_VERBOSE_ERROR("Radius failure"); 31979476Smarkm PAM_RETURN(PAM_AUTHINFO_UNAVAIL); 32041227Sjdp 32141227Sjdp default: 32241227Sjdp syslog(LOG_CRIT, 32341227Sjdp "rad_send_request: unexpected return value"); 32441227Sjdp rad_close(radh); 32581473Smarkm PAM_VERBOSE_ERROR("Radius error"); 32679476Smarkm PAM_RETURN(PAM_SERVICE_ERR); 32741227Sjdp } 32841227Sjdp } 32941227Sjdp} 33041227Sjdp 33141227SjdpPAM_EXTERN int 33241227Sjdppam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 33341227Sjdp{ 33481473Smarkm struct options options; 33581473Smarkm 33681473Smarkm pam_std_option(&options, NULL, argc, argv); 33781473Smarkm 33881473Smarkm PAM_LOG("Options processed"); 33981473Smarkm 34081473Smarkm PAM_RETURN(PAM_SUCCESS); 34141227Sjdp} 34242917Sjdp 34387398SdesPAM_EXTERN int 34487398Sdespam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc ,const char **argv) 34587398Sdes{ 34687398Sdes struct options options; 34787398Sdes 34887398Sdes pam_std_option(&options, NULL, argc, argv); 34987398Sdes 35087398Sdes PAM_LOG("Options processed"); 35187398Sdes 35287398Sdes PAM_RETURN(PAM_IGNORE); 35387398Sdes} 35487398Sdes 35587398SdesPAM_EXTERN int 35687398Sdespam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 35787398Sdes{ 35887398Sdes struct options options; 35987398Sdes 36087398Sdes pam_std_option(&options, NULL, argc, argv); 36187398Sdes 36287398Sdes PAM_LOG("Options processed"); 36387398Sdes 36487398Sdes PAM_RETURN(PAM_IGNORE); 36587398Sdes} 36687398Sdes 36787398SdesPAM_EXTERN int 36887398Sdespam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 36987398Sdes{ 37087398Sdes struct options options; 37187398Sdes 37287398Sdes pam_std_option(&options, NULL, argc, argv); 37387398Sdes 37487398Sdes PAM_LOG("Options processed"); 37587398Sdes 37687398Sdes PAM_RETURN(PAM_IGNORE); 37787398Sdes} 37887398Sdes 37987398SdesPAM_EXTERN int 38087398Sdespam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 38187398Sdes{ 38287398Sdes struct options options; 38387398Sdes 38487398Sdes pam_std_option(&options, NULL, argc, argv); 38587398Sdes 38687398Sdes PAM_LOG("Options processed"); 38787398Sdes 38887398Sdes PAM_RETURN(PAM_IGNORE); 38987398Sdes} 39087398Sdes 39142917SjdpPAM_MODULE_ENTRY("pam_radius"); 392