pam_radius.c revision 87398
1130803Smarcel/*- 2130803Smarcel * Copyright 1998 Juniper Networks, Inc. 3130803Smarcel * All rights reserved. 4130803Smarcel * Copyright (c) 2001 Networks Associates Technologies, Inc. 5130803Smarcel * All rights reserved. 6130803Smarcel * 7130803Smarcel * Portions of this software were developed for the FreeBSD Project by 8130803Smarcel * ThinkSec AS and NAI Labs, the Security Research Division of Network 9130803Smarcel * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10130803Smarcel * ("CBOSS"), as part of the DARPA CHATS research program. 11130803Smarcel * 12130803Smarcel * Redistribution and use in source and binary forms, with or without 13130803Smarcel * modification, are permitted provided that the following conditions 14130803Smarcel * are met: 15130803Smarcel * 1. Redistributions of source code must retain the above copyright 16130803Smarcel * notice, this list of conditions and the following disclaimer. 17130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright 18130803Smarcel * notice, this list of conditions and the following disclaimer in the 19130803Smarcel * documentation and/or other materials provided with the distribution. 20130803Smarcel * 3. The name of the author may not be used to endorse or promote 21130803Smarcel * products derived from this software without specific prior written 22130803Smarcel * permission. 23130803Smarcel * 24130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25130803Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26130803Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27130803Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28130803Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29130803Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30130803Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32130803Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33130803Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34130803Smarcel * SUCH DAMAGE. 35130803Smarcel */ 36130803Smarcel 37130803Smarcel#include <sys/cdefs.h> 38130803Smarcel__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_radius/pam_radius.c 87398 2001-12-05 16:06:35Z des $"); 39130803Smarcel 40130803Smarcel#include <sys/param.h> 41130803Smarcel#include <pwd.h> 42130803Smarcel#include <radlib.h> 43130803Smarcel#include <stdlib.h> 44130803Smarcel#include <string.h> 45130803Smarcel#include <syslog.h> 46130803Smarcel#include <unistd.h> 47130803Smarcel 48130803Smarcel#define PAM_SM_AUTH 49130803Smarcel#define PAM_SM_ACCOUNT 50130803Smarcel#define PAM_SM_SESSION 51130803Smarcel#define PAM_SM_PASSWORD 52130803Smarcel 53130803Smarcel#include <security/pam_modules.h> 54130803Smarcel 55130803Smarcel#include "pam_mod_misc.h" 56130803Smarcel 57130803Smarcelenum { PAM_OPT_CONF=PAM_OPT_STD_MAX, PAM_OPT_TEMPLATE_USER }; 58130803Smarcel 59130803Smarcelstatic struct opttab other_options[] = { 60130803Smarcel { "conf", PAM_OPT_CONF }, 61130803Smarcel { "template_user", PAM_OPT_TEMPLATE_USER }, 62130803Smarcel { NULL, 0 } 63130803Smarcel}; 64130803Smarcel 65130803Smarcel#define MAX_CHALLENGE_MSGS 10 66130803Smarcel#define PASSWORD_PROMPT "RADIUS password:" 67130803Smarcel 68130803Smarcelstatic int build_access_request(struct rad_handle *, const char *, 69130803Smarcel const char *, const void *, size_t); 70130803Smarcelstatic int do_accept(pam_handle_t *, struct rad_handle *); 71130803Smarcelstatic int do_challenge(pam_handle_t *, struct rad_handle *, 72130803Smarcel const char *); 73130803Smarcel 74130803Smarcel/* 75130803Smarcel * Construct an access request, but don't send it. Returns 0 on success, 76130803Smarcel * -1 on failure. 77130803Smarcel */ 78130803Smarcelstatic int 79130803Smarcelbuild_access_request(struct rad_handle *radh, const char *user, 80130803Smarcel const char *pass, const void *state, size_t state_len) 81130803Smarcel{ 82130803Smarcel char host[MAXHOSTNAMELEN]; 83130803Smarcel 84130803Smarcel if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 85130803Smarcel syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 86130803Smarcel return -1; 87130803Smarcel } 88130803Smarcel if ((user != NULL && 89130803Smarcel rad_put_string(radh, RAD_USER_NAME, user) == -1) || 90130803Smarcel (pass != NULL && 91130803Smarcel rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 92130803Smarcel (gethostname(host, sizeof host) != -1 && 93130803Smarcel rad_put_string(radh, RAD_NAS_IDENTIFIER, host) == -1)) { 94130803Smarcel syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 95130803Smarcel return -1; 96130803Smarcel } 97130803Smarcel if (state != NULL && rad_put_attr(radh, RAD_STATE, state, 98130803Smarcel state_len) == -1) { 99130803Smarcel syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 100130803Smarcel return -1; 101130803Smarcel } 102130803Smarcel if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 103130803Smarcel syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 104130803Smarcel return -1; 105130803Smarcel } 106130803Smarcel return 0; 107130803Smarcel} 108130803Smarcel 109130803Smarcelstatic int 110130803Smarceldo_accept(pam_handle_t *pamh, struct rad_handle *radh) 111130803Smarcel{ 112130803Smarcel int attrtype; 113130803Smarcel const void *attrval; 114130803Smarcel size_t attrlen; 115130803Smarcel char *s; 116130803Smarcel 117130803Smarcel while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 118130803Smarcel if (attrtype == RAD_USER_NAME) { 119130803Smarcel s = rad_cvt_string(attrval, attrlen); 120 if (s == NULL) { 121 syslog(LOG_CRIT, 122 "rad_cvt_string: out of memory"); 123 return -1; 124 } 125 pam_set_item(pamh, PAM_USER, s); 126 free(s); 127 } 128 } 129 if (attrtype == -1) { 130 syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 131 return -1; 132 } 133 return 0; 134} 135 136static int 137do_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) 138{ 139 int retval; 140 int attrtype; 141 const void *attrval; 142 size_t attrlen; 143 const void *state; 144 size_t statelen; 145 struct pam_message msgs[MAX_CHALLENGE_MSGS]; 146 const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 147 struct pam_response *resp; 148 int num_msgs; 149 const void *item; 150 const struct pam_conv *conv; 151 152 state = NULL; 153 statelen = 0; 154 num_msgs = 0; 155 while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 156 switch (attrtype) { 157 158 case RAD_STATE: 159 state = attrval; 160 statelen = attrlen; 161 break; 162 163 case RAD_REPLY_MESSAGE: 164 if (num_msgs >= MAX_CHALLENGE_MSGS) { 165 syslog(LOG_CRIT, 166 "Too many RADIUS challenge messages"); 167 return PAM_SERVICE_ERR; 168 } 169 msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 170 if (msgs[num_msgs].msg == NULL) { 171 syslog(LOG_CRIT, 172 "rad_cvt_string: out of memory"); 173 return PAM_SERVICE_ERR; 174 } 175 msgs[num_msgs].msg_style = PAM_TEXT_INFO; 176 msg_ptrs[num_msgs] = &msgs[num_msgs]; 177 num_msgs++; 178 break; 179 } 180 } 181 if (attrtype == -1) { 182 syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 183 return PAM_SERVICE_ERR; 184 } 185 if (num_msgs == 0) { 186 msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 187 if (msgs[num_msgs].msg == NULL) { 188 syslog(LOG_CRIT, "Out of memory"); 189 return PAM_SERVICE_ERR; 190 } 191 msgs[num_msgs].msg_style = PAM_TEXT_INFO; 192 msg_ptrs[num_msgs] = &msgs[num_msgs]; 193 num_msgs++; 194 } 195 msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 196 if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 197 syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 198 return retval; 199 } 200 conv = (const struct pam_conv *)item; 201 if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 202 conv->appdata_ptr)) != PAM_SUCCESS) 203 return retval; 204 if (build_access_request(radh, user, resp[num_msgs-1].resp, state, 205 statelen) == -1) 206 return PAM_SERVICE_ERR; 207 memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 208 free(resp[num_msgs-1].resp); 209 free(resp); 210 while (num_msgs > 0) 211 free((void *)msgs[--num_msgs].msg); 212 return PAM_SUCCESS; 213} 214 215PAM_EXTERN int 216pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 217{ 218 struct options options; 219 struct rad_handle *radh; 220 const char *user, *tmpuser, *pass; 221 char *conf_file, *template_user; 222 int retval; 223 int e; 224 225 pam_std_option(&options, other_options, argc, argv); 226 227 PAM_LOG("Options processed"); 228 229 conf_file = NULL; 230 pam_test_option(&options, PAM_OPT_CONF, &conf_file); 231 template_user = NULL; 232 pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user); 233 234 retval = pam_get_user(pamh, &user, NULL); 235 if (retval != PAM_SUCCESS) 236 PAM_RETURN(retval); 237 238 PAM_LOG("Got user: %s", user); 239 240 retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, &options); 241 if (retval != PAM_SUCCESS) 242 PAM_RETURN(retval); 243 244 PAM_LOG("Got password"); 245 246 radh = rad_open(); 247 if (radh == NULL) { 248 syslog(LOG_CRIT, "rad_open failed"); 249 PAM_RETURN(PAM_SERVICE_ERR); 250 } 251 252 PAM_LOG("Radius opened"); 253 254 if (rad_config(radh, conf_file) == -1) { 255 syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 256 rad_close(radh); 257 PAM_RETURN(PAM_SERVICE_ERR); 258 } 259 260 PAM_LOG("Radius config file read"); 261 262 if (build_access_request(radh, user, pass, NULL, 0) == -1) { 263 rad_close(radh); 264 PAM_RETURN(PAM_SERVICE_ERR); 265 } 266 267 PAM_LOG("Radius build access done"); 268 269 for (;;) { 270 switch (rad_send_request(radh)) { 271 272 case RAD_ACCESS_ACCEPT: 273 e = do_accept(pamh, radh); 274 rad_close(radh); 275 if (e == -1) 276 PAM_RETURN(PAM_SERVICE_ERR); 277 if (template_user != NULL) { 278 279 PAM_LOG("Trying template user: %s", 280 template_user); 281 282 /* 283 * If the given user name doesn't exist in 284 * the local password database, change it 285 * to the value given in the "template_user" 286 * option. 287 */ 288 retval = pam_get_item(pamh, PAM_USER, 289 (void *)&tmpuser); 290 if (retval != PAM_SUCCESS) 291 PAM_RETURN(retval); 292 if (getpwnam(tmpuser) == NULL) { 293 pam_set_item(pamh, PAM_USER, 294 template_user); 295 PAM_LOG("Using template user"); 296 } 297 298 } 299 PAM_RETURN(PAM_SUCCESS); 300 301 case RAD_ACCESS_REJECT: 302 rad_close(radh); 303 PAM_VERBOSE_ERROR("Radius rejection"); 304 PAM_RETURN(PAM_AUTH_ERR); 305 306 case RAD_ACCESS_CHALLENGE: 307 retval = do_challenge(pamh, radh, user); 308 if (retval != PAM_SUCCESS) { 309 rad_close(radh); 310 PAM_RETURN(retval); 311 } 312 break; 313 314 case -1: 315 syslog(LOG_CRIT, "rad_send_request: %s", 316 rad_strerror(radh)); 317 rad_close(radh); 318 PAM_VERBOSE_ERROR("Radius failure"); 319 PAM_RETURN(PAM_AUTHINFO_UNAVAIL); 320 321 default: 322 syslog(LOG_CRIT, 323 "rad_send_request: unexpected return value"); 324 rad_close(radh); 325 PAM_VERBOSE_ERROR("Radius error"); 326 PAM_RETURN(PAM_SERVICE_ERR); 327 } 328 } 329} 330 331PAM_EXTERN int 332pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 333{ 334 struct options options; 335 336 pam_std_option(&options, NULL, argc, argv); 337 338 PAM_LOG("Options processed"); 339 340 PAM_RETURN(PAM_SUCCESS); 341} 342 343PAM_EXTERN int 344pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc ,const char **argv) 345{ 346 struct options options; 347 348 pam_std_option(&options, NULL, argc, argv); 349 350 PAM_LOG("Options processed"); 351 352 PAM_RETURN(PAM_IGNORE); 353} 354 355PAM_EXTERN int 356pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 357{ 358 struct options options; 359 360 pam_std_option(&options, NULL, argc, argv); 361 362 PAM_LOG("Options processed"); 363 364 PAM_RETURN(PAM_IGNORE); 365} 366 367PAM_EXTERN int 368pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 369{ 370 struct options options; 371 372 pam_std_option(&options, NULL, argc, argv); 373 374 PAM_LOG("Options processed"); 375 376 PAM_RETURN(PAM_IGNORE); 377} 378 379PAM_EXTERN int 380pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 381{ 382 struct options options; 383 384 pam_std_option(&options, NULL, argc, argv); 385 386 PAM_LOG("Options processed"); 387 388 PAM_RETURN(PAM_IGNORE); 389} 390 391PAM_MODULE_ENTRY("pam_radius"); 392