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