1 /*- 2 * Copyright 1998 Juniper Networks, Inc. 3 * All rights reserved. 4 * Copyright (c) 2001-2003 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 #define PAM_OPT_CONF "conf" 56 #define PAM_OPT_TEMPLATE_USER "template_user" 57 58 typedef int (*set_func)(struct tac_handle *, const char *); 59 60 static int do_item(pam_handle_t *, struct tac_handle *, int, 61 set_func, const char *); 62 static char *get_msg(struct tac_handle *); 63 static int set_msg(struct tac_handle *, const char *); 64 65 static int 66 do_item(pam_handle_t *pamh, struct tac_handle *tach, int item, 67 set_func func, const char *funcname) 68 { 69 int retval; 70 const void *value; 71 72 retval = pam_get_item(pamh, item, &value); 73 if (retval != PAM_SUCCESS) 74 return retval; 75 if (value != NULL && (*func)(tach, (const char *)value) == -1) { 76 syslog(LOG_CRIT, "%s: %s", funcname, tac_strerror(tach)); 77 tac_close(tach); 78 return PAM_SERVICE_ERR; 79 } 80 return PAM_SUCCESS; 81 } 82 83 static char * 84 get_msg(struct tac_handle *tach) 85 { 86 char *msg; 87 88 msg = tac_get_msg(tach); 89 if (msg == NULL) { 90 syslog(LOG_CRIT, "tac_get_msg: %s", tac_strerror(tach)); 91 tac_close(tach); 92 return NULL; 93 } 94 return msg; 95 } 96 97 static int 98 set_msg(struct tac_handle *tach, const char *msg) 99 { 100 if (tac_set_msg(tach, msg) == -1) { 101 syslog(LOG_CRIT, "tac_set_msg: %s", tac_strerror(tach)); 102 tac_close(tach); 103 return -1; 104 } 105 return 0; 106 } 107 108 PAM_EXTERN int 109 pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 110 int argc __unused, const char *argv[] __unused) 111 { 112 int retval; 113 struct tac_handle *tach; 114 const char *conf_file, *template_user; 115 116 conf_file = openpam_get_option(pamh, PAM_OPT_CONF); 117 template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); 118 119 tach = tac_open(); 120 if (tach == NULL) { 121 syslog(LOG_CRIT, "tac_open failed"); 122 return (PAM_SERVICE_ERR); 123 } 124 if (tac_config(tach, conf_file) == -1) { 125 syslog(LOG_ALERT, "tac_config: %s", tac_strerror(tach)); 126 tac_close(tach); 127 return (PAM_SERVICE_ERR); 128 } 129 if (tac_create_authen(tach, TAC_AUTHEN_LOGIN, TAC_AUTHEN_TYPE_ASCII, 130 TAC_AUTHEN_SVC_LOGIN) == -1) { 131 syslog(LOG_CRIT, "tac_create_authen: %s", tac_strerror(tach)); 132 tac_close(tach); 133 return (PAM_SERVICE_ERR); 134 } 135 136 PAM_LOG("Done tac_open() ... tac_close()"); 137 138 retval = do_item(pamh, tach, PAM_USER, tac_set_user, "tac_set_user"); 139 if (retval != PAM_SUCCESS) 140 return (retval); 141 142 PAM_LOG("Done user"); 143 144 retval = do_item(pamh, tach, PAM_TTY, tac_set_port, "tac_set_port"); 145 if (retval != PAM_SUCCESS) 146 return (retval); 147 148 PAM_LOG("Done tty"); 149 150 retval = do_item(pamh, tach, PAM_RHOST, tac_set_rem_addr, 151 "tac_set_rem_addr"); 152 if (retval != PAM_SUCCESS) 153 return (retval); 154 155 for (;;) { 156 char *srvr_msg; 157 size_t msg_len; 158 const char *user_msg; 159 char *data_msg; 160 int sflags; 161 int status; 162 163 sflags = tac_send_authen(tach); 164 if (sflags == -1) { 165 syslog(LOG_CRIT, "tac_send_authen: %s", 166 tac_strerror(tach)); 167 tac_close(tach); 168 return (PAM_AUTHINFO_UNAVAIL); 169 } 170 status = TAC_AUTHEN_STATUS(sflags); 171 openpam_set_option(pamh, PAM_OPT_ECHO_PASS, 172 TAC_AUTHEN_NOECHO(sflags) ? NULL : ""); 173 switch (status) { 174 175 case TAC_AUTHEN_STATUS_PASS: 176 tac_close(tach); 177 if (template_user != NULL) { 178 const void *item; 179 const char *user; 180 181 PAM_LOG("Trying template user: %s", 182 template_user); 183 184 /* 185 * If the given user name doesn't exist in 186 * the local password database, change it 187 * to the value given in the "template_user" 188 * option. 189 */ 190 retval = pam_get_item(pamh, PAM_USER, &item); 191 if (retval != PAM_SUCCESS) 192 return (retval); 193 user = (const char *)item; 194 if (getpwnam(user) == NULL) { 195 pam_set_item(pamh, PAM_USER, 196 template_user); 197 PAM_LOG("Using template user"); 198 } 199 } 200 return (PAM_SUCCESS); 201 202 case TAC_AUTHEN_STATUS_FAIL: 203 tac_close(tach); 204 PAM_VERBOSE_ERROR("TACACS+ authentication failed"); 205 return (PAM_AUTH_ERR); 206 207 case TAC_AUTHEN_STATUS_GETUSER: 208 case TAC_AUTHEN_STATUS_GETPASS: 209 if ((srvr_msg = get_msg(tach)) == NULL) 210 return (PAM_SERVICE_ERR); 211 if (status == TAC_AUTHEN_STATUS_GETUSER) 212 retval = pam_get_user(pamh, &user_msg, 213 *srvr_msg ? srvr_msg : NULL); 214 else if (status == TAC_AUTHEN_STATUS_GETPASS) 215 retval = pam_get_authtok(pamh, 216 PAM_AUTHTOK, &user_msg, 217 *srvr_msg ? srvr_msg : "Password:"); 218 free(srvr_msg); 219 if (retval != PAM_SUCCESS) { 220 /* XXX - send a TACACS+ abort packet */ 221 tac_close(tach); 222 return (retval); 223 } 224 if (set_msg(tach, user_msg) == -1) 225 return (PAM_SERVICE_ERR); 226 break; 227 228 case TAC_AUTHEN_STATUS_GETDATA: 229 if ((srvr_msg = get_msg(tach)) == NULL) 230 return (PAM_SERVICE_ERR); 231 retval = pam_prompt(pamh, 232 openpam_get_option(pamh, PAM_OPT_ECHO_PASS) ? 233 PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF, 234 &data_msg, "%s", *srvr_msg ? srvr_msg : "Data:"); 235 free(srvr_msg); 236 if (retval != PAM_SUCCESS) { 237 /* XXX - send a TACACS+ abort packet */ 238 tac_close(tach); 239 return (retval); 240 } 241 retval = set_msg(tach, data_msg); 242 memset(data_msg, 0, strlen(data_msg)); 243 free(data_msg); 244 if (retval == -1) 245 return (PAM_SERVICE_ERR); 246 break; 247 248 case TAC_AUTHEN_STATUS_ERROR: 249 srvr_msg = (char *)tac_get_data(tach, &msg_len); 250 if (srvr_msg != NULL && msg_len != 0) { 251 syslog(LOG_CRIT, "tac_send_authen:" 252 " server detected error: %s", srvr_msg); 253 free(srvr_msg); 254 } 255 else 256 syslog(LOG_CRIT, 257 "tac_send_authen: server detected error"); 258 tac_close(tach); 259 return (PAM_AUTHINFO_UNAVAIL); 260 break; 261 262 case TAC_AUTHEN_STATUS_RESTART: 263 case TAC_AUTHEN_STATUS_FOLLOW: 264 default: 265 syslog(LOG_CRIT, 266 "tac_send_authen: unexpected status %#x", status); 267 tac_close(tach); 268 return (PAM_AUTHINFO_UNAVAIL); 269 } 270 } 271 } 272 273 PAM_EXTERN int 274 pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 275 int argc __unused, const char *argv[] __unused) 276 { 277 278 return (PAM_IGNORE); 279 } 280 281 PAM_MODULE_ENTRY("pam_tacplus"); 282