pam_radius.c revision 122589
1193326Sed/*- 2193326Sed * Copyright 1998 Juniper Networks, Inc. 3193326Sed * All rights reserved. 4193326Sed * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 5193326Sed * All rights reserved. 6193326Sed * 7193326Sed * Portions of this software were developed for the FreeBSD Project by 8193326Sed * ThinkSec AS and NAI Labs, the Security Research Division of Network 9193326Sed * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10193326Sed * ("CBOSS"), as part of the DARPA CHATS research program. 11193326Sed * 12193326Sed * Redistribution and use in source and binary forms, with or without 13193326Sed * modification, are permitted provided that the following conditions 14193326Sed * are met: 15193326Sed * 1. Redistributions of source code must retain the above copyright 16198092Srdivacky * notice, this list of conditions and the following disclaimer. 17193326Sed * 2. Redistributions in binary form must reproduce the above copyright 18193326Sed * notice, this list of conditions and the following disclaimer in the 19198092Srdivacky * documentation and/or other materials provided with the distribution. 20198398Srdivacky * 3. The name of the author may not be used to endorse or promote 21193326Sed * products derived from this software without specific prior written 22198092Srdivacky * permission. 23193326Sed * 24195341Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26193326Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27193326Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28193326Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29193326Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30198092Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31198092Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32193326Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34198893Srdivacky * SUCH DAMAGE. 35198893Srdivacky */ 36198893Srdivacky 37198893Srdivacky#include <sys/cdefs.h> 38198893Srdivacky__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_radius/pam_radius.c 122589 2003-11-12 23:36:17Z sobomax $"); 39193326Sed 40198893Srdivacky#include <sys/param.h> 41198893Srdivacky#include <sys/types.h> 42198893Srdivacky#include <sys/socket.h> 43198893Srdivacky#include <netdb.h> 44198893Srdivacky#include <pwd.h> 45198893Srdivacky#include <radlib.h> 46193326Sed#include <stdlib.h> 47193326Sed#include <string.h> 48193326Sed#include <syslog.h> 49193326Sed#include <unistd.h> 50193326Sed 51198893Srdivacky#define PAM_SM_AUTH 52198893Srdivacky 53198893Srdivacky#include <security/pam_appl.h> 54198893Srdivacky#include <security/pam_modules.h> 55198893Srdivacky#include <security/pam_mod_misc.h> 56198893Srdivacky 57198893Srdivacky#define PAM_OPT_CONF "conf" 58198893Srdivacky#define PAM_OPT_TEMPLATE_USER "template_user" 59198893Srdivacky#define PAM_OPT_NAS_ID "nas_id" 60198893Srdivacky#define PAM_OPT_NAS_IPADDR "nas_ipaddr" 61198893Srdivacky 62198893Srdivacky#define MAX_CHALLENGE_MSGS 10 63198893Srdivacky#define PASSWORD_PROMPT "RADIUS Password:" 64198893Srdivacky 65198893Srdivackystatic int build_access_request(struct rad_handle *, const char *, 66198893Srdivacky const char *, const char *, const char *, const void *, 67198893Srdivacky size_t); 68198893Srdivackystatic int do_accept(pam_handle_t *, struct rad_handle *); 69198893Srdivackystatic int do_challenge(pam_handle_t *, struct rad_handle *, 70193326Sed const char *); 71193326Sed 72198893Srdivacky/* 73193326Sed * Construct an access request, but don't send it. Returns 0 on success, 74193326Sed * -1 on failure. 75198893Srdivacky */ 76193326Sedstatic int 77193326Sedbuild_access_request(struct rad_handle *radh, const char *user, 78198893Srdivacky const char *pass, const char *nas_id, const char *nas_ipaddr, 79198893Srdivacky const void *state, size_t state_len) 80198893Srdivacky{ 81198893Srdivacky int error; 82198893Srdivacky char host[MAXHOSTNAMELEN]; 83198893Srdivacky struct sockaddr_in *haddr; 84198893Srdivacky struct addrinfo hints; 85193326Sed struct addrinfo *res; 86193326Sed 87193326Sed if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 88193326Sed syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 89193326Sed return (-1); 90193326Sed } 91193326Sed if (nas_id == NULL || 92193326Sed (nas_ipaddr != NULL && strlen(nas_ipaddr) == 0)) { 93193326Sed if (gethostname(host, sizeof host) != -1) { 94193326Sed if (nas_id == NULL) 95193326Sed nas_id = host; 96193326Sed if (nas_ipaddr != NULL && strlen(nas_ipaddr) == 0) 97193326Sed nas_ipaddr = host; 98193326Sed } 99193326Sed } 100193326Sed if ((user != NULL && 101193326Sed rad_put_string(radh, RAD_USER_NAME, user) == -1) || 102193326Sed (pass != NULL && 103193326Sed rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 104193326Sed (nas_id != NULL && 105198893Srdivacky rad_put_string(radh, RAD_NAS_IDENTIFIER, nas_id) == -1)) { 106193326Sed syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 107193326Sed return (-1); 108193326Sed } 109193326Sed if (nas_ipaddr != NULL) { 110193326Sed memset(&hints, 0, sizeof(hints)); 111198893Srdivacky hints.ai_family = PF_INET; 112193326Sed if (getaddrinfo(nas_ipaddr, NULL, &hints, &res) == 0 && 113193326Sed res != NULL) { 114193326Sed (struct sockaddr *)haddr = res->ai_addr; 115193326Sed error = rad_put_addr(radh, RAD_NAS_IP_ADDRESS, 116198092Srdivacky haddr->sin_addr); 117198092Srdivacky freeaddrinfo(res); 118198092Srdivacky if (error == -1) { 119198092Srdivacky syslog(LOG_CRIT, "rad_put_addr: %s", 120198092Srdivacky rad_strerror(radh)); 121198092Srdivacky return (-1); 122198092Srdivacky } 123198092Srdivacky } 124198092Srdivacky } 125198092Srdivacky if (state != NULL && rad_put_attr(radh, RAD_STATE, state, 126193326Sed state_len) == -1) { 127193326Sed syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 128193326Sed return (-1); 129198092Srdivacky } 130198092Srdivacky if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 131195341Sed syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 132193326Sed return (-1); 133193326Sed } 134198893Srdivacky return (0); 135198893Srdivacky} 136198893Srdivacky 137198893Srdivackystatic int 138198893Srdivackydo_accept(pam_handle_t *pamh, struct rad_handle *radh) 139198893Srdivacky{ 140198893Srdivacky int attrtype; 141198092Srdivacky const void *attrval; 142193326Sed size_t attrlen; 143193326Sed char *s; 144193326Sed 145193326Sed while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 146193326Sed if (attrtype == RAD_USER_NAME) { 147193326Sed s = rad_cvt_string(attrval, attrlen); 148193326Sed if (s == NULL) { 149198893Srdivacky syslog(LOG_CRIT, 150193326Sed "rad_cvt_string: out of memory"); 151193326Sed return (-1); 152193326Sed } 153198893Srdivacky pam_set_item(pamh, PAM_USER, s); 154193326Sed free(s); 155193326Sed } 156193326Sed } 157193326Sed if (attrtype == -1) { 158193326Sed syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 159193326Sed return (-1); 160193326Sed } 161193326Sed return (0); 162193326Sed} 163193326Sed 164198893Srdivackystatic int 165198893Srdivackydo_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) 166198893Srdivacky{ 167193326Sed int retval; 168198092Srdivacky int attrtype; 169195099Sed const void *attrval; 170195099Sed size_t attrlen; 171195099Sed const void *state; 172198893Srdivacky size_t statelen; 173195099Sed struct pam_message msgs[MAX_CHALLENGE_MSGS]; 174198893Srdivacky const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 175193326Sed struct pam_response *resp; 176195099Sed int num_msgs; 177193326Sed const void *item; 178198092Srdivacky const struct pam_conv *conv; 179198092Srdivacky 180193326Sed state = NULL; 181193326Sed statelen = 0; 182193326Sed num_msgs = 0; 183193326Sed while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 184193326Sed switch (attrtype) { 185193326Sed 186198893Srdivacky case RAD_STATE: 187198893Srdivacky state = attrval; 188198893Srdivacky statelen = attrlen; 189198893Srdivacky break; 190198893Srdivacky 191198893Srdivacky case RAD_REPLY_MESSAGE: 192198893Srdivacky if (num_msgs >= MAX_CHALLENGE_MSGS) { 193198893Srdivacky syslog(LOG_CRIT, 194193326Sed "Too many RADIUS challenge messages"); 195193326Sed return (PAM_SERVICE_ERR); 196193326Sed } 197193326Sed msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 198193326Sed if (msgs[num_msgs].msg == NULL) { 199193326Sed syslog(LOG_CRIT, 200198893Srdivacky "rad_cvt_string: out of memory"); 201198893Srdivacky return (PAM_SERVICE_ERR); 202198893Srdivacky } 203198893Srdivacky msgs[num_msgs].msg_style = PAM_TEXT_INFO; 204198893Srdivacky msg_ptrs[num_msgs] = &msgs[num_msgs]; 205198893Srdivacky num_msgs++; 206198893Srdivacky break; 207198893Srdivacky } 208193326Sed } 209193326Sed if (attrtype == -1) { 210193326Sed syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 211193326Sed return (PAM_SERVICE_ERR); 212193326Sed } 213193326Sed if (num_msgs == 0) { 214193326Sed msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 215193326Sed if (msgs[num_msgs].msg == NULL) { 216193326Sed syslog(LOG_CRIT, "Out of memory"); 217193326Sed return (PAM_SERVICE_ERR); 218193326Sed } 219193326Sed msgs[num_msgs].msg_style = PAM_TEXT_INFO; 220193326Sed msg_ptrs[num_msgs] = &msgs[num_msgs]; 221193326Sed num_msgs++; 222193326Sed } 223198893Srdivacky msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 224193326Sed if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 225198893Srdivacky syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 226193326Sed return (retval); 227193326Sed } 228193326Sed conv = (const struct pam_conv *)item; 229193326Sed if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 230193326Sed conv->appdata_ptr)) != PAM_SUCCESS) 231198893Srdivacky return (retval); 232198092Srdivacky if (build_access_request(radh, user, resp[num_msgs-1].resp, NULL, 233198092Srdivacky NULL, state, statelen) == -1) 234198092Srdivacky return (PAM_SERVICE_ERR); 235198893Srdivacky memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 236198092Srdivacky free(resp[num_msgs-1].resp); 237198092Srdivacky free(resp); 238198092Srdivacky while (num_msgs > 0) 239198893Srdivacky free(msgs[--num_msgs].msg); 240198893Srdivacky return (PAM_SUCCESS); 241198893Srdivacky} 242193326Sed 243198893SrdivackyPAM_EXTERN int 244198893Srdivackypam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 245193326Sed int argc __unused, const char *argv[] __unused) 246198893Srdivacky{ 247198092Srdivacky struct rad_handle *radh; 248198092Srdivacky const char *user, *tmpuser, *pass; 249198893Srdivacky const char *conf_file, *template_user, *nas_id, *nas_ipaddr; 250198092Srdivacky int retval; 251198092Srdivacky int e; 252198092Srdivacky 253198092Srdivacky conf_file = openpam_get_option(pamh, PAM_OPT_CONF); 254198092Srdivacky template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); 255193326Sed nas_id = openpam_get_option(pamh, PAM_OPT_NAS_ID); 256198893Srdivacky nas_ipaddr = openpam_get_option(pamh, PAM_OPT_NAS_IPADDR); 257193326Sed 258198092Srdivacky retval = pam_get_user(pamh, &user, NULL); 259193326Sed if (retval != PAM_SUCCESS) 260193326Sed return (retval); 261193326Sed 262193326Sed PAM_LOG("Got user: %s", user); 263198893Srdivacky 264193326Sed retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT); 265193326Sed if (retval != PAM_SUCCESS) 266198092Srdivacky return (retval); 267198092Srdivacky 268198092Srdivacky PAM_LOG("Got password"); 269198092Srdivacky 270198092Srdivacky radh = rad_open(); 271198092Srdivacky if (radh == NULL) { 272198092Srdivacky syslog(LOG_CRIT, "rad_open failed"); 273198092Srdivacky return (PAM_SERVICE_ERR); 274198092Srdivacky } 275198092Srdivacky 276198092Srdivacky PAM_LOG("Radius opened"); 277198092Srdivacky 278198092Srdivacky if (rad_config(radh, conf_file) == -1) { 279198092Srdivacky syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 280198092Srdivacky rad_close(radh); 281193326Sed return (PAM_SERVICE_ERR); 282198092Srdivacky } 283198092Srdivacky 284198092Srdivacky PAM_LOG("Radius config file read"); 285193326Sed 286198092Srdivacky if (build_access_request(radh, user, pass, nas_id, nas_ipaddr, NULL, 287198092Srdivacky 0) == -1) { 288193326Sed rad_close(radh); 289198893Srdivacky return (PAM_SERVICE_ERR); 290193326Sed } 291198893Srdivacky 292193326Sed PAM_LOG("Radius build access done"); 293193326Sed 294198092Srdivacky for (;;) { 295193326Sed switch (rad_send_request(radh)) { 296193326Sed 297193326Sed case RAD_ACCESS_ACCEPT: 298193326Sed e = do_accept(pamh, radh); 299198092Srdivacky rad_close(radh); 300198893Srdivacky if (e == -1) 301193326Sed return (PAM_SERVICE_ERR); 302193326Sed if (template_user != NULL) { 303193326Sed 304193326Sed PAM_LOG("Trying template user: %s", 305193326Sed template_user); 306193326Sed 307193326Sed /* 308193326Sed * If the given user name doesn't exist in 309193326Sed * the local password database, change it 310193326Sed * to the value given in the "template_user" 311193326Sed * option. 312195099Sed */ 313195099Sed retval = pam_get_item(pamh, PAM_USER, 314195099Sed (const void **)&tmpuser); 315195099Sed if (retval != PAM_SUCCESS) 316198893Srdivacky return (retval); 317195341Sed if (getpwnam(tmpuser) == NULL) { 318195341Sed pam_set_item(pamh, PAM_USER, 319198893Srdivacky template_user); 320195341Sed PAM_LOG("Using template user"); 321195099Sed } 322195099Sed 323195099Sed } 324195099Sed return (PAM_SUCCESS); 325195099Sed 326195099Sed case RAD_ACCESS_REJECT: 327195099Sed rad_close(radh); 328198092Srdivacky PAM_VERBOSE_ERROR("Radius rejection"); 329193326Sed return (PAM_AUTH_ERR); 330193326Sed 331198893Srdivacky case RAD_ACCESS_CHALLENGE: 332193326Sed retval = do_challenge(pamh, radh, user); 333193326Sed if (retval != PAM_SUCCESS) { 334198092Srdivacky rad_close(radh); 335193326Sed return (retval); 336193326Sed } 337198893Srdivacky break; 338198893Srdivacky 339193326Sed case -1: 340193326Sed syslog(LOG_CRIT, "rad_send_request: %s", 341198092Srdivacky rad_strerror(radh)); 342193326Sed rad_close(radh); 343193326Sed PAM_VERBOSE_ERROR("Radius failure"); 344198092Srdivacky return (PAM_AUTHINFO_UNAVAIL); 345193326Sed 346193326Sed default: 347193326Sed syslog(LOG_CRIT, 348198893Srdivacky "rad_send_request: unexpected return value"); 349198092Srdivacky rad_close(radh); 350193326Sed PAM_VERBOSE_ERROR("Radius error"); 351193326Sed return (PAM_SERVICE_ERR); 352193326Sed } 353193326Sed } 354193326Sed} 355193326Sed 356198092SrdivackyPAM_EXTERN int 357193326Sedpam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 358198092Srdivacky int argc __unused, const char *argv[] __unused) 359198092Srdivacky{ 360198092Srdivacky 361198092Srdivacky return (PAM_SUCCESS); 362193326Sed} 363193326Sed 364193326SedPAM_MODULE_ENTRY("pam_radius"); 365198893Srdivacky