141227Sjdp/*- 241227Sjdp * Copyright 1998 Juniper Networks, Inc. 341227Sjdp * All rights reserved. 4115470Sdes * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 587398Sdes * All rights reserved. 641227Sjdp * 787398Sdes * Portions of this software were developed for the FreeBSD Project by 887398Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 987398Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1087398Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1187398Sdes * 1241227Sjdp * Redistribution and use in source and binary forms, with or without 1341227Sjdp * modification, are permitted provided that the following conditions 1441227Sjdp * are met: 1541227Sjdp * 1. Redistributions of source code must retain the above copyright 1641227Sjdp * notice, this list of conditions and the following disclaimer. 1741227Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1841227Sjdp * notice, this list of conditions and the following disclaimer in the 1941227Sjdp * documentation and/or other materials provided with the distribution. 2087398Sdes * 3. The name of the author may not be used to endorse or promote 2187398Sdes * products derived from this software without specific prior written 2287398Sdes * permission. 2341227Sjdp * 2441227Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2541227Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2641227Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2741227Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2841227Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2941227Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3041227Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3141227Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3241227Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3341227Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3441227Sjdp * SUCH DAMAGE. 3541227Sjdp */ 3641227Sjdp 3784218Sdillon#include <sys/cdefs.h> 3884218Sdillon__FBSDID("$FreeBSD: releng/11.0/lib/libpam/modules/pam_radius/pam_radius.c 277237 2015-01-16 09:07:31Z des $"); 3984218Sdillon 4041227Sjdp#include <sys/param.h> 41122571Ssobomax#include <sys/socket.h> 42122571Ssobomax#include <netdb.h> 4341227Sjdp#include <pwd.h> 4441227Sjdp#include <radlib.h> 4541227Sjdp#include <stdlib.h> 4641227Sjdp#include <string.h> 4741227Sjdp#include <syslog.h> 4841227Sjdp#include <unistd.h> 4941227Sjdp 5041227Sjdp#define PAM_SM_AUTH 5187398Sdes 5290229Sdes#include <security/pam_appl.h> 5341227Sjdp#include <security/pam_modules.h> 5490229Sdes#include <security/pam_mod_misc.h> 5541227Sjdp 56115465Sdes#define PAM_OPT_CONF "conf" 57115465Sdes#define PAM_OPT_TEMPLATE_USER "template_user" 58115465Sdes#define PAM_OPT_NAS_ID "nas_id" 59122571Ssobomax#define PAM_OPT_NAS_IPADDR "nas_ipaddr" 6041227Sjdp 6179476Smarkm#define MAX_CHALLENGE_MSGS 10 6293984Sdes#define PASSWORD_PROMPT "RADIUS Password:" 6379476Smarkm 6441227Sjdpstatic int build_access_request(struct rad_handle *, const char *, 65277237Sdes const char *, const char *, const char *, const char *, 66277237Sdes const void *, size_t); 6741227Sjdpstatic int do_accept(pam_handle_t *, struct rad_handle *); 6841227Sjdpstatic int do_challenge(pam_handle_t *, struct rad_handle *, 69277237Sdes const char *, const char *, const char *, const char *); 7041227Sjdp 7141227Sjdp/* 7241227Sjdp * Construct an access request, but don't send it. Returns 0 on success, 7341227Sjdp * -1 on failure. 7441227Sjdp */ 7541227Sjdpstatic int 7641227Sjdpbuild_access_request(struct rad_handle *radh, const char *user, 77122571Ssobomax const char *pass, const char *nas_id, const char *nas_ipaddr, 78277237Sdes const char *rhost, const void *state, size_t state_len) 7941227Sjdp{ 80122571Ssobomax int error; 81122571Ssobomax char host[MAXHOSTNAMELEN]; 82122571Ssobomax struct sockaddr_in *haddr; 83122571Ssobomax struct addrinfo hints; 84122571Ssobomax struct addrinfo *res; 8541227Sjdp 8641227Sjdp if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 8741227Sjdp syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 8894564Sdes return (-1); 8941227Sjdp } 90122571Ssobomax if (nas_id == NULL || 91122571Ssobomax (nas_ipaddr != NULL && strlen(nas_ipaddr) == 0)) { 92122571Ssobomax if (gethostname(host, sizeof host) != -1) { 93122571Ssobomax if (nas_id == NULL) 94122571Ssobomax nas_id = host; 95122571Ssobomax if (nas_ipaddr != NULL && strlen(nas_ipaddr) == 0) 96122571Ssobomax nas_ipaddr = host; 97122571Ssobomax } 98122571Ssobomax } 9941227Sjdp if ((user != NULL && 10041227Sjdp rad_put_string(radh, RAD_USER_NAME, user) == -1) || 10141227Sjdp (pass != NULL && 10241227Sjdp rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 103106082Sdes (nas_id != NULL && 104106082Sdes rad_put_string(radh, RAD_NAS_IDENTIFIER, nas_id) == -1)) { 10541227Sjdp syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 10694564Sdes return (-1); 10741227Sjdp } 108122571Ssobomax if (nas_ipaddr != NULL) { 109122571Ssobomax memset(&hints, 0, sizeof(hints)); 110147350Sdes hints.ai_family = AF_INET; 111122571Ssobomax if (getaddrinfo(nas_ipaddr, NULL, &hints, &res) == 0 && 112147350Sdes res != NULL && res->ai_family == AF_INET) { 113147350Sdes haddr = (struct sockaddr_in *)res->ai_addr; 114122571Ssobomax error = rad_put_addr(radh, RAD_NAS_IP_ADDRESS, 115122571Ssobomax haddr->sin_addr); 116122571Ssobomax freeaddrinfo(res); 117122571Ssobomax if (error == -1) { 118122571Ssobomax syslog(LOG_CRIT, "rad_put_addr: %s", 119122571Ssobomax rad_strerror(radh)); 120122571Ssobomax return (-1); 121122571Ssobomax } 122122571Ssobomax } 123122571Ssobomax } 124277237Sdes if (rhost != NULL && 125277237Sdes rad_put_string(radh, RAD_CALLING_STATION_ID, rhost) == -1) { 126277237Sdes syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 127277237Sdes return (-1); 128277237Sdes } 129277237Sdes if (state != NULL && 130277237Sdes rad_put_attr(radh, RAD_STATE, state, state_len) == -1) { 13141227Sjdp syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 13294564Sdes return (-1); 13341227Sjdp } 13441227Sjdp if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 13541227Sjdp syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 13694564Sdes return (-1); 13741227Sjdp } 13894564Sdes return (0); 13941227Sjdp} 14041227Sjdp 14141227Sjdpstatic int 14241227Sjdpdo_accept(pam_handle_t *pamh, struct rad_handle *radh) 14341227Sjdp{ 14441227Sjdp int attrtype; 14541227Sjdp const void *attrval; 14641227Sjdp size_t attrlen; 14741227Sjdp char *s; 14841227Sjdp 14941227Sjdp while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 15041227Sjdp if (attrtype == RAD_USER_NAME) { 15141227Sjdp s = rad_cvt_string(attrval, attrlen); 15241227Sjdp if (s == NULL) { 15341227Sjdp syslog(LOG_CRIT, 15441227Sjdp "rad_cvt_string: out of memory"); 15594564Sdes return (-1); 15641227Sjdp } 15741227Sjdp pam_set_item(pamh, PAM_USER, s); 15841227Sjdp free(s); 15941227Sjdp } 16041227Sjdp } 16141227Sjdp if (attrtype == -1) { 16241227Sjdp syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 16394564Sdes return (-1); 16441227Sjdp } 16594564Sdes return (0); 16641227Sjdp} 16741227Sjdp 16841227Sjdpstatic int 169166136Spjddo_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user, 170277237Sdes const char *nas_id, const char *nas_ipaddr, const char *rhost) 17141227Sjdp{ 17241227Sjdp int retval; 17341227Sjdp int attrtype; 17441227Sjdp const void *attrval; 17541227Sjdp size_t attrlen; 17641227Sjdp const void *state; 17741227Sjdp size_t statelen; 17841227Sjdp struct pam_message msgs[MAX_CHALLENGE_MSGS]; 17941227Sjdp const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 18041227Sjdp struct pam_response *resp; 18141227Sjdp int num_msgs; 18241227Sjdp const void *item; 18341227Sjdp const struct pam_conv *conv; 18441227Sjdp 18541227Sjdp state = NULL; 18641227Sjdp statelen = 0; 18741227Sjdp num_msgs = 0; 18841227Sjdp while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 18941227Sjdp switch (attrtype) { 19041227Sjdp 19141227Sjdp case RAD_STATE: 19241227Sjdp state = attrval; 19341227Sjdp statelen = attrlen; 19441227Sjdp break; 19541227Sjdp 19641227Sjdp case RAD_REPLY_MESSAGE: 19741227Sjdp if (num_msgs >= MAX_CHALLENGE_MSGS) { 19841227Sjdp syslog(LOG_CRIT, 19941227Sjdp "Too many RADIUS challenge messages"); 20094564Sdes return (PAM_SERVICE_ERR); 20141227Sjdp } 20241227Sjdp msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 20341227Sjdp if (msgs[num_msgs].msg == NULL) { 20441227Sjdp syslog(LOG_CRIT, 20541227Sjdp "rad_cvt_string: out of memory"); 20694564Sdes return (PAM_SERVICE_ERR); 20741227Sjdp } 20841227Sjdp msgs[num_msgs].msg_style = PAM_TEXT_INFO; 20941227Sjdp msg_ptrs[num_msgs] = &msgs[num_msgs]; 21041227Sjdp num_msgs++; 21141227Sjdp break; 21241227Sjdp } 21341227Sjdp } 21441227Sjdp if (attrtype == -1) { 21541227Sjdp syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 21694564Sdes return (PAM_SERVICE_ERR); 21741227Sjdp } 21841227Sjdp if (num_msgs == 0) { 21941227Sjdp msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 22041227Sjdp if (msgs[num_msgs].msg == NULL) { 22141227Sjdp syslog(LOG_CRIT, "Out of memory"); 22294564Sdes return (PAM_SERVICE_ERR); 22341227Sjdp } 22441227Sjdp msgs[num_msgs].msg_style = PAM_TEXT_INFO; 22541227Sjdp msg_ptrs[num_msgs] = &msgs[num_msgs]; 22641227Sjdp num_msgs++; 22741227Sjdp } 22841227Sjdp msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 22941227Sjdp if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 23041227Sjdp syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 23194564Sdes return (retval); 23241227Sjdp } 23341227Sjdp conv = (const struct pam_conv *)item; 23441227Sjdp if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 23541227Sjdp conv->appdata_ptr)) != PAM_SUCCESS) 23694564Sdes return (retval); 237166136Spjd if (build_access_request(radh, user, resp[num_msgs-1].resp, nas_id, 238277237Sdes nas_ipaddr, rhost, state, statelen) == -1) 23994564Sdes return (PAM_SERVICE_ERR); 24041227Sjdp memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 24141227Sjdp free(resp[num_msgs-1].resp); 24241227Sjdp free(resp); 24341227Sjdp while (num_msgs > 0) 244117841Sdes free(msgs[--num_msgs].msg); 24594564Sdes return (PAM_SUCCESS); 24641227Sjdp} 24741227Sjdp 24841227SjdpPAM_EXTERN int 24994564Sdespam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 250115465Sdes int argc __unused, const char *argv[] __unused) 25141227Sjdp{ 25241227Sjdp struct rad_handle *radh; 253123448Sdes const char *user, *pass; 254277237Sdes const void *rhost, *tmpuser; 255122571Ssobomax const char *conf_file, *template_user, *nas_id, *nas_ipaddr; 25641227Sjdp int retval; 25741227Sjdp int e; 25841227Sjdp 259115465Sdes conf_file = openpam_get_option(pamh, PAM_OPT_CONF); 260115465Sdes template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); 261115465Sdes nas_id = openpam_get_option(pamh, PAM_OPT_NAS_ID); 262122571Ssobomax nas_ipaddr = openpam_get_option(pamh, PAM_OPT_NAS_IPADDR); 263277237Sdes pam_get_item(pamh, PAM_RHOST, &rhost); 26441227Sjdp 26579476Smarkm retval = pam_get_user(pamh, &user, NULL); 26679476Smarkm if (retval != PAM_SUCCESS) 26794564Sdes return (retval); 26879476Smarkm 26979476Smarkm PAM_LOG("Got user: %s", user); 27079476Smarkm 27193984Sdes retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT); 27279476Smarkm if (retval != PAM_SUCCESS) 27394564Sdes return (retval); 27479476Smarkm 27579476Smarkm PAM_LOG("Got password"); 27679476Smarkm 27779476Smarkm radh = rad_open(); 27879476Smarkm if (radh == NULL) { 27941227Sjdp syslog(LOG_CRIT, "rad_open failed"); 28094564Sdes return (PAM_SERVICE_ERR); 28141227Sjdp } 28279476Smarkm 28379476Smarkm PAM_LOG("Radius opened"); 28479476Smarkm 28541227Sjdp if (rad_config(radh, conf_file) == -1) { 28641227Sjdp syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 28741227Sjdp rad_close(radh); 28894564Sdes return (PAM_SERVICE_ERR); 28941227Sjdp } 29079476Smarkm 29179476Smarkm PAM_LOG("Radius config file read"); 29279476Smarkm 293277237Sdes if (build_access_request(radh, user, pass, nas_id, nas_ipaddr, rhost, 294277237Sdes NULL, 0) == -1) { 29541227Sjdp rad_close(radh); 29694564Sdes return (PAM_SERVICE_ERR); 29741227Sjdp } 29879476Smarkm 29979476Smarkm PAM_LOG("Radius build access done"); 30079476Smarkm 30187398Sdes for (;;) { 30241227Sjdp switch (rad_send_request(radh)) { 30341227Sjdp 30441227Sjdp case RAD_ACCESS_ACCEPT: 30541227Sjdp e = do_accept(pamh, radh); 30641227Sjdp rad_close(radh); 30741227Sjdp if (e == -1) 30894564Sdes return (PAM_SERVICE_ERR); 30941227Sjdp if (template_user != NULL) { 31041227Sjdp 31179476Smarkm PAM_LOG("Trying template user: %s", 31279476Smarkm template_user); 31379476Smarkm 31441227Sjdp /* 31541227Sjdp * If the given user name doesn't exist in 31641227Sjdp * the local password database, change it 31741227Sjdp * to the value given in the "template_user" 31841227Sjdp * option. 31941227Sjdp */ 320123448Sdes retval = pam_get_item(pamh, PAM_USER, &tmpuser); 32141227Sjdp if (retval != PAM_SUCCESS) 32294564Sdes return (retval); 32379476Smarkm if (getpwnam(tmpuser) == NULL) { 32441227Sjdp pam_set_item(pamh, PAM_USER, 32541227Sjdp template_user); 32679476Smarkm PAM_LOG("Using template user"); 32779476Smarkm } 32879476Smarkm 32941227Sjdp } 33094564Sdes return (PAM_SUCCESS); 33141227Sjdp 33241227Sjdp case RAD_ACCESS_REJECT: 33341227Sjdp rad_close(radh); 33481473Smarkm PAM_VERBOSE_ERROR("Radius rejection"); 33594564Sdes return (PAM_AUTH_ERR); 33641227Sjdp 33741227Sjdp case RAD_ACCESS_CHALLENGE: 338166136Spjd retval = do_challenge(pamh, radh, user, nas_id, 339277237Sdes nas_ipaddr, rhost); 34079476Smarkm if (retval != PAM_SUCCESS) { 34141227Sjdp rad_close(radh); 34294564Sdes return (retval); 34341227Sjdp } 34441227Sjdp break; 34541227Sjdp 34641227Sjdp case -1: 34741227Sjdp syslog(LOG_CRIT, "rad_send_request: %s", 34841227Sjdp rad_strerror(radh)); 34941227Sjdp rad_close(radh); 35081473Smarkm PAM_VERBOSE_ERROR("Radius failure"); 35194564Sdes return (PAM_AUTHINFO_UNAVAIL); 35241227Sjdp 35341227Sjdp default: 35441227Sjdp syslog(LOG_CRIT, 35541227Sjdp "rad_send_request: unexpected return value"); 35641227Sjdp rad_close(radh); 35781473Smarkm PAM_VERBOSE_ERROR("Radius error"); 35894564Sdes return (PAM_SERVICE_ERR); 35941227Sjdp } 36041227Sjdp } 36141227Sjdp} 36241227Sjdp 36341227SjdpPAM_EXTERN int 36494564Sdespam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 36594564Sdes int argc __unused, const char *argv[] __unused) 36641227Sjdp{ 36781473Smarkm 36894564Sdes return (PAM_SUCCESS); 36941227Sjdp} 37042917Sjdp 37142917SjdpPAM_MODULE_ENTRY("pam_radius"); 372