pam_radius.c revision 41227
1169689Skan/*- 2169689Skan * Copyright 1998 Juniper Networks, Inc. 3169689Skan * All rights reserved. 4169689Skan * 5169689Skan * Redistribution and use in source and binary forms, with or without 6169689Skan * modification, are permitted provided that the following conditions 7169689Skan * are met: 8169689Skan * 1. Redistributions of source code must retain the above copyright 9169689Skan * notice, this list of conditions and the following disclaimer. 10169689Skan * 2. Redistributions in binary form must reproduce the above copyright 11169689Skan * notice, this list of conditions and the following disclaimer in the 12169689Skan * documentation and/or other materials provided with the distribution. 13169689Skan * 14169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169689Skan * SUCH DAMAGE. 25169689Skan * 26169689Skan * $FreeBSD: cvs2svn/branches/JUNIPER/lib/libpam/modules/pam_radius/pam_radius.c 41227 1998-11-18 01:44:37Z jdp $ 27169689Skan */ 28169689Skan 29169689Skan#include <sys/param.h> 30169689Skan#include <pwd.h> 31169689Skan#include <radlib.h> 32169689Skan#include <stdlib.h> 33169689Skan#include <string.h> 34169689Skan#include <syslog.h> 35169689Skan#include <unistd.h> 36169689Skan 37169689Skan#define PAM_SM_AUTH 38169689Skan#include <security/pam_modules.h> 39169689Skan 40169689Skan#include "pam_mod_misc.h" 41169689Skan 42169689Skan#define MAX_CHALLENGE_MSGS 10 43169689Skan#define PASSWORD_PROMPT "RADIUS password:" 44169689Skan 45169689Skan/* Option names, including the "=" sign. */ 46169689Skan#define OPT_CONF "conf=" 47169689Skan#define OPT_TMPL "template_user=" 48169689Skan 49169689Skanstatic int build_access_request(struct rad_handle *, const char *, 50169689Skan const char *, const void *, size_t); 51169689Skanstatic int do_accept(pam_handle_t *, struct rad_handle *); 52169689Skanstatic int do_challenge(pam_handle_t *, struct rad_handle *, 53169689Skan const char *); 54169689Skan 55169689Skan/* 56169689Skan * Construct an access request, but don't send it. Returns 0 on success, 57169689Skan * -1 on failure. 58169689Skan */ 59169689Skanstatic int 60169689Skanbuild_access_request(struct rad_handle *radh, const char *user, 61169689Skan const char *pass, const void *state, size_t state_len) 62169689Skan{ 63169689Skan char host[MAXHOSTNAMELEN]; 64169689Skan 65169689Skan if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 66169689Skan syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 67169689Skan return -1; 68169689Skan } 69169689Skan if ((user != NULL && 70169689Skan rad_put_string(radh, RAD_USER_NAME, user) == -1) || 71169689Skan (pass != NULL && 72169689Skan rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 73169689Skan (gethostname(host, sizeof host) != -1 && 74169689Skan rad_put_string(radh, RAD_NAS_IDENTIFIER, host) == -1)) { 75169689Skan syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 76169689Skan return -1; 77169689Skan } 78169689Skan if (state != NULL && rad_put_attr(radh, RAD_STATE, state, 79169689Skan state_len) == -1) { 80169689Skan syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 81169689Skan return -1; 82169689Skan } 83169689Skan if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 84169689Skan syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 85169689Skan return -1; 86169689Skan } 87169689Skan return 0; 88169689Skan} 89169689Skan 90169689Skanstatic int 91169689Skando_accept(pam_handle_t *pamh, struct rad_handle *radh) 92169689Skan{ 93169689Skan int attrtype; 94169689Skan const void *attrval; 95169689Skan size_t attrlen; 96169689Skan char *s; 97169689Skan 98169689Skan while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 99169689Skan if (attrtype == RAD_USER_NAME) { 100169689Skan s = rad_cvt_string(attrval, attrlen); 101169689Skan if (s == NULL) { 102169689Skan syslog(LOG_CRIT, 103169689Skan "rad_cvt_string: out of memory"); 104169689Skan return -1; 105169689Skan } 106169689Skan pam_set_item(pamh, PAM_USER, s); 107169689Skan free(s); 108169689Skan } 109169689Skan } 110169689Skan if (attrtype == -1) { 111169689Skan syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 112169689Skan return -1; 113169689Skan } 114169689Skan return 0; 115169689Skan} 116169689Skan 117169689Skanstatic int 118169689Skando_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) 119169689Skan{ 120169689Skan int retval; 121169689Skan int attrtype; 122169689Skan const void *attrval; 123169689Skan size_t attrlen; 124169689Skan const void *state; 125169689Skan size_t statelen; 126169689Skan struct pam_message msgs[MAX_CHALLENGE_MSGS]; 127169689Skan const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 128169689Skan struct pam_response *resp; 129169689Skan int num_msgs; 130169689Skan const void *item; 131169689Skan const struct pam_conv *conv; 132169689Skan 133169689Skan state = NULL; 134169689Skan statelen = 0; 135169689Skan num_msgs = 0; 136169689Skan while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 137169689Skan switch (attrtype) { 138169689Skan 139169689Skan case RAD_STATE: 140169689Skan state = attrval; 141169689Skan statelen = attrlen; 142169689Skan break; 143169689Skan 144169689Skan case RAD_REPLY_MESSAGE: 145169689Skan if (num_msgs >= MAX_CHALLENGE_MSGS) { 146169689Skan syslog(LOG_CRIT, 147169689Skan "Too many RADIUS challenge messages"); 148169689Skan return PAM_SERVICE_ERR; 149169689Skan } 150169689Skan msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 151169689Skan if (msgs[num_msgs].msg == NULL) { 152169689Skan syslog(LOG_CRIT, 153169689Skan "rad_cvt_string: out of memory"); 154169689Skan return PAM_SERVICE_ERR; 155169689Skan } 156169689Skan msgs[num_msgs].msg_style = PAM_TEXT_INFO; 157169689Skan msg_ptrs[num_msgs] = &msgs[num_msgs]; 158169689Skan num_msgs++; 159169689Skan break; 160169689Skan } 161169689Skan } 162169689Skan if (attrtype == -1) { 163169689Skan syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 164169689Skan return PAM_SERVICE_ERR; 165169689Skan } 166169689Skan if (num_msgs == 0) { 167169689Skan msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 168169689Skan if (msgs[num_msgs].msg == NULL) { 169169689Skan syslog(LOG_CRIT, "Out of memory"); 170169689Skan return PAM_SERVICE_ERR; 171169689Skan } 172169689Skan msgs[num_msgs].msg_style = PAM_TEXT_INFO; 173169689Skan msg_ptrs[num_msgs] = &msgs[num_msgs]; 174169689Skan num_msgs++; 175169689Skan } 176169689Skan msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 177169689Skan if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 178169689Skan syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 179169689Skan return retval; 180169689Skan } 181169689Skan conv = (const struct pam_conv *)item; 182169689Skan if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 183169689Skan conv->appdata_ptr)) != PAM_SUCCESS) 184169689Skan return retval; 185169689Skan if (build_access_request(radh, user, resp[num_msgs-1].resp, state, 186169689Skan statelen) == -1) 187169689Skan return PAM_SERVICE_ERR; 188169689Skan memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 189169689Skan free(resp[num_msgs-1].resp); 190169689Skan free(resp); 191169689Skan while (num_msgs > 0) 192169689Skan free((void *)msgs[--num_msgs].msg); 193169689Skan return PAM_SUCCESS; 194169689Skan} 195169689Skan 196169689SkanPAM_EXTERN int 197169689Skanpam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, 198169689Skan const char **argv) 199169689Skan{ 200169689Skan struct rad_handle *radh; 201169689Skan const char *user; 202169689Skan const char *pass; 203169689Skan const char *conf_file = NULL; 204169689Skan const char *template_user = NULL; 205169689Skan int options = 0; 206169689Skan int retval; 207169689Skan int i; 208169689Skan int e; 209169689Skan 210169689Skan for (i = 0; i < argc; i++) { 211169689Skan size_t len; 212169689Skan 213169689Skan pam_std_option(&options, argv[i]); 214169689Skan if (strncmp(argv[i], OPT_CONF, (len = strlen(OPT_CONF))) == 0) 215169689Skan conf_file = argv[i] + len; 216169689Skan else if (strncmp(argv[i], OPT_TMPL, 217169689Skan (len = strlen(OPT_TMPL))) == 0) 218169689Skan template_user = argv[i] + len; 219169689Skan } 220169689Skan if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) 221169689Skan return retval; 222169689Skan if ((retval = pam_get_pass(pamh, &pass, PASSWORD_PROMPT, 223169689Skan options)) != PAM_SUCCESS) 224169689Skan return retval; 225169689Skan 226169689Skan if ((radh = rad_open()) == NULL) { 227169689Skan syslog(LOG_CRIT, "rad_open failed"); 228169689Skan return PAM_SERVICE_ERR; 229169689Skan } 230169689Skan if (rad_config(radh, conf_file) == -1) { 231169689Skan syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 232169689Skan rad_close(radh); 233169689Skan return PAM_SERVICE_ERR; 234169689Skan } 235169689Skan if (build_access_request(radh, user, pass, NULL, 0) == -1) { 236169689Skan rad_close(radh); 237169689Skan return PAM_SERVICE_ERR; 238169689Skan } 239169689Skan for ( ; ; ) { 240169689Skan switch (rad_send_request(radh)) { 241169689Skan 242169689Skan case RAD_ACCESS_ACCEPT: 243169689Skan e = do_accept(pamh, radh); 244169689Skan rad_close(radh); 245169689Skan if (e == -1) 246169689Skan return PAM_SERVICE_ERR; 247169689Skan if (template_user != NULL) { 248169689Skan const void *item; 249169689Skan const char *user; 250169689Skan 251169689Skan /* 252169689Skan * If the given user name doesn't exist in 253169689Skan * the local password database, change it 254169689Skan * to the value given in the "template_user" 255169689Skan * option. 256169689Skan */ 257169689Skan retval = pam_get_item(pamh, PAM_USER, &item); 258169689Skan if (retval != PAM_SUCCESS) 259169689Skan return retval; 260169689Skan user = (const char *)item; 261169689Skan if (getpwnam(user) == NULL) 262169689Skan pam_set_item(pamh, PAM_USER, 263169689Skan template_user); 264169689Skan } 265169689Skan return PAM_SUCCESS; 266169689Skan 267169689Skan case RAD_ACCESS_REJECT: 268169689Skan rad_close(radh); 269169689Skan return PAM_AUTH_ERR; 270169689Skan 271169689Skan case RAD_ACCESS_CHALLENGE: 272169689Skan if ((retval = do_challenge(pamh, radh, user)) != 273169689Skan PAM_SUCCESS) { 274169689Skan rad_close(radh); 275169689Skan return retval; 276169689Skan } 277169689Skan break; 278169689Skan 279169689Skan case -1: 280169689Skan syslog(LOG_CRIT, "rad_send_request: %s", 281169689Skan rad_strerror(radh)); 282169689Skan rad_close(radh); 283169689Skan return PAM_AUTHINFO_UNAVAIL; 284169689Skan 285169689Skan default: 286169689Skan syslog(LOG_CRIT, 287169689Skan "rad_send_request: unexpected return value"); 288169689Skan rad_close(radh); 289169689Skan return PAM_SERVICE_ERR; 290169689Skan } 291169689Skan } 292169689Skan} 293169689Skan 294169689SkanPAM_EXTERN int 295169689Skanpam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 296169689Skan{ 297169689Skan return PAM_SUCCESS; 298169689Skan} 299169689Skan