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 char *__pam_msg = NULL;
72
73 static
74 char *
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 *
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
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
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 if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) {
343 retval = pam_open_session(authctxt->pam->h, 0);
344 authctxt->pam->last_pam_retval = retval;
345 if (retval != PAM_SUCCESS)
346 return retval;
347 authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION;
348 }
349
350 /*
351 * All PAM work done successfully.
352 *
353 * PAM handle stays around so we can call pam_close_session() on
354 * it later.
355 */
356 return PAM_SUCCESS;
357 }
358
359 /*
360 * PAM conversation function for non-interactive userauth methods that
361 * really cannot do any prompting. Password userauth and CHANGEREQ can
362 * always set the PAM_AUTHTOK and PAM_OLDAUTHTOK items to avoid
363 * conversation (and if they do and nonetheless some module tries to
364 * converse, then password userauth / CHANGEREQ MUST fail).
365 *
366 * Except, PAM_TEXT_INFO and PAM_ERROR_MSG prompts can be squirelled
367 * away and shown to the user later.
368 *
369 * Keyboard-interactive userauth has its own much more interesting
370 * conversation function.
371 *
372 */
373 static int
374 do_pam_conversation(int num_msg, const struct pam_message **msg,
375 struct pam_response **resp, void *appdata_ptr)
376 {
377 struct pam_response *reply;
378 int count;
379
380 /* PAM will free this later */
381 reply = xmalloc(num_msg * sizeof(*reply));
382
383 (void) memset(reply, 0, num_msg * sizeof(*reply));
384
385 for (count = 0; count < num_msg; count++) {
386 /*
387 * We can't use stdio yet, queue messages for
388 * printing later
389 */
390 switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
391 case PAM_PROMPT_ECHO_ON:
392 xfree(reply);
393 return PAM_CONV_ERR;
394 case PAM_PROMPT_ECHO_OFF:
395 xfree(reply);
396 return PAM_CONV_ERR;
397 break;
398 case PAM_ERROR_MSG:
399 case PAM_TEXT_INFO:
400 if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
401 message_cat(&__pam_msg,
402 PAM_MSG_MEMBER(msg, count, msg));
403 }
404 reply[count].resp = xstrdup("");
405 reply[count].resp_retcode = PAM_SUCCESS;
406 break;
407 default:
408 xfree(reply);
409 return PAM_CONV_ERR;
410 }
411 }
412
413 *resp = reply;
414
415 return PAM_SUCCESS;
416 }
417
418 /* Called at exit to cleanly shutdown PAM */
419 static void
420 do_pam_cleanup_proc(void *context)
421 {
422 int pam_retval;
423 pam_stuff *pam = (pam_stuff *) context;
424
425 if (pam == NULL)
426 return;
427
428 if (pam->authctxt != NULL && pam->authctxt->pam == pam) {
429 pam->authctxt->pam_retval = pam->last_pam_retval;
430 pam->authctxt->pam = NULL;
431 pam->authctxt = NULL;
432 }
433
434 if (pam->h == NULL)
435 return;
436
437 /*
438 * We're in fatal_cleanup() or not in userauth or without a
439 * channel -- can't converse now, too bad.
440 */
441 pam_retval = pam_set_item(pam->h, PAM_CONV, NULL);
442 if (pam_retval != PAM_SUCCESS) {
443 log("Cannot remove PAM conv, close session or delete creds[%d]: %.200s",
444 pam_retval, PAM_STRERROR(pam->h, pam_retval));
445 goto cleanup;
446 }
447
448 if (pam->state & PAM_S_DONE_OPEN_SESSION) {
449 pam_retval = pam_close_session(pam->h, 0);
450 if (pam_retval != PAM_SUCCESS)
451 log("Cannot close PAM session[%d]: %.200s",
452 pam_retval, PAM_STRERROR(pam->h, pam_retval));
453 }
454
455 if (pam->state & PAM_S_DONE_SETCRED) {
456 pam_retval = pam_setcred(pam->h, PAM_DELETE_CRED);
457 if (pam_retval != PAM_SUCCESS)
458 debug("Cannot delete credentials[%d]: %.200s",
459 pam_retval, PAM_STRERROR(pam->h, pam_retval));
460 }
461
462 cleanup:
463
464 /* Use the previous PAM result, if not PAM_SUCCESS for pam_end() */
465 if (pam->last_pam_retval != PAM_SUCCESS)
466 pam_retval = pam_end(pam->h, pam->last_pam_retval);
467 else if (pam_retval != PAM_SUCCESS)
468 pam_retval = pam_end(pam->h, pam_retval);
469 else
470 pam_retval = pam_end(pam->h, PAM_ABORT);
471
472 if (pam_retval != PAM_SUCCESS)
473 log("Cannot release PAM authentication[%d]: %.200s",
474 pam_retval, PAM_STRERROR(pam->h, pam_retval));
475
476 xfree(pam);
477 }
478
479 /* Attempt password authentation using PAM */
480 int
481 auth_pam_password(Authctxt *authctxt, const char *password)
482 {
483 int retval;
484
485 /* Ensure we have a fresh PAM handle / state */
486 new_start_pam(authctxt, &conv);
487
488 retval = pam_set_item(authctxt->pam->h, PAM_AUTHTOK, password);
489 if (retval != PAM_SUCCESS) {
490 authctxt->pam->last_pam_retval = retval;
491 return 1;
492 }
493
494 retval = pam_authenticate(authctxt->pam->h,
495 options.permit_empty_passwd ? 0 :
496 PAM_DISALLOW_NULL_AUTHTOK);
497
498 if (retval != PAM_SUCCESS) {
499 authctxt->pam->last_pam_retval = retval;
500 return 0;
501 }
502
503 if ((retval = finish_userauth_do_pam(authctxt)) != PAM_SUCCESS)
504 return 0;
505
506 if (authctxt->method)
507 authctxt->method->authenticated = 1; /* SSHv2 */
508
509 return 1;
510 }
511
512 int
513 do_pam_non_initial_userauth(Authctxt *authctxt)
514 {
515 new_start_pam(authctxt, &conv);
516 return (finish_userauth_do_pam(authctxt) == PAM_SUCCESS);
517 }
518
519 /* Cleanly shutdown PAM */
520 void finish_pam(Authctxt *authctxt)
521 {
522 fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
523 do_pam_cleanup_proc(authctxt->pam);
524 }
525
526 static
527 char **
528 find_env(char **env, char *var)
529 {
530 char **p;
531 int len;
532
533 if (strchr(var, '=') == NULL)
534 len = strlen(var);
535 else
536 len = (strchr(var, '=') - var) + 1;
537
538 for ( p = env ; p != NULL && *p != NULL ; p++ ) {
539 if (strncmp(*p, var, len) == 0)
540 return (p);
541 }
542
543 return (NULL);
544 }
545
546 /* Return list of PAM environment strings */
547 char **
548 fetch_pam_environment(Authctxt *authctxt)
549 {
550 #ifdef HAVE_PAM_GETENVLIST
551 char **penv;
552
553 if (authctxt == NULL || authctxt->pam == NULL ||
554 authctxt->pam->h == NULL)
555 return (NULL);
556
557 penv = pam_getenvlist(authctxt->pam->h);
558
559 return (penv);
560 #else /* HAVE_PAM_GETENVLIST */
561 return(NULL);
562 #endif /* HAVE_PAM_GETENVLIST */
563 }
564
565 void free_pam_environment(char **env)
566 {
567 int i;
568
569 if (env != NULL) {
570 for (i = 0; env[i] != NULL; i++)
571 xfree(env[i]);
572 }
573
574 xfree(env);
575 }
576
577 /* Print any messages that have been generated during authentication */
578 /* or account checking to stderr */
579 void print_pam_messages(void)
580 {
581 if (__pam_msg != NULL)
582 (void) fputs(__pam_msg, stderr);
583 }
584
585 /* Append a message to buffer */
586 void message_cat(char **p, const char *a)
587 {
588 char *cp;
589 size_t new_len;
590
591 new_len = strlen(a);
592
593 if (*p) {
594 size_t len = strlen(*p);
595
596 *p = xrealloc(*p, new_len + len + 2);
597 cp = *p + len;
598 } else
599 *p = cp = xmalloc(new_len + 2);
600
601 (void) memcpy(cp, a, new_len);
602 cp[new_len] = '\n';
603 cp[new_len + 1] = '\0';
604 }
605
606 #endif /* USE_PAM */