1 /* 2 * Copyright (c) 2000 Damien Miller. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "includes.h" 26 27 #ifdef USE_PAM 28 #include "ssh.h" 29 #include "xmalloc.h" 30 #include "log.h" 31 #include "auth.h" 32 #include "auth-pam.h" 33 #include "servconf.h" 34 #include "canohost.h" 35 #include "readpass.h" 36 37 extern char *__progname; 38 39 RCSID("$Id: auth-pam.c,v 1.46 2002/05/08 02:27:56 djm Exp $"); 40 41 #define NEW_AUTHTOK_MSG \ 42 "Warning: Your password has expired, please change it now" 43 44 static int do_pam_conversation(int num_msg, const struct pam_message **msg, 45 struct pam_response **resp, void *appdata_ptr); 46 47 /* module-local variables */ 48 static struct pam_conv conv = { 49 do_pam_conversation, 50 NULL 51 }; 52 static char *__pam_msg = NULL; 53 static pam_handle_t *__pamh = NULL; 54 static const char *__pampasswd = NULL; 55 56 /* states for do_pam_conversation() */ 57 enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN; 58 /* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */ 59 static int password_change_required = 0; 60 /* remember whether the last pam_authenticate() succeeded or not */ 61 static int was_authenticated = 0; 62 63 /* Remember what has been initialised */ 64 static int session_opened = 0; 65 static int creds_set = 0; 66 67 /* accessor which allows us to switch conversation structs according to 68 * the authentication method being used */ 69 void do_pam_set_conv(struct pam_conv *conv) 70 { 71 pam_set_item(__pamh, PAM_CONV, conv); 72 } 73 74 /* start an authentication run */ 75 int do_pam_authenticate(int flags) 76 { 77 int retval = pam_authenticate(__pamh, flags); 78 was_authenticated = (retval == PAM_SUCCESS); 79 return retval; 80 } 81 82 /* 83 * PAM conversation function. 84 * There are two states this can run in. 85 * 86 * INITIAL_LOGIN mode simply feeds the password from the client into 87 * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output 88 * messages with into __pam_msg. This is used during initial 89 * authentication to bypass the normal PAM password prompt. 90 * 91 * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase() 92 * and outputs messages to stderr. This mode is used if pam_chauthtok() 93 * is called to update expired passwords. 94 */ 95 static int do_pam_conversation(int num_msg, const struct pam_message **msg, 96 struct pam_response **resp, void *appdata_ptr) 97 { 98 struct pam_response *reply; 99 int count; 100 char buf[1024]; 101 102 /* PAM will free this later */ 103 reply = malloc(num_msg * sizeof(*reply)); 104 if (reply == NULL) 105 return PAM_CONV_ERR; 106 107 for (count = 0; count < num_msg; count++) { 108 if (pamstate == INITIAL_LOGIN) { 109 /* 110 * We can't use stdio yet, queue messages for 111 * printing later 112 */ 113 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 114 case PAM_PROMPT_ECHO_ON: 115 free(reply); 116 return PAM_CONV_ERR; 117 case PAM_PROMPT_ECHO_OFF: 118 if (__pampasswd == NULL) { 119 free(reply); 120 return PAM_CONV_ERR; 121 } 122 reply[count].resp = xstrdup(__pampasswd); 123 reply[count].resp_retcode = PAM_SUCCESS; 124 break; 125 case PAM_ERROR_MSG: 126 case PAM_TEXT_INFO: 127 if ((*msg)[count].msg != NULL) { 128 message_cat(&__pam_msg, 129 PAM_MSG_MEMBER(msg, count, msg)); 130 } 131 reply[count].resp = xstrdup(""); 132 reply[count].resp_retcode = PAM_SUCCESS; 133 break; 134 default: 135 free(reply); 136 return PAM_CONV_ERR; 137 } 138 } else { 139 /* 140 * stdio is connected, so interact directly 141 */ 142 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 143 case PAM_PROMPT_ECHO_ON: 144 fputs(PAM_MSG_MEMBER(msg, count, msg), stderr); 145 fgets(buf, sizeof(buf), stdin); 146 reply[count].resp = xstrdup(buf); 147 reply[count].resp_retcode = PAM_SUCCESS; 148 break; 149 case PAM_PROMPT_ECHO_OFF: 150 reply[count].resp = 151 read_passphrase(PAM_MSG_MEMBER(msg, count, 152 msg), RP_ALLOW_STDIN); 153 reply[count].resp_retcode = PAM_SUCCESS; 154 break; 155 case PAM_ERROR_MSG: 156 case PAM_TEXT_INFO: 157 if ((*msg)[count].msg != NULL) 158 fprintf(stderr, "%s\n", 159 PAM_MSG_MEMBER(msg, count, msg)); 160 reply[count].resp = xstrdup(""); 161 reply[count].resp_retcode = PAM_SUCCESS; 162 break; 163 default: 164 free(reply); 165 return PAM_CONV_ERR; 166 } 167 } 168 } 169 170 *resp = reply; 171 172 return PAM_SUCCESS; 173 } 174 175 /* Called at exit to cleanly shutdown PAM */ 176 void do_pam_cleanup_proc(void *context) 177 { 178 int pam_retval = PAM_SUCCESS; 179 180 if (__pamh && session_opened) { 181 pam_retval = pam_close_session(__pamh, 0); 182 if (pam_retval != PAM_SUCCESS) 183 log("Cannot close PAM session[%d]: %.200s", 184 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 185 } 186 187 if (__pamh && creds_set) { 188 pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED); 189 if (pam_retval != PAM_SUCCESS) 190 debug("Cannot delete credentials[%d]: %.200s", 191 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 192 } 193 194 if (__pamh) { 195 pam_retval = pam_end(__pamh, pam_retval); 196 if (pam_retval != PAM_SUCCESS) 197 log("Cannot release PAM authentication[%d]: %.200s", 198 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 199 } 200 } 201 202 /* Attempt password authentation using PAM */ 203 int auth_pam_password(Authctxt *authctxt, const char *password) 204 { 205 extern ServerOptions options; 206 int pam_retval; 207 struct passwd *pw = authctxt->pw; 208 209 do_pam_set_conv(&conv); 210 211 /* deny if no user. */ 212 if (pw == NULL) 213 return 0; 214 if (pw->pw_uid == 0 && options.permit_root_login == PERMIT_NO_PASSWD) 215 return 0; 216 if (*password == '\0' && options.permit_empty_passwd == 0) 217 return 0; 218 219 __pampasswd = password; 220 221 pamstate = INITIAL_LOGIN; 222 pam_retval = do_pam_authenticate( 223 options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); 224 if (pam_retval == PAM_SUCCESS) { 225 debug("PAM Password authentication accepted for " 226 "user \"%.100s\"", pw->pw_name); 227 return 1; 228 } else { 229 debug("PAM Password authentication for \"%.100s\" " 230 "failed[%d]: %s", pw->pw_name, pam_retval, 231 PAM_STRERROR(__pamh, pam_retval)); 232 return 0; 233 } 234 } 235 236 /* Do account management using PAM */ 237 int do_pam_account(char *username, char *remote_user) 238 { 239 int pam_retval; 240 241 do_pam_set_conv(&conv); 242 243 if (remote_user) { 244 debug("PAM setting ruser to \"%.200s\"", remote_user); 245 pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user); 246 if (pam_retval != PAM_SUCCESS) 247 fatal("PAM set ruser failed[%d]: %.200s", pam_retval, 248 PAM_STRERROR(__pamh, pam_retval)); 249 } 250 251 pam_retval = pam_acct_mgmt(__pamh, 0); 252 debug2("pam_acct_mgmt() = %d", pam_retval); 253 switch (pam_retval) { 254 case PAM_SUCCESS: 255 /* This is what we want */ 256 break; 257 #if 0 258 case PAM_NEW_AUTHTOK_REQD: 259 message_cat(&__pam_msg, NEW_AUTHTOK_MSG); 260 /* flag that password change is necessary */ 261 password_change_required = 1; 262 break; 263 #endif 264 default: 265 log("PAM rejected by account configuration[%d]: " 266 "%.200s", pam_retval, PAM_STRERROR(__pamh, 267 pam_retval)); 268 return(0); 269 } 270 271 return(1); 272 } 273 274 /* Do PAM-specific session initialisation */ 275 void do_pam_session(char *username, const char *ttyname) 276 { 277 int pam_retval; 278 279 do_pam_set_conv(&conv); 280 281 if (ttyname != NULL) { 282 debug("PAM setting tty to \"%.200s\"", ttyname); 283 pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname); 284 if (pam_retval != PAM_SUCCESS) 285 fatal("PAM set tty failed[%d]: %.200s", 286 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 287 } 288 289 pam_retval = pam_open_session(__pamh, 0); 290 if (pam_retval != PAM_SUCCESS) 291 fatal("PAM session setup failed[%d]: %.200s", 292 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 293 294 session_opened = 1; 295 } 296 297 /* Set PAM credentials */ 298 void do_pam_setcred(int init) 299 { 300 int pam_retval; 301 302 if (__pamh == NULL) 303 return; 304 305 do_pam_set_conv(&conv); 306 307 debug("PAM establishing creds"); 308 pam_retval = pam_setcred(__pamh, 309 init ? PAM_ESTABLISH_CRED : PAM_REINITIALIZE_CRED); 310 if (pam_retval != PAM_SUCCESS) { 311 if (was_authenticated) 312 fatal("PAM setcred failed[%d]: %.200s", 313 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 314 else 315 debug("PAM setcred failed[%d]: %.200s", 316 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 317 } else 318 creds_set = 1; 319 } 320 321 /* accessor function for file scope static variable */ 322 int is_pam_password_change_required(void) 323 { 324 return password_change_required; 325 } 326 327 /* 328 * Have user change authentication token if pam_acct_mgmt() indicated 329 * it was expired. This needs to be called after an interactive 330 * session is established and the user's pty is connected to 331 * stdin/stout/stderr. 332 */ 333 void do_pam_chauthtok(void) 334 { 335 int pam_retval; 336 337 do_pam_set_conv(&conv); 338 339 if (password_change_required) { 340 pamstate = OTHER; 341 pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 342 if (pam_retval != PAM_SUCCESS) 343 fatal("PAM pam_chauthtok failed[%d]: %.200s", 344 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 345 } 346 } 347 348 /* Cleanly shutdown PAM */ 349 void finish_pam(void) 350 { 351 do_pam_cleanup_proc(NULL); 352 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL); 353 } 354 355 /* Start PAM authentication for specified account */ 356 void start_pam(const char *user) 357 { 358 int pam_retval; 359 extern ServerOptions options; 360 extern u_int utmp_len; 361 const char *rhost; 362 363 debug("Starting up PAM with username \"%.200s\"", user); 364 365 pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh); 366 367 if (pam_retval != PAM_SUCCESS) 368 fatal("PAM initialisation failed[%d]: %.200s", 369 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 370 371 rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); 372 debug("PAM setting rhost to \"%.200s\"", rhost); 373 374 pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost); 375 if (pam_retval != PAM_SUCCESS) 376 fatal("PAM set rhost failed[%d]: %.200s", pam_retval, 377 PAM_STRERROR(__pamh, pam_retval)); 378 #ifdef PAM_TTY_KLUDGE 379 /* 380 * Some PAM modules (e.g. pam_time) require a TTY to operate, 381 * and will fail in various stupid ways if they don't get one. 382 * sshd doesn't set the tty until too late in the auth process and may 383 * not even need one (for tty-less connections) 384 * Kludge: Set a fake PAM_TTY 385 */ 386 pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh"); 387 if (pam_retval != PAM_SUCCESS) 388 fatal("PAM set tty failed[%d]: %.200s", 389 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 390 #endif /* PAM_TTY_KLUDGE */ 391 392 fatal_add_cleanup(&do_pam_cleanup_proc, NULL); 393 } 394 395 /* Return list of PAM enviornment strings */ 396 char **fetch_pam_environment(void) 397 { 398 #ifdef HAVE_PAM_GETENVLIST 399 return(pam_getenvlist(__pamh)); 400 #else /* HAVE_PAM_GETENVLIST */ 401 return(NULL); 402 #endif /* HAVE_PAM_GETENVLIST */ 403 } 404 405 /* Print any messages that have been generated during authentication */ 406 /* or account checking to stderr */ 407 void print_pam_messages(void) 408 { 409 if (__pam_msg != NULL) 410 fputs(__pam_msg, stderr); 411 } 412 413 /* Append a message to buffer */ 414 void message_cat(char **p, const char *a) 415 { 416 char *cp; 417 size_t new_len; 418 419 new_len = strlen(a); 420 421 if (*p) { 422 size_t len = strlen(*p); 423 424 *p = xrealloc(*p, new_len + len + 2); 425 cp = *p + len; 426 } else 427 *p = cp = xmalloc(new_len + 2); 428 429 memcpy(cp, a, new_len); 430 cp[new_len] = '\n'; 431 cp[new_len + 1] = '\0'; 432 } 433 434 #endif /* USE_PAM */ 435