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