1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright 1998 Juniper Networks, Inc. 5 * All rights reserved. 6 * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 7 * All rights reserved. 8 * 9 * Portions of this software were developed for the FreeBSD Project by 10 * ThinkSec AS and NAI Labs, the Security Research Division of Network 11 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 12 * ("CBOSS"), as part of the DARPA CHATS research program. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. The name of the author may not be used to endorse or promote 23 * products derived from this software without specific prior written 24 * permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/param.h> 40 41 #include <pwd.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <syslog.h> 45 #include <taclib.h> 46 #include <unistd.h> 47 48 #define PAM_SM_AUTH 49 50 #include <security/pam_appl.h> 51 #include <security/pam_modules.h> 52 #include <security/pam_mod_misc.h> 53 54 #define PAM_OPT_CONF "conf" 55 #define PAM_OPT_TEMPLATE_USER "template_user" 56 57 typedef int (*set_func)(struct tac_handle *, const char *); 58 59 static int do_item(pam_handle_t *, struct tac_handle *, int, 60 set_func, const char *); 61 static char *get_msg(struct tac_handle *); 62 static int set_msg(struct tac_handle *, const char *); 63 64 static int 65 do_item(pam_handle_t *pamh, struct tac_handle *tach, int item, 66 set_func func, const char *funcname) 67 { 68 int retval; 69 const void *value; 70 71 retval = pam_get_item(pamh, item, &value); 72 if (retval != PAM_SUCCESS) 73 return retval; 74 if (value != NULL && (*func)(tach, (const char *)value) == -1) { 75 syslog(LOG_CRIT, "%s: %s", funcname, tac_strerror(tach)); 76 tac_close(tach); 77 return PAM_SERVICE_ERR; 78 } 79 return PAM_SUCCESS; 80 } 81 82 static char * 83 get_msg(struct tac_handle *tach) 84 { 85 char *msg; 86 87 msg = tac_get_msg(tach); 88 if (msg == NULL) { 89 syslog(LOG_CRIT, "tac_get_msg: %s", tac_strerror(tach)); 90 tac_close(tach); 91 return NULL; 92 } 93 return msg; 94 } 95 96 static int 97 set_msg(struct tac_handle *tach, const char *msg) 98 { 99 if (tac_set_msg(tach, msg) == -1) { 100 syslog(LOG_CRIT, "tac_set_msg: %s", tac_strerror(tach)); 101 tac_close(tach); 102 return -1; 103 } 104 return 0; 105 } 106 107 PAM_EXTERN int 108 pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 109 int argc __unused, const char *argv[] __unused) 110 { 111 int retval; 112 struct tac_handle *tach; 113 const char *conf_file, *template_user; 114 115 conf_file = openpam_get_option(pamh, PAM_OPT_CONF); 116 template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); 117 118 tach = tac_open(); 119 if (tach == NULL) { 120 syslog(LOG_CRIT, "tac_open failed"); 121 return (PAM_SERVICE_ERR); 122 } 123 if (tac_config(tach, conf_file) == -1) { 124 syslog(LOG_ALERT, "tac_config: %s", tac_strerror(tach)); 125 tac_close(tach); 126 return (PAM_SERVICE_ERR); 127 } 128 if (tac_create_authen(tach, TAC_AUTHEN_LOGIN, TAC_AUTHEN_TYPE_ASCII, 129 TAC_AUTHEN_SVC_LOGIN) == -1) { 130 syslog(LOG_CRIT, "tac_create_authen: %s", tac_strerror(tach)); 131 tac_close(tach); 132 return (PAM_SERVICE_ERR); 133 } 134 135 PAM_LOG("Done tac_open() ... tac_close()"); 136 137 retval = do_item(pamh, tach, PAM_USER, tac_set_user, "tac_set_user"); 138 if (retval != PAM_SUCCESS) 139 return (retval); 140 141 PAM_LOG("Done user"); 142 143 retval = do_item(pamh, tach, PAM_TTY, tac_set_port, "tac_set_port"); 144 if (retval != PAM_SUCCESS) 145 return (retval); 146 147 PAM_LOG("Done tty"); 148 149 retval = do_item(pamh, tach, PAM_RHOST, tac_set_rem_addr, 150 "tac_set_rem_addr"); 151 if (retval != PAM_SUCCESS) 152 return (retval); 153 154 for (;;) { 155 char *srvr_msg; 156 size_t msg_len; 157 const char *user_msg; 158 char *data_msg; 159 int sflags; 160 int status; 161 162 sflags = tac_send_authen(tach); 163 if (sflags == -1) { 164 syslog(LOG_CRIT, "tac_send_authen: %s", 165 tac_strerror(tach)); 166 tac_close(tach); 167 return (PAM_AUTHINFO_UNAVAIL); 168 } 169 status = TAC_AUTHEN_STATUS(sflags); 170 openpam_set_option(pamh, PAM_OPT_ECHO_PASS, 171 TAC_AUTHEN_NOECHO(sflags) ? NULL : ""); 172 switch (status) { 173 174 case TAC_AUTHEN_STATUS_PASS: 175 tac_close(tach); 176 if (template_user != NULL) { 177 const void *item; 178 const char *user; 179 180 PAM_LOG("Trying template user: %s", 181 template_user); 182 183 /* 184 * If the given user name doesn't exist in 185 * the local password database, change it 186 * to the value given in the "template_user" 187 * option. 188 */ 189 retval = pam_get_item(pamh, PAM_USER, &item); 190 if (retval != PAM_SUCCESS) 191 return (retval); 192 user = (const char *)item; 193 if (getpwnam(user) == NULL) { 194 pam_set_item(pamh, PAM_USER, 195 template_user); 196 PAM_LOG("Using template user"); 197 } 198 } 199 return (PAM_SUCCESS); 200 201 case TAC_AUTHEN_STATUS_FAIL: 202 tac_close(tach); 203 PAM_VERBOSE_ERROR("TACACS+ authentication failed"); 204 return (PAM_AUTH_ERR); 205 206 case TAC_AUTHEN_STATUS_GETUSER: 207 case TAC_AUTHEN_STATUS_GETPASS: 208 if ((srvr_msg = get_msg(tach)) == NULL) 209 return (PAM_SERVICE_ERR); 210 if (status == TAC_AUTHEN_STATUS_GETUSER) 211 retval = pam_get_user(pamh, &user_msg, 212 *srvr_msg ? srvr_msg : NULL); 213 else if (status == TAC_AUTHEN_STATUS_GETPASS) 214 retval = pam_get_authtok(pamh, 215 PAM_AUTHTOK, &user_msg, 216 *srvr_msg ? srvr_msg : "Password:"); 217 free(srvr_msg); 218 if (retval != PAM_SUCCESS) { 219 /* XXX - send a TACACS+ abort packet */ 220 tac_close(tach); 221 return (retval); 222 } 223 if (set_msg(tach, user_msg) == -1) 224 return (PAM_SERVICE_ERR); 225 break; 226 227 case TAC_AUTHEN_STATUS_GETDATA: 228 if ((srvr_msg = get_msg(tach)) == NULL) 229 return (PAM_SERVICE_ERR); 230 retval = pam_prompt(pamh, 231 openpam_get_option(pamh, PAM_OPT_ECHO_PASS) ? 232 PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF, 233 &data_msg, "%s", *srvr_msg ? srvr_msg : "Data:"); 234 free(srvr_msg); 235 if (retval != PAM_SUCCESS) { 236 /* XXX - send a TACACS+ abort packet */ 237 tac_close(tach); 238 return (retval); 239 } 240 retval = set_msg(tach, data_msg); 241 memset(data_msg, 0, strlen(data_msg)); 242 free(data_msg); 243 if (retval == -1) 244 return (PAM_SERVICE_ERR); 245 break; 246 247 case TAC_AUTHEN_STATUS_ERROR: 248 srvr_msg = (char *)tac_get_data(tach, &msg_len); 249 if (srvr_msg != NULL && msg_len != 0) { 250 syslog(LOG_CRIT, "tac_send_authen:" 251 " server detected error: %s", srvr_msg); 252 free(srvr_msg); 253 } 254 else 255 syslog(LOG_CRIT, 256 "tac_send_authen: server detected error"); 257 tac_close(tach); 258 return (PAM_AUTHINFO_UNAVAIL); 259 break; 260 261 case TAC_AUTHEN_STATUS_RESTART: 262 case TAC_AUTHEN_STATUS_FOLLOW: 263 default: 264 syslog(LOG_CRIT, 265 "tac_send_authen: unexpected status %#x", status); 266 tac_close(tach); 267 return (PAM_AUTHINFO_UNAVAIL); 268 } 269 } 270 } 271 272 PAM_EXTERN int 273 pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 274 int argc __unused, const char *argv[] __unused) 275 { 276 277 return (PAM_IGNORE); 278 } 279 280 PAM_MODULE_ENTRY("pam_tacplus"); 281