pam_radius.c revision 122571
169408Sache/*- 259243Sobrien * Copyright 1998 Juniper Networks, Inc. 359243Sobrien * All rights reserved. 459243Sobrien * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 559243Sobrien * All rights reserved. 659243Sobrien * 759243Sobrien * Portions of this software were developed for the FreeBSD Project by 859243Sobrien * ThinkSec AS and NAI Labs, the Security Research Division of Network 959243Sobrien * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1059243Sobrien * ("CBOSS"), as part of the DARPA CHATS research program. 1159243Sobrien * 1259243Sobrien * Redistribution and use in source and binary forms, with or without 1359243Sobrien * modification, are permitted provided that the following conditions 1459243Sobrien * are met: 1559243Sobrien * 1. Redistributions of source code must retain the above copyright 1659243Sobrien * notice, this list of conditions and the following disclaimer. 1759243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1859243Sobrien * notice, this list of conditions and the following disclaimer in the 1959243Sobrien * documentation and/or other materials provided with the distribution. 2059243Sobrien * 3. The name of the author may not be used to endorse or promote 2159243Sobrien * products derived from this software without specific prior written 2259243Sobrien * permission. 2359243Sobrien * 2459243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2559243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2659243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2759243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2859243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2959243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3059243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3459243Sobrien * SUCH DAMAGE. 3559243Sobrien */ 3659243Sobrien 3759243Sobrien#include <sys/cdefs.h> 3859243Sobrien__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_radius/pam_radius.c 122571 2003-11-12 17:47:23Z sobomax $"); 3959243Sobrien 4059243Sobrien#include <sys/param.h> 4159243Sobrien#include <sys/types.h> 4259243Sobrien#include <sys/socket.h> 4359243Sobrien#include <netdb.h> 4459243Sobrien#include <pwd.h> 4559243Sobrien#include <radlib.h> 4659243Sobrien#include <stdlib.h> 4759243Sobrien#include <string.h> 4859243Sobrien#include <syslog.h> 4959243Sobrien#include <unistd.h> 5059243Sobrien 5159243Sobrien#define PAM_SM_AUTH 5259243Sobrien 5359243Sobrien#include <security/pam_appl.h> 5459243Sobrien#include <security/pam_modules.h> 5559243Sobrien#include <security/pam_mod_misc.h> 5659243Sobrien 5759243Sobrien#define PAM_OPT_CONF "conf" 5859243Sobrien#define PAM_OPT_TEMPLATE_USER "template_user" 5959243Sobrien#define PAM_OPT_NAS_ID "nas_id" 6059243Sobrien#define PAM_OPT_NAS_IPADDR "nas_ipaddr" 6159243Sobrien 6259243Sobrien#define MAX_CHALLENGE_MSGS 10 6359243Sobrien#define PASSWORD_PROMPT "RADIUS Password:" 6459243Sobrien 6559243Sobrienstatic int build_access_request(struct rad_handle *, const char *, 6659243Sobrien const char *, const char *, const char *, const void *, 6759243Sobrien size_t); 6859243Sobrienstatic int do_accept(pam_handle_t *, struct rad_handle *); 6959243Sobrienstatic int do_challenge(pam_handle_t *, struct rad_handle *, 7059243Sobrien const char *); 7159243Sobrien 7259243Sobrien/* 7359243Sobrien * Construct an access request, but don't send it. Returns 0 on success, 7459243Sobrien * -1 on failure. 7559243Sobrien */ 7659243Sobrienstatic int 7759243Sobrienbuild_access_request(struct rad_handle *radh, const char *user, 7859243Sobrien const char *pass, const char *nas_id, const char *nas_ipaddr, 7959243Sobrien const void *state, size_t state_len) 8059243Sobrien{ 8159243Sobrien int error; 8259243Sobrien char host[MAXHOSTNAMELEN]; 8359243Sobrien struct sockaddr_in *haddr; 8459243Sobrien struct addrinfo hints; 8559243Sobrien struct addrinfo *res; 8659243Sobrien 8759243Sobrien if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { 8859243Sobrien syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); 8959243Sobrien return (-1); 9059243Sobrien } 9159243Sobrien if (nas_id == NULL || 9259243Sobrien (nas_ipaddr != NULL && strlen(nas_ipaddr) == 0)) { 9359243Sobrien if (gethostname(host, sizeof host) != -1) { 9459243Sobrien if (nas_id == NULL) 9569408Sache nas_id = host; 9659243Sobrien if (nas_ipaddr != NULL && strlen(nas_ipaddr) == 0) 9759243Sobrien nas_ipaddr = host; 9859243Sobrien } 9959243Sobrien } 10059243Sobrien if ((user != NULL && 10159243Sobrien rad_put_string(radh, RAD_USER_NAME, user) == -1) || 10259243Sobrien (pass != NULL && 10359243Sobrien rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || 10459243Sobrien (nas_id != NULL && 10559243Sobrien rad_put_string(radh, RAD_NAS_IDENTIFIER, nas_id) == -1)) { 10659243Sobrien syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); 10759243Sobrien return (-1); 10859243Sobrien } 10959243Sobrien if (nas_ipaddr != NULL) { 11059243Sobrien memset(&hints, 0, sizeof(hints)); 11159243Sobrien hints.ai_family = PF_INET; 11259243Sobrien if (getaddrinfo(nas_ipaddr, NULL, &hints, &res) == 0 && 11359243Sobrien res != NULL) { 11459243Sobrien haddr = (struct sockaddr_in *)res->ai_addr; 11559243Sobrien error = rad_put_addr(radh, RAD_NAS_IP_ADDRESS, 11659243Sobrien haddr->sin_addr); 11759243Sobrien freeaddrinfo(res); 11859243Sobrien if (error == -1) { 11959243Sobrien syslog(LOG_CRIT, "rad_put_addr: %s", 12059243Sobrien rad_strerror(radh)); 12159243Sobrien return (-1); 12259243Sobrien } 12359243Sobrien } 12459243Sobrien } 12559243Sobrien if (state != NULL && rad_put_attr(radh, RAD_STATE, state, 12659243Sobrien state_len) == -1) { 12759243Sobrien syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); 12859243Sobrien return (-1); 12959243Sobrien } 13059243Sobrien if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { 13159243Sobrien syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); 13259243Sobrien return (-1); 13359243Sobrien } 13459243Sobrien return (0); 13559243Sobrien} 13659243Sobrien 13759243Sobrienstatic int 13859243Sobriendo_accept(pam_handle_t *pamh, struct rad_handle *radh) 13959243Sobrien{ 14059243Sobrien int attrtype; 14159243Sobrien const void *attrval; 14259243Sobrien size_t attrlen; 14359243Sobrien char *s; 14459243Sobrien 14559243Sobrien while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 14659243Sobrien if (attrtype == RAD_USER_NAME) { 14759243Sobrien s = rad_cvt_string(attrval, attrlen); 14859243Sobrien if (s == NULL) { 14959243Sobrien syslog(LOG_CRIT, 15059243Sobrien "rad_cvt_string: out of memory"); 15159243Sobrien return (-1); 15259243Sobrien } 15359243Sobrien pam_set_item(pamh, PAM_USER, s); 15459243Sobrien free(s); 15559243Sobrien } 15659243Sobrien } 15759243Sobrien if (attrtype == -1) { 15859243Sobrien syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 15959243Sobrien return (-1); 16059243Sobrien } 16159243Sobrien return (0); 16259243Sobrien} 16359243Sobrien 16459243Sobrienstatic int 16559243Sobriendo_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) 16659243Sobrien{ 16759243Sobrien int retval; 16859243Sobrien int attrtype; 16959243Sobrien const void *attrval; 17059243Sobrien size_t attrlen; 17159243Sobrien const void *state; 17259243Sobrien size_t statelen; 17359243Sobrien struct pam_message msgs[MAX_CHALLENGE_MSGS]; 17459243Sobrien const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; 17559243Sobrien struct pam_response *resp; 17659243Sobrien int num_msgs; 17759243Sobrien const void *item; 17859243Sobrien const struct pam_conv *conv; 17959243Sobrien 18059243Sobrien state = NULL; 18159243Sobrien statelen = 0; 18259243Sobrien num_msgs = 0; 18359243Sobrien while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { 18459243Sobrien switch (attrtype) { 18559243Sobrien 18659243Sobrien case RAD_STATE: 18759243Sobrien state = attrval; 18859243Sobrien statelen = attrlen; 18959243Sobrien break; 19059243Sobrien 19159243Sobrien case RAD_REPLY_MESSAGE: 19259243Sobrien if (num_msgs >= MAX_CHALLENGE_MSGS) { 19359243Sobrien syslog(LOG_CRIT, 19459243Sobrien "Too many RADIUS challenge messages"); 19559243Sobrien return (PAM_SERVICE_ERR); 19659243Sobrien } 19759243Sobrien msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); 19859243Sobrien if (msgs[num_msgs].msg == NULL) { 19959243Sobrien syslog(LOG_CRIT, 20059243Sobrien "rad_cvt_string: out of memory"); 20159243Sobrien return (PAM_SERVICE_ERR); 20259243Sobrien } 20359243Sobrien msgs[num_msgs].msg_style = PAM_TEXT_INFO; 20459243Sobrien msg_ptrs[num_msgs] = &msgs[num_msgs]; 20559243Sobrien num_msgs++; 20659243Sobrien break; 20759243Sobrien } 20859243Sobrien } 20959243Sobrien if (attrtype == -1) { 21059243Sobrien syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); 21159243Sobrien return (PAM_SERVICE_ERR); 21259243Sobrien } 21359243Sobrien if (num_msgs == 0) { 21459243Sobrien msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); 21559243Sobrien if (msgs[num_msgs].msg == NULL) { 21659243Sobrien syslog(LOG_CRIT, "Out of memory"); 21759243Sobrien return (PAM_SERVICE_ERR); 21859243Sobrien } 21959243Sobrien msgs[num_msgs].msg_style = PAM_TEXT_INFO; 22059243Sobrien msg_ptrs[num_msgs] = &msgs[num_msgs]; 22159243Sobrien num_msgs++; 22259243Sobrien } 22359243Sobrien msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; 22459243Sobrien if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { 22559243Sobrien syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); 22659243Sobrien return (retval); 22759243Sobrien } 22859243Sobrien conv = (const struct pam_conv *)item; 22959243Sobrien if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, 23059243Sobrien conv->appdata_ptr)) != PAM_SUCCESS) 23159243Sobrien return (retval); 23259243Sobrien if (build_access_request(radh, user, resp[num_msgs-1].resp, NULL, 23359243Sobrien NULL, state, statelen) == -1) 23459243Sobrien return (PAM_SERVICE_ERR); 23559243Sobrien memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); 23659243Sobrien free(resp[num_msgs-1].resp); 23759243Sobrien free(resp); 23859243Sobrien while (num_msgs > 0) 23959243Sobrien free(msgs[--num_msgs].msg); 24059243Sobrien return (PAM_SUCCESS); 24159243Sobrien} 24259243Sobrien 24359243SobrienPAM_EXTERN int 24459243Sobrienpam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 24559243Sobrien int argc __unused, const char *argv[] __unused) 24659243Sobrien{ 24759243Sobrien struct rad_handle *radh; 24859243Sobrien const char *user, *tmpuser, *pass; 24959243Sobrien const char *conf_file, *template_user, *nas_id, *nas_ipaddr; 25059243Sobrien int retval; 25159243Sobrien int e; 25259243Sobrien 25359243Sobrien conf_file = openpam_get_option(pamh, PAM_OPT_CONF); 25459243Sobrien template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); 25559243Sobrien nas_id = openpam_get_option(pamh, PAM_OPT_NAS_ID); 25659243Sobrien nas_ipaddr = openpam_get_option(pamh, PAM_OPT_NAS_IPADDR); 25759243Sobrien 25859243Sobrien retval = pam_get_user(pamh, &user, NULL); 25959243Sobrien if (retval != PAM_SUCCESS) 26059243Sobrien return (retval); 26159243Sobrien 26259243Sobrien PAM_LOG("Got user: %s", user); 26359243Sobrien 26459243Sobrien retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT); 26559243Sobrien if (retval != PAM_SUCCESS) 26659243Sobrien return (retval); 26759243Sobrien 26859243Sobrien PAM_LOG("Got password"); 26959243Sobrien 27059243Sobrien radh = rad_open(); 27159243Sobrien if (radh == NULL) { 27259243Sobrien syslog(LOG_CRIT, "rad_open failed"); 27359243Sobrien return (PAM_SERVICE_ERR); 27459243Sobrien } 27559243Sobrien 27659243Sobrien PAM_LOG("Radius opened"); 27759243Sobrien 27859243Sobrien if (rad_config(radh, conf_file) == -1) { 27959243Sobrien syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); 28059243Sobrien rad_close(radh); 28159243Sobrien return (PAM_SERVICE_ERR); 28259243Sobrien } 28359243Sobrien 28459243Sobrien PAM_LOG("Radius config file read"); 28559243Sobrien 28659243Sobrien if (build_access_request(radh, user, pass, nas_id, nas_ipaddr, NULL, 28759243Sobrien 0) == -1) { 28859243Sobrien rad_close(radh); 28959243Sobrien return (PAM_SERVICE_ERR); 29059243Sobrien } 29159243Sobrien 29259243Sobrien PAM_LOG("Radius build access done"); 29359243Sobrien 29459243Sobrien for (;;) { 29559243Sobrien switch (rad_send_request(radh)) { 29659243Sobrien 29759243Sobrien case RAD_ACCESS_ACCEPT: 29859243Sobrien e = do_accept(pamh, radh); 29959243Sobrien rad_close(radh); 30059243Sobrien if (e == -1) 30159243Sobrien return (PAM_SERVICE_ERR); 30259243Sobrien if (template_user != NULL) { 30359243Sobrien 30459243Sobrien PAM_LOG("Trying template user: %s", 30559243Sobrien template_user); 30659243Sobrien 30759243Sobrien /* 30859243Sobrien * If the given user name doesn't exist in 30959243Sobrien * the local password database, change it 31059243Sobrien * to the value given in the "template_user" 31159243Sobrien * option. 31259243Sobrien */ 31359243Sobrien retval = pam_get_item(pamh, PAM_USER, 31459243Sobrien (const void **)&tmpuser); 31559243Sobrien if (retval != PAM_SUCCESS) 31659243Sobrien return (retval); 31759243Sobrien if (getpwnam(tmpuser) == NULL) { 31859243Sobrien pam_set_item(pamh, PAM_USER, 31959243Sobrien template_user); 32059243Sobrien PAM_LOG("Using template user"); 32159243Sobrien } 32259243Sobrien 32359243Sobrien } 32459243Sobrien return (PAM_SUCCESS); 32559243Sobrien 32659243Sobrien case RAD_ACCESS_REJECT: 32759243Sobrien rad_close(radh); 32859243Sobrien PAM_VERBOSE_ERROR("Radius rejection"); 32959243Sobrien return (PAM_AUTH_ERR); 33059243Sobrien 33159243Sobrien case RAD_ACCESS_CHALLENGE: 33259243Sobrien retval = do_challenge(pamh, radh, user); 33359243Sobrien if (retval != PAM_SUCCESS) { 33459243Sobrien rad_close(radh); 33559243Sobrien return (retval); 33659243Sobrien } 33759243Sobrien break; 33859243Sobrien 33959243Sobrien case -1: 34059243Sobrien syslog(LOG_CRIT, "rad_send_request: %s", 34159243Sobrien rad_strerror(radh)); 34259243Sobrien rad_close(radh); 34359243Sobrien PAM_VERBOSE_ERROR("Radius failure"); 34459243Sobrien return (PAM_AUTHINFO_UNAVAIL); 34559243Sobrien 34659243Sobrien default: 34759243Sobrien syslog(LOG_CRIT, 34859243Sobrien "rad_send_request: unexpected return value"); 34959243Sobrien rad_close(radh); 35059243Sobrien PAM_VERBOSE_ERROR("Radius error"); 35159243Sobrien return (PAM_SERVICE_ERR); 35259243Sobrien } 35359243Sobrien } 35459243Sobrien} 35559243Sobrien 35659243SobrienPAM_EXTERN int 35759243Sobrienpam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 35859243Sobrien int argc __unused, const char *argv[] __unused) 35959243Sobrien{ 36059243Sobrien 36159243Sobrien return (PAM_SUCCESS); 36259243Sobrien} 36359243Sobrien 36459243SobrienPAM_MODULE_ENTRY("pam_radius"); 36559243Sobrien