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 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
26 */
27
28 #include "includes.h"
29
30 #ifdef USE_PAM
31 #include "xmalloc.h"
32 #include "log.h"
33 #include "auth.h"
34 #include "auth-options.h"
35 #include "auth-pam.h"
36 #include "buffer.h"
37 #include "servconf.h"
38 #include "canohost.h"
39 #include "compat.h"
40 #include "misc.h"
41 #include "sshlogin.h"
42 #include "ssh-gss.h"
43
44 #include <security/pam_appl.h>
45
46 extern char *__progname;
47
48 extern u_int utmp_len;
49 extern ServerOptions options;
50
51 extern Authmethod method_kbdint;
52
53 RCSID("$Id: auth-pam.c,v 1.54 2002/07/28 20:24:08 stevesk Exp $");
54
55 #define NEW_AUTHTOK_MSG \
56 "Warning: Your password has expired, please change it now."
57
58 /* PAM conversation for non-interactive userauth methods */
59 static int do_pam_conversation(int num_msg, const struct pam_message **msg,
60 struct pam_response **resp, void *appdata_ptr);
61
62 static void do_pam_cleanup_proc(void *context);
63
64 static char *get_method_name(Authctxt *authctxt);
65
66 /* PAM conversation for non-interactive userauth methods */
67 static struct pam_conv conv = {
68 (int (*)())do_pam_conversation,
69 NULL
70 };
71 static char *__pam_msg = NULL;
72
73 static
74 char *
get_method_name(Authctxt * authctxt)75 get_method_name(Authctxt *authctxt)
76 {
77 if (!authctxt)
78 return "(unknown)";
79
80 if (!compat20)
81 return (authctxt->v1_auth_name) ? authctxt->v1_auth_name :
82 "(sshv1-unknown)";
83
84 if (!authctxt->method || !authctxt->method->name)
85 return "(sshv2-unknown)";
86
87 return authctxt->method->name;
88 }
89
90 char *
derive_pam_service_name(Authmethod * method)91 derive_pam_service_name(Authmethod *method)
92 {
93 char *svcname = xmalloc(BUFSIZ);
94
95 /*
96 * If PamServiceName is set we use that for everything, including
97 * SSHv1
98 */
99 if (options.pam_service_name != NULL) {
100 (void) strlcpy(svcname, options.pam_service_name, BUFSIZ);
101 return (svcname);
102 }
103
104 if (compat20 && method) {
105 char *method_name = method->name;
106
107 if (!method_name)
108 fatal("Userauth method unknown while starting PAM");
109
110 /*
111 * For SSHv2 we use "sshd-<userauth name>
112 * The "sshd" prefix can be changed via the PAMServicePrefix
113 * sshd_config option.
114 */
115 if (strcmp(method_name, "none") == 0) {
116 snprintf(svcname, BUFSIZ, "%s-none",
117 options.pam_service_prefix);
118 }
119 if (strcmp(method_name, "password") == 0) {
120 snprintf(svcname, BUFSIZ, "%s-password",
121 options.pam_service_prefix);
122 }
123 if (strcmp(method_name, "keyboard-interactive") == 0) {
124 /* "keyboard-interactive" is too long, shorten it */
125 snprintf(svcname, BUFSIZ, "%s-kbdint",
126 options.pam_service_prefix);
127 }
128 if (strcmp(method_name, "publickey") == 0) {
129 /* "publickey" is too long, shorten it */
130 snprintf(svcname, BUFSIZ, "%s-pubkey",
131 options.pam_service_prefix);
132 }
133 if (strcmp(method_name, "hostbased") == 0) {
134 /* "hostbased" can't really be shortened... */
135 snprintf(svcname, BUFSIZ, "%s-hostbased",
136 options.pam_service_prefix);
137 }
138 if (strncmp(method_name, "gss", 3) == 0) {
139 /* "gss" is too short, elongate it */
140 snprintf(svcname, BUFSIZ, "%s-gssapi",
141 options.pam_service_prefix);
142 }
143 return svcname;
144 } else {
145 /* SSHv1 doesn't get to be so cool */
146 snprintf(svcname, BUFSIZ, "%s-v1",
147 options.pam_service_prefix);
148 }
149 return svcname;
150 }
151
152 void
new_start_pam(Authctxt * authctxt,struct pam_conv * conv)153 new_start_pam(Authctxt *authctxt, struct pam_conv *conv)
154 {
155 int retval;
156 pam_handle_t *pamh;
157 const char *rhost;
158 char *svc;
159 char *user = NULL;
160 pam_stuff *pam;
161
162 if (authctxt == NULL)
163 fatal("Internal error during userauth");
164
165 if (compat20 && authctxt->method == NULL)
166 fatal("Userauth method unknown while starting PAM");
167
168 /* PAM service selected here */
169 svc = derive_pam_service_name(authctxt->method);
170 debug2("Starting PAM service %s for method %s", svc,
171 get_method_name(authctxt));
172
173 if (authctxt->user != NULL)
174 user = authctxt->user;
175
176 /* Cleanup previous PAM state */
177 if (authctxt->pam != NULL) {
178 fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
179 do_pam_cleanup_proc(authctxt->pam);
180 }
181
182 pam = xmalloc(sizeof(pam_stuff));
183 (void) memset(pam, 0, sizeof(pam_stuff));
184
185 /*
186 * pam->last_pam_retval has to be and is considered
187 * along with pam->state.
188 *
189 * pam->state = 0; -> no PAM auth, account, etc, work
190 * done yet. (Set by memset() above.)
191 *
192 * pam->last_pam_retval = PAM_SUCCESS; -> meaningless at
193 * this point.
194 *
195 * See finish_userauth_do_pam() below.
196 */
197 pam->authctxt = authctxt;
198 pam->last_pam_retval = PAM_SUCCESS;
199
200 authctxt->pam = pam;
201
202 /* Free any previously stored text/error PAM prompts */
203 if (__pam_msg) {
204 xfree(__pam_msg);
205 __pam_msg = NULL;
206 }
207
208 if ((retval = pam_start(svc, user, conv, &pamh)) != PAM_SUCCESS) {
209 fatal("PAM initialization failed during %s userauth",
210 get_method_name(authctxt));
211 }
212
213 free(svc);
214
215 fatal_add_cleanup((void (*)(void *)) &do_pam_cleanup_proc,
216 (void *) authctxt->pam);
217
218 rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping);
219 if ((retval = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
220 (void) pam_end(pamh, retval);
221 fatal("Could not set PAM_RHOST item during %s userauth",
222 get_method_name(authctxt));
223 }
224
225 if ((retval = pam_set_item(pamh, PAM_TTY, "sshd")) != PAM_SUCCESS) {
226 (void) pam_end(pamh, retval);
227 fatal("Could not set PAM_TTY item during %s userauth",
228 get_method_name(authctxt));
229 }
230
231 if (authctxt->cuser != NULL)
232 if ((retval = pam_set_item(pamh, PAM_AUSER, authctxt->cuser)) != PAM_SUCCESS) {
233 (void) pam_end(pamh, retval);
234 fatal("Could not set PAM_AUSER item during %s userauth",
235 get_method_name(authctxt));
236 }
237
238 authctxt->pam->h = pamh;
239 }
240
241 /*
242 * To be called from userauth methods, directly (as in keyboard-interactive) or
243 * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth().
244 *
245 * The caller is responsible for calling new_start_pam() first.
246 *
247 * PAM state is not cleaned up here on error. This is left to subsequent calls
248 * to new_start_pam() or to the cleanup function upon authentication error.
249 */
250 int
finish_userauth_do_pam(Authctxt * authctxt)251 finish_userauth_do_pam(Authctxt *authctxt)
252 {
253 int retval;
254 char *user, *method;
255
256 /* Various checks; fail gracefully */
257 if (authctxt == NULL || authctxt->pam == NULL)
258 return PAM_SYSTEM_ERR; /* shouldn't happen */
259
260 if (compat20) {
261 if (authctxt->method == NULL || authctxt->method->name == NULL)
262 return PAM_SYSTEM_ERR; /* shouldn't happen */
263 method = authctxt->method->name;
264 } else if ((method = authctxt->v1_auth_name) == NULL)
265 return PAM_SYSTEM_ERR; /* shouldn't happen */
266
267 if (AUTHPAM_DONE(authctxt))
268 return PAM_SYSTEM_ERR; /* shouldn't happen */
269
270 if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) {
271 retval = pam_acct_mgmt(authctxt->pam->h, 0);
272 authctxt->pam->last_pam_retval = retval;
273 if (retval == PAM_NEW_AUTHTOK_REQD) {
274 userauth_force_kbdint();
275 return retval;
276 }
277 if (retval != PAM_SUCCESS)
278 return retval;
279 authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
280 }
281
282 /*
283 * Handle PAM_USER change, if any.
284 *
285 * We do this before pam_open_session() because we need the PAM_USER's
286 * UID for:
287 *
288 * a) PermitRootLogin checking
289 * b) to get at the lastlog entry before pam_open_session() updates it.
290 */
291 retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user);
292 if (retval != PAM_SUCCESS) {
293 fatal("PAM failure: pam_get_item(PAM_USER) "
294 "returned %d: %.200s", retval,
295 PAM_STRERROR(authctxt->pam->h, retval));
296 }
297
298 if (user == NULL || *user == '\0') {
299 debug("PAM set NULL PAM_USER");
300 return PAM_PERM_DENIED;
301 }
302
303 if (strcmp(user, authctxt->user) != 0) {
304 log("PAM changed the SSH username");
305 pwfree(&authctxt->pw);
306 authctxt->pw = getpwnamallow(user);
307 authctxt->valid = (authctxt->pw != NULL);
308 xfree(authctxt->user);
309 authctxt->user = xstrdup(user);
310 }
311
312 if (!authctxt->valid) {
313 debug2("PAM set PAM_USER to unknown user");
314 /*
315 * Return success, userauth_finish() will catch
316 * this and send back a failure message.
317 */
318 return PAM_SUCCESS;
319 }
320
321 /* Check PermitRootLogin semantics */
322 if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method))
323 return PAM_PERM_DENIED;
324
325 if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) {
326 retval = pam_setcred(authctxt->pam->h,
327 PAM_ESTABLISH_CRED);
328 authctxt->pam->last_pam_retval = retval;
329 if (retval != PAM_SUCCESS)
330 return retval;
331 authctxt->pam->state |= PAM_S_DONE_SETCRED;
332
333 #ifdef GSSAPI
334 /*
335 * Store GSS-API delegated creds after pam_setcred(), which may
336 * have set the current credential store.
337 */
338 ssh_gssapi_storecreds(NULL, authctxt);
339 #endif /* GSSAPI */
340 }
341
342 /*
343 * On Solaris pam_unix_session.so updates the lastlog, but does
344 * not converse a PAM_TEXT_INFO message about it. So we need to
345 * fetch the lastlog entry here and save it for use later.
346 */
347 authctxt->last_login_time =
348 get_last_login_time(authctxt->pw->pw_uid,
349 authctxt->pw->pw_name,
350 authctxt->last_login_host,
351 sizeof(authctxt->last_login_host));
352
353 if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) {
354 retval = pam_open_session(authctxt->pam->h, 0);
355 authctxt->pam->last_pam_retval = retval;
356 if (retval != PAM_SUCCESS)
357 return retval;
358 authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION;
359 }
360
361 /*
362 * All PAM work done successfully.
363 *
364 * PAM handle stays around so we can call pam_close_session() on
365 * it later.
366 */
367 return PAM_SUCCESS;
368 }
369
370 /*
371 * PAM conversation function for non-interactive userauth methods that
372 * really cannot do any prompting. Password userauth and CHANGEREQ can
373 * always set the PAM_AUTHTOK and PAM_OLDAUTHTOK items to avoid
374 * conversation (and if they do and nonetheless some module tries to
375 * converse, then password userauth / CHANGEREQ MUST fail).
376 *
377 * Except, PAM_TEXT_INFO and PAM_ERROR_MSG prompts can be squirelled
378 * away and shown to the user later.
379 *
380 * Keyboard-interactive userauth has its own much more interesting
381 * conversation function.
382 *
383 */
384 static int
do_pam_conversation(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)385 do_pam_conversation(int num_msg, const struct pam_message **msg,
386 struct pam_response **resp, void *appdata_ptr)
387 {
388 struct pam_response *reply;
389 int count;
390
391 /* PAM will free this later */
392 reply = xmalloc(num_msg * sizeof(*reply));
393
394 (void) memset(reply, 0, num_msg * sizeof(*reply));
395
396 for (count = 0; count < num_msg; count++) {
397 /*
398 * We can't use stdio yet, queue messages for
399 * printing later
400 */
401 switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
402 case PAM_PROMPT_ECHO_ON:
403 xfree(reply);
404 return PAM_CONV_ERR;
405 case PAM_PROMPT_ECHO_OFF:
406 xfree(reply);
407 return PAM_CONV_ERR;
408 break;
409 case PAM_ERROR_MSG:
410 case PAM_TEXT_INFO:
411 if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
412 message_cat(&__pam_msg,
413 PAM_MSG_MEMBER(msg, count, msg));
414 }
415 reply[count].resp = xstrdup("");
416 reply[count].resp_retcode = PAM_SUCCESS;
417 break;
418 default:
419 xfree(reply);
420 return PAM_CONV_ERR;
421 }
422 }
423
424 *resp = reply;
425
426 return PAM_SUCCESS;
427 }
428
429 /* Called at exit to cleanly shutdown PAM */
430 static void
do_pam_cleanup_proc(void * context)431 do_pam_cleanup_proc(void *context)
432 {
433 int pam_retval;
434 pam_stuff *pam = (pam_stuff *) context;
435
436 if (pam == NULL)
437 return;
438
439 if (pam->authctxt != NULL && pam->authctxt->pam == pam) {
440 pam->authctxt->pam_retval = pam->last_pam_retval;
441 pam->authctxt->pam = NULL;
442 pam->authctxt = NULL;
443 }
444
445 if (pam->h == NULL)
446 return;
447
448 /*
449 * We're in fatal_cleanup() or not in userauth or without a
450 * channel -- can't converse now, too bad.
451 */
452 pam_retval = pam_set_item(pam->h, PAM_CONV, NULL);
453 if (pam_retval != PAM_SUCCESS) {
454 log("Cannot remove PAM conv, close session or delete creds[%d]: %.200s",
455 pam_retval, PAM_STRERROR(pam->h, pam_retval));
456 goto cleanup;
457 }
458
459 if (pam->state & PAM_S_DONE_OPEN_SESSION) {
460 pam_retval = pam_close_session(pam->h, 0);
461 if (pam_retval != PAM_SUCCESS)
462 log("Cannot close PAM session[%d]: %.200s",
463 pam_retval, PAM_STRERROR(pam->h, pam_retval));
464 }
465
466 if (pam->state & PAM_S_DONE_SETCRED) {
467 pam_retval = pam_setcred(pam->h, PAM_DELETE_CRED);
468 if (pam_retval != PAM_SUCCESS)
469 debug("Cannot delete credentials[%d]: %.200s",
470 pam_retval, PAM_STRERROR(pam->h, pam_retval));
471 }
472
473 cleanup:
474
475 /* Use the previous PAM result, if not PAM_SUCCESS for pam_end() */
476 if (pam->last_pam_retval != PAM_SUCCESS)
477 pam_retval = pam_end(pam->h, pam->last_pam_retval);
478 else if (pam_retval != PAM_SUCCESS)
479 pam_retval = pam_end(pam->h, pam_retval);
480 else
481 pam_retval = pam_end(pam->h, PAM_ABORT);
482
483 if (pam_retval != PAM_SUCCESS)
484 log("Cannot release PAM authentication[%d]: %.200s",
485 pam_retval, PAM_STRERROR(pam->h, pam_retval));
486
487 xfree(pam);
488 }
489
490 /* Attempt password authentation using PAM */
491 int
auth_pam_password(Authctxt * authctxt,const char * password)492 auth_pam_password(Authctxt *authctxt, const char *password)
493 {
494 int retval;
495
496 /* Ensure we have a fresh PAM handle / state */
497 new_start_pam(authctxt, &conv);
498
499 retval = pam_set_item(authctxt->pam->h, PAM_AUTHTOK, password);
500 if (retval != PAM_SUCCESS) {
501 authctxt->pam->last_pam_retval = retval;
502 return 1;
503 }
504
505 retval = pam_authenticate(authctxt->pam->h,
506 options.permit_empty_passwd ? 0 :
507 PAM_DISALLOW_NULL_AUTHTOK);
508
509 if (retval != PAM_SUCCESS) {
510 authctxt->pam->last_pam_retval = retval;
511 return 0;
512 }
513
514 if ((retval = finish_userauth_do_pam(authctxt)) != PAM_SUCCESS)
515 return 0;
516
517 if (authctxt->method)
518 authctxt->method->authenticated = 1; /* SSHv2 */
519
520 return 1;
521 }
522
523 int
do_pam_non_initial_userauth(Authctxt * authctxt)524 do_pam_non_initial_userauth(Authctxt *authctxt)
525 {
526 new_start_pam(authctxt, NULL);
527 return (finish_userauth_do_pam(authctxt) == PAM_SUCCESS);
528 }
529
530 /* Cleanly shutdown PAM */
finish_pam(Authctxt * authctxt)531 void finish_pam(Authctxt *authctxt)
532 {
533 fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
534 do_pam_cleanup_proc(authctxt->pam);
535 }
536
537 static
538 char **
find_env(char ** env,char * var)539 find_env(char **env, char *var)
540 {
541 char **p;
542 int len;
543
544 if (strchr(var, '=') == NULL)
545 len = strlen(var);
546 else
547 len = (strchr(var, '=') - var) + 1;
548
549 for ( p = env ; p != NULL && *p != NULL ; p++ ) {
550 if (strncmp(*p, var, len) == 0)
551 return (p);
552 }
553
554 return (NULL);
555 }
556
557 /* Return list of PAM environment strings */
558 char **
fetch_pam_environment(Authctxt * authctxt)559 fetch_pam_environment(Authctxt *authctxt)
560 {
561 #ifdef HAVE_PAM_GETENVLIST
562 char **penv;
563
564 if (authctxt == NULL || authctxt->pam == NULL ||
565 authctxt->pam->h == NULL)
566 return (NULL);
567
568 penv = pam_getenvlist(authctxt->pam->h);
569
570 return (penv);
571 #else /* HAVE_PAM_GETENVLIST */
572 return(NULL);
573 #endif /* HAVE_PAM_GETENVLIST */
574 }
575
free_pam_environment(char ** env)576 void free_pam_environment(char **env)
577 {
578 int i;
579
580 if (env != NULL) {
581 for (i = 0; env[i] != NULL; i++)
582 xfree(env[i]);
583 }
584
585 xfree(env);
586 }
587
588 /* Print any messages that have been generated during authentication */
589 /* or account checking to stderr */
print_pam_messages(void)590 void print_pam_messages(void)
591 {
592 if (__pam_msg != NULL)
593 (void) fputs(__pam_msg, stderr);
594 }
595
596 /* Append a message to buffer */
message_cat(char ** p,const char * a)597 void message_cat(char **p, const char *a)
598 {
599 char *cp;
600 size_t new_len;
601
602 new_len = strlen(a);
603
604 if (*p) {
605 size_t len = strlen(*p);
606
607 *p = xrealloc(*p, new_len + len + 2);
608 cp = *p + len;
609 } else
610 *p = cp = xmalloc(new_len + 2);
611
612 (void) memcpy(cp, a, new_len);
613 cp[new_len] = '\n';
614 cp[new_len + 1] = '\0';
615 }
616
617 #endif /* USE_PAM */
618