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$ 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 49 static int build_access_request(struct rad_handle *, const char *, 50 const char *, const void *, size_t); 51 static int do_accept(pam_handle_t *, struct rad_handle *); 52 static 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 */ 59 static int 60 build_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 90 static int 91 do_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 117 static int 118 do_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 196 PAM_EXTERN int 197 pam_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 294 PAM_EXTERN int 295 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 296 { 297 return PAM_SUCCESS; 298 } 299 300 PAM_MODULE_ENTRY("pam_radius"); 301