pam_radius.c revision 81473
1226586Sdim/*- 2226586Sdim * Copyright 1998 Juniper Networks, Inc. 3226586Sdim * All rights reserved. 4226586Sdim * 5226586Sdim * Redistribution and use in source and binary forms, with or without 6226586Sdim * modification, are permitted provided that the following conditions 7226586Sdim * are met: 8226586Sdim * 1. Redistributions of source code must retain the above copyright 9226586Sdim * notice, this list of conditions and the following disclaimer. 10226586Sdim * 2. Redistributions in binary form must reproduce the above copyright 11226586Sdim * notice, this list of conditions and the following disclaimer in the 12226586Sdim * documentation and/or other materials provided with the distribution. 13226586Sdim * 14239462Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15226586Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16226586Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17226586Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18226586Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19226586Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20226586Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21226586Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22226586Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239462Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24226586Sdim * SUCH DAMAGE. 25226586Sdim * 26226586Sdim * $FreeBSD: head/lib/libpam/modules/pam_radius/pam_radius.c 81473 2001-08-10 19:12:59Z markm $ 27226586Sdim */ 28226586Sdim 29226586Sdim#include <sys/param.h> 30226586Sdim#include <pwd.h> 31226586Sdim#include <radlib.h> 32249423Sdim#include <stdlib.h> 33226586Sdim#include <string.h> 34234353Sdim#include <syslog.h> 35234353Sdim#include <unistd.h> 36234353Sdim 37249423Sdim#define PAM_SM_AUTH 38226586Sdim#include <security/pam_modules.h> 39226586Sdim 40226586Sdim#include "pam_mod_misc.h" 41239462Sdim 42226586Sdimenum { PAM_OPT_CONF=PAM_OPT_STD_MAX, PAM_OPT_TEMPLATE_USER }; 43226586Sdim 44226586Sdimstatic struct opttab other_options[] = { 45243830Sdim { "conf", PAM_OPT_CONF }, 46243830Sdim { "template_user", PAM_OPT_TEMPLATE_USER }, 47249423Sdim { NULL, 0 } 48243830Sdim}; 49249423Sdim 50226586Sdim#define MAX_CHALLENGE_MSGS 10 51226586Sdim#define PASSWORD_PROMPT "RADIUS password:" 52226586Sdim 53226586Sdimstatic int build_access_request(struct rad_handle *, const char *, 54226586Sdim const char *, const void *, size_t); 55226586Sdimstatic int do_accept(pam_handle_t *, struct rad_handle *); 56226586Sdimstatic int do_challenge(pam_handle_t *, struct rad_handle *, 57226586Sdim const char *); 58226586Sdim 59226586Sdim/* 60226586Sdim * Construct an access request, but don't send it. Returns 0 on success, 61226586Sdim * -1 on failure. 62226586Sdim */ 63226586Sdimstatic int 64226586Sdimbuild_access_request(struct rad_handle *radh, const char *user, 65226586Sdim const char *pass, const void *state, size_t state_len) 66226586Sdim{ 67226586Sdim char host[MAXHOSTNAMELEN]; 68226586Sdim 69226586Sdim if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 70226586Sdim syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 71226586Sdim return -1; 72226586Sdim } 73226586Sdim if ((user != NULL && 74226586Sdim rad_put_string(radh, RAD_USER_NAME, user) == -1) || 75226586Sdim (pass != NULL && 76249423Sdim rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 77249423Sdim (gethostname(host, sizeof host) != -1 && 78249423Sdim rad_put_string(radh, RAD_NAS_IDENTIFIER, host) == -1)) { 79226586Sdim syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 80226586Sdim return -1; 81226586Sdim } 82234353Sdim if (state != NULL && rad_put_attr(radh, RAD_STATE, state, 83234353Sdim state_len) == -1) { 84234353Sdim syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 85234353Sdim return -1; 86234353Sdim } 87234353Sdim if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 88234353Sdim syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 89234353Sdim return -1; 90234353Sdim } 91249423Sdim return 0; 92249423Sdim} 93226586Sdim 94226586Sdimstatic int 95226586Sdimdo_accept(pam_handle_t *pamh, struct rad_handle *radh) 96226586Sdim{ 97226586Sdim int attrtype; 98226586Sdim const void *attrval; 99226586Sdim size_t attrlen; 100239462Sdim char *s; 101239462Sdim 102226586Sdim while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 103226586Sdim if (attrtype == RAD_USER_NAME) { 104226586Sdim s = rad_cvt_string(attrval, attrlen); 105226586Sdim if (s == NULL) { 106226586Sdim syslog(LOG_CRIT, 107226586Sdim "rad_cvt_string: out of memory"); 108243830Sdim return -1; 109243830Sdim } 110243830Sdim pam_set_item(pamh, PAM_USER, s); 111243830Sdim free(s); 112243830Sdim } 113243830Sdim } 114243830Sdim if (attrtype == -1) { 115243830Sdim syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 116249423Sdim return -1; 117249423Sdim } 118249423Sdim return 0; 119249423Sdim} 120243830Sdim 121243830Sdimstatic int 122249423Sdimdo_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) 123249423Sdim{ 124249423Sdim int retval; 125249423Sdim int attrtype; 126243830Sdim const void *attrval; 127243830Sdim size_t attrlen; 128226586Sdim const void *state; 129226586Sdim size_t statelen; 130226586Sdim struct pam_message msgs[MAX_CHALLENGE_MSGS]; 131226586Sdim const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 132226586Sdim struct pam_response *resp; 133226586Sdim int num_msgs; 134226586Sdim const void *item; 135226586Sdim const struct pam_conv *conv; 136226586Sdim 137226586Sdim state = NULL; 138226586Sdim statelen = 0; 139226586Sdim num_msgs = 0; 140226586Sdim while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 141243830Sdim switch (attrtype) { 142243830Sdim 143243830Sdim case RAD_STATE: 144243830Sdim state = attrval; 145243830Sdim statelen = attrlen; 146243830Sdim break; 147243830Sdim 148243830Sdim case RAD_REPLY_MESSAGE: 149243830Sdim if (num_msgs >= MAX_CHALLENGE_MSGS) { 150243830Sdim syslog(LOG_CRIT, 151243830Sdim "Too many RADIUS challenge messages"); 152243830Sdim return PAM_SERVICE_ERR; 153243830Sdim } 154243830Sdim msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 155243830Sdim if (msgs[num_msgs].msg == NULL) { 156243830Sdim syslog(LOG_CRIT, 157243830Sdim "rad_cvt_string: out of memory"); 158243830Sdim return PAM_SERVICE_ERR; 159243830Sdim } 160243830Sdim msgs[num_msgs].msg_style = PAM_TEXT_INFO; 161249423Sdim msg_ptrs[num_msgs] = &msgs[num_msgs]; 162249423Sdim num_msgs++; 163249423Sdim break; 164243830Sdim } 165243830Sdim } 166243830Sdim if (attrtype == -1) { 167243830Sdim syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 168243830Sdim return PAM_SERVICE_ERR; 169243830Sdim } 170243830Sdim if (num_msgs == 0) { 171243830Sdim msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 172243830Sdim if (msgs[num_msgs].msg == NULL) { 173243830Sdim syslog(LOG_CRIT, "Out of memory"); 174243830Sdim return PAM_SERVICE_ERR; 175243830Sdim } 176249423Sdim msgs[num_msgs].msg_style = PAM_TEXT_INFO; 177249423Sdim msg_ptrs[num_msgs] = &msgs[num_msgs]; 178249423Sdim num_msgs++; 179243830Sdim } 180243830Sdim msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 181243830Sdim if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 182243830Sdim syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 183243830Sdim return retval; 184243830Sdim } 185243830Sdim conv = (const struct pam_conv *)item; 186243830Sdim if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 187243830Sdim conv->appdata_ptr)) != PAM_SUCCESS) 188243830Sdim return retval; 189243830Sdim if (build_access_request(radh, user, resp[num_msgs-1].resp, state, 190243830Sdim statelen) == -1) 191243830Sdim return PAM_SERVICE_ERR; 192243830Sdim memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 193243830Sdim free(resp[num_msgs-1].resp); 194243830Sdim free(resp); 195243830Sdim while (num_msgs > 0) 196243830Sdim free((void *)msgs[--num_msgs].msg); 197243830Sdim return PAM_SUCCESS; 198243830Sdim} 199243830Sdim 200243830SdimPAM_EXTERN int 201243830Sdimpam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 202243830Sdim{ 203243830Sdim struct options options; 204243830Sdim struct rad_handle *radh; 205243830Sdim const char *user, *tmpuser, *pass; 206243830Sdim char *conf_file, *template_user; 207249423Sdim int retval; 208249423Sdim int e; 209249423Sdim 210243830Sdim pam_std_option(&options, other_options, argc, argv); 211243830Sdim 212243830Sdim PAM_LOG("Options processed"); 213249423Sdim 214249423Sdim conf_file = NULL; 215249423Sdim pam_test_option(&options, PAM_OPT_CONF, &conf_file); 216243830Sdim template_user = NULL; 217243830Sdim pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user); 218243830Sdim 219243830Sdim retval = pam_get_user(pamh, &user, NULL); 220243830Sdim if (retval != PAM_SUCCESS) 221243830Sdim PAM_RETURN(retval); 222243830Sdim 223243830Sdim PAM_LOG("Got user: %s", user); 224243830Sdim 225243830Sdim retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options); 226243830Sdim if (retval != PAM_SUCCESS) 227243830Sdim PAM_RETURN(retval); 228243830Sdim 229243830Sdim PAM_LOG("Got password"); 230243830Sdim 231243830Sdim radh = rad_open(); 232226586Sdim if (radh == NULL) { 233243830Sdim syslog(LOG_CRIT, "rad_open failed"); 234234353Sdim PAM_RETURN(PAM_SERVICE_ERR); 235243830Sdim } 236226586Sdim 237226586Sdim PAM_LOG("Radius opened"); 238226586Sdim 239226586Sdim if (rad_config(radh, conf_file) == -1) { 240226586Sdim syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 241226586Sdim rad_close(radh); 242243830Sdim PAM_RETURN(PAM_SERVICE_ERR); 243226586Sdim } 244 245 PAM_LOG("Radius config file read"); 246 247 if (build_access_request(radh, user, pass, NULL, 0) == -1) { 248 rad_close(radh); 249 PAM_RETURN(PAM_SERVICE_ERR); 250 } 251 252 PAM_LOG("Radius build access done"); 253 254 for ( ; ; ) { 255 switch (rad_send_request(radh)) { 256 257 case RAD_ACCESS_ACCEPT: 258 e = do_accept(pamh, radh); 259 rad_close(radh); 260 if (e == -1) 261 PAM_RETURN(PAM_SERVICE_ERR); 262 if (template_user != NULL) { 263 264 PAM_LOG("Trying template user: %s", 265 template_user); 266 267 /* 268 * If the given user name doesn't exist in 269 * the local password database, change it 270 * to the value given in the "template_user" 271 * option. 272 */ 273 retval = pam_get_item(pamh, PAM_USER, 274 (void *)&tmpuser); 275 if (retval != PAM_SUCCESS) 276 PAM_RETURN(retval); 277 if (getpwnam(tmpuser) == NULL) { 278 pam_set_item(pamh, PAM_USER, 279 template_user); 280 PAM_LOG("Using template user"); 281 } 282 283 } 284 PAM_RETURN(PAM_SUCCESS); 285 286 case RAD_ACCESS_REJECT: 287 rad_close(radh); 288 PAM_VERBOSE_ERROR("Radius rejection"); 289 PAM_RETURN(PAM_AUTH_ERR); 290 291 case RAD_ACCESS_CHALLENGE: 292 retval = do_challenge(pamh, radh, user); 293 if (retval != PAM_SUCCESS) { 294 rad_close(radh); 295 PAM_RETURN(retval); 296 } 297 break; 298 299 case -1: 300 syslog(LOG_CRIT, "rad_send_request: %s", 301 rad_strerror(radh)); 302 rad_close(radh); 303 PAM_VERBOSE_ERROR("Radius failure"); 304 PAM_RETURN(PAM_AUTHINFO_UNAVAIL); 305 306 default: 307 syslog(LOG_CRIT, 308 "rad_send_request: unexpected return value"); 309 rad_close(radh); 310 PAM_VERBOSE_ERROR("Radius error"); 311 PAM_RETURN(PAM_SERVICE_ERR); 312 } 313 } 314} 315 316PAM_EXTERN int 317pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 318{ 319 struct options options; 320 321 pam_std_option(&options, NULL, argc, argv); 322 323 PAM_LOG("Options processed"); 324 325 PAM_RETURN(PAM_SUCCESS); 326} 327 328PAM_MODULE_ENTRY("pam_radius"); 329