~ubuntu-branches/ubuntu/natty/libpam-ssh/natty

« back to all changes in this revision

Viewing changes to pam_ssh.c

  • Committer: Bazaar Package Importer
  • Author(s): Jens Peter Secher
  • Date: 2010-02-27 12:09:50 UTC
  • mfrom: (8.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100227120950-psfy4c146v72ijd3
* Use pam-auth-update together with the new setup file
  /usr/share/pam-configs/silent-ssh-single-sign-on to automatically
  enable the traditional functionality.  Please send additional profiles
  if you think that such profiles should be included in this package.  
  (Closes: #383950, #566723)
* Fixed manpage warning.
* Bumped the Standards-Version to 3.8.4, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
#endif
56
56
 
57
57
#include <errno.h>
 
58
#include <dirent.h>
58
59
#include <fcntl.h>
 
60
#include <fnmatch.h>
59
61
#include <pwd.h>
60
62
#include <signal.h>
61
63
#include <stdio.h>
 
64
#include <syslog.h>
62
65
#include <stdlib.h>
63
66
#include <string.h>
64
67
#include <sysexits.h>
102
105
 
103
106
#define MODULE_NAME                     "pam_ssh"
104
107
#define NEED_PASSPHRASE                 "SSH passphrase: "
105
 
#define DEF_KEYFILES                    "id_dsa,id_rsa,identity"
106
108
#define ENV_PID_SUFFIX                  "_AGENT_PID"
107
109
#define ENV_SOCKET_SUFFIX               "_AUTH_SOCK"
108
 
#define PAM_OPT_KEYFILES_NAME           "keyfiles"
109
110
#define PAM_OPT_BLANK_PASSPHRASE_NAME   "allow_blank_passphrase"
110
 
#define SEP_KEYFILES                    ","
111
 
#define SSH_CLIENT_DIR                  ".ssh"
 
111
#define SSH_DIR                         ".ssh"
 
112
#define SSH_LOGIN_KEYS_DIR              "login-keys.d"
112
113
 
113
114
enum {
114
 
#if HAVE_OPENPAM || HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
115
 
        PAM_OPT_KEYFILES = PAM_OPT_STD_MAX,
116
 
        PAM_OPT_BLANK_PASSPHRASE
117
 
#else
118
 
        PAM_OPT_KEYFILES,
119
 
        PAM_OPT_BLANK_PASSPHRASE
120
 
#endif
 
115
        /* The first option after the standard options. */
 
116
        PAM_OPT_BLANK_PASSPHRASE = 8
121
117
};
122
118
 
123
119
static struct opttab other_options[] = {
124
 
        { PAM_OPT_KEYFILES_NAME,                PAM_OPT_KEYFILES },
125
120
        { PAM_OPT_BLANK_PASSPHRASE_NAME,        PAM_OPT_BLANK_PASSPHRASE },
126
121
        { NULL, 0 }
127
122
};
203
198
 
204
199
static Key *
205
200
key_load_private_maybe(const char *path, const char *passphrase,
206
 
    char **commentp, int allow_blank)
 
201
                       char **commentp, int allow_blank)
207
202
{
208
 
        Key *key;
209
 
 
210
 
        /* try loading the key with a blank passphrase */
211
 
        key = key_load_private(path, "", commentp);
212
 
        if (key)
213
 
                return allow_blank && *passphrase == '\0' ? key : NULL;
214
 
 
215
 
        /* the private key's passphrase isn't blank */
216
 
        return key_load_private(path, passphrase, commentp);
 
203
        Key *key;
 
204
 
 
205
        /* try loading the key with a blank passphrase */
 
206
        key = key_load_private(path, "", commentp);
 
207
        if (key)
 
208
                return allow_blank && *passphrase == '\0' ? key : NULL;
 
209
 
 
210
        /* the private key's passphrase isn't blank */
 
211
        return key_load_private(path, passphrase, commentp);
217
212
}
218
213
 
219
214
/*
225
220
 */
226
221
 
227
222
static int
228
 
auth_via_key(pam_handle_t *pamh, const char *file, const char *dir,
229
 
    const struct passwd *user, const char *pass, int allow_blank)
 
223
auth_via_key(pam_handle_t *pamh, const char *path, const char *name,
 
224
             const char *pass, int allow_blank)
230
225
{
231
226
        char *comment;          /* private key comment */
232
227
        char *data_name;        /* PAM state */
233
228
        static int index = 0;   /* for saved keys */
234
229
        Key *key;               /* user's key */
235
 
        char *path;             /* to key files */
236
230
        int retval;             /* from calls */
237
231
 
238
232
        /* an int only goes so far */
240
234
        if (index < 0)
241
235
                return PAM_SERVICE_ERR;
242
236
          
243
 
        /* locate the user's private key file */
244
 
 
245
 
        if (asprintf(&path, "%s/%s", dir, file) == -1) {
246
 
                pam_ssh_log(LOG_CRIT, "out of memory");
247
 
                return PAM_SERVICE_ERR;
248
 
        }
249
 
 
250
237
        /* Try to decrypt the private key with the passphrase provided.  If
251
238
           success, the user is authenticated. */
252
239
 
253
240
        comment = NULL;
254
241
        key = key_load_private_maybe(path, pass, &comment, allow_blank);
255
 
        free(path);
256
 
        if (!comment && !(comment = strdup(file))) {
 
242
        if (!comment && !(comment = strdup(name))) {
257
243
                pam_ssh_log(LOG_CRIT, "out of memory");
258
244
                return PAM_SERVICE_ERR;
259
245
        }
263
249
        }
264
250
 
265
251
        /* save the key and comment to pass to ssh-agent in the session
266
 
           phase */
 
252
           phase */
267
253
 
268
254
        if (asprintf(&data_name, "ssh_private_key_%d", index) == -1) {
269
255
                free(comment);
300
286
 */
301
287
 
302
288
static int
303
 
add_keys(pam_handle_t *pamh, char *socket)
 
289
add_keys(pam_handle_t *pamh, AuthenticationConnection *ac)
304
290
{
305
 
        AuthenticationConnection *ac;   /* connection to ssh-agent */
306
291
        char *comment;                  /* private key comment */
307
292
        char *data_name;                /* PAM state */
308
293
        int final;                      /* final return value */
310
295
        Key *key;                       /* user's private key */
311
296
        int retval;                     /* from calls */
312
297
 
313
 
        /* connect to the agent */
314
 
 
315
 
        if (!(ac = ssh_get_authentication_connection(socket))) {
316
 
                pam_ssh_log(LOG_ERR, "%s: %m", socket);
317
 
                return PAM_SESSION_ERR;
318
 
        }
319
 
 
320
298
        /* hand off each private key to the agent */
321
299
 
322
300
        final = 0;
327
305
                        return PAM_SERVICE_ERR;
328
306
                }
329
307
                retval = pam_get_data(pamh, data_name,
330
 
                    (const void **)(void *)&key);
 
308
                                      (const void **)(void *)&key);
331
309
                free(data_name);
332
310
                if (retval != PAM_SUCCESS)
333
311
                        break;
337
315
                        return PAM_SERVICE_ERR;
338
316
                }
339
317
                retval = pam_get_data(pamh, data_name,
340
 
                    (const void **)(void *)&comment);
 
318
                                      (const void **)(void *)&comment);
341
319
                free(data_name);
342
320
                if (retval != PAM_SUCCESS)
343
321
                        break;
345
323
                if (!final)
346
324
                        final = retval;
347
325
        }
348
 
        ssh_close_authentication_connection(ac);
349
326
 
350
327
        return final ? PAM_SUCCESS : PAM_SESSION_ERR;
351
328
}
352
329
 
 
330
static int
 
331
start_ssh_agent(pam_handle_t *pamh, uid_t uid, FILE **env_read)
 
332
{
 
333
        pid_t child_pid;                /* child process that spawns agent */
 
334
        int child_pipe[2];              /* pipe to child process */
 
335
        int child_status;               /* child process status */
 
336
        char *arg[3], *env[1];          /* to pass to execve() */
 
337
 
 
338
        if (pipe(child_pipe) < 0) {
 
339
                pam_ssh_log(LOG_ERR, "pipe: %m");
 
340
                return PAM_SERVICE_ERR;
 
341
        }
 
342
        switch (child_pid = fork()) {
 
343
        case -1:        /* error */
 
344
                pam_ssh_log(LOG_ERR, "fork: %m");
 
345
                close(child_pipe[0]);
 
346
                close(child_pipe[1]);
 
347
                return PAM_SERVICE_ERR;
 
348
                /* NOTREACHED */
 
349
        case 0:         /* child */
 
350
 
 
351
                /* Permanently drop privileges using setuid()
 
352
                         before executing ssh-agent so that root
 
353
                         privileges can't possibly be regained (some
 
354
                         ssh-agents insist that euid == ruid
 
355
                         anyway).  System V won't let us use
 
356
                         setuid() unless euid == 0, so we
 
357
                         temporarily regain root privileges first
 
358
                         with openpam_restore_cred() (which calls
 
359
                         seteuid()). */
 
360
 
 
361
                switch (openpam_restore_cred(pamh)) {
 
362
                case PAM_SYSTEM_ERR:
 
363
                        pam_ssh_log(LOG_ERR,
 
364
                                    "can't restore privileges: %m");
 
365
                        _exit(EX_OSERR);
 
366
                        /* NOTREACHED */
 
367
                case PAM_SUCCESS:
 
368
                        if (setuid(uid) == -1) {
 
369
                                pam_ssh_log(LOG_ERR,
 
370
                                            "can't drop privileges: %m",
 
371
                                            uid);
 
372
                                _exit(EX_NOPERM);
 
373
                        }
 
374
                        break;
 
375
                }
 
376
 
 
377
                if (close(child_pipe[0]) == -1) {
 
378
                        pam_ssh_log(LOG_ERR, "close: %m");
 
379
                        _exit(EX_OSERR);
 
380
                }
 
381
                if (child_pipe[1] != STDOUT_FILENO) {
 
382
                        if (dup2(child_pipe[1], STDOUT_FILENO) == -1) {
 
383
                                pam_ssh_log(LOG_ERR, "dup: %m");
 
384
                                _exit(EX_OSERR);
 
385
                        }
 
386
                        if (close(child_pipe[1]) == -1) {
 
387
                                pam_ssh_log(LOG_ERR, "close: %m");
 
388
                                _exit(EX_OSERR);
 
389
                        }
 
390
                }
 
391
                arg[0] = "ssh-agent";
 
392
                arg[1] = "-s";
 
393
                arg[2] = NULL;
 
394
                env[0] = NULL;
 
395
                debug("Starting ssh-agent.");
 
396
                execve(PATH_SSH_AGENT, arg, env);
 
397
                pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
 
398
                _exit(127);
 
399
                /* NOTREACHED */
 
400
        }
 
401
        if (close(child_pipe[1]) == -1) {
 
402
                pam_ssh_log(LOG_ERR, "close: %m");
 
403
                return PAM_SESSION_ERR;
 
404
        }
 
405
        if (!(*env_read = fdopen(child_pipe[0], "r"))) {
 
406
                pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
 
407
                return PAM_SESSION_ERR;
 
408
        }
 
409
 
 
410
        child_status = 0;
 
411
        if (waitpid_intr(child_pid, &child_status, 0) == -1 &&
 
412
                        errno != ECHILD) {
 
413
                pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
 
414
                return PAM_SESSION_ERR;
 
415
        }
 
416
 
 
417
        if (child_status != 0) {
 
418
                if (WIFSIGNALED(child_status))
 
419
                        pam_ssh_log(LOG_ERR, "%s exited on signal %d",
 
420
                                                                        PATH_SSH_AGENT, WTERMSIG(child_status));
 
421
                else
 
422
                        if (WEXITSTATUS(child_status) == 127)
 
423
                                pam_ssh_log(LOG_ERR,
 
424
                                            "cannot execute %s",
 
425
                                            PATH_SSH_AGENT);
 
426
                        else
 
427
                                pam_ssh_log(LOG_ERR,
 
428
                                            "%s exited with status %d",
 
429
                                            PATH_SSH_AGENT,
 
430
                                            WEXITSTATUS(child_status));
 
431
                return PAM_SESSION_ERR;
 
432
        }
 
433
 
 
434
        return PAM_SUCCESS;
 
435
}
 
436
 
 
437
static int
 
438
read_write_agent_env(pam_handle_t *pamh,
 
439
                     FILE *env_read,
 
440
                     int env_write,
 
441
                     char **agent_socket)
 
442
{
 
443
        char *agent_pid;                /* copy of agent PID */
 
444
        char *env_end;                  /* end of env */
 
445
        char env_string[BUFSIZ];        /* environment string */
 
446
        char *env_value;                /* envariable value */
 
447
        int retval;                     /* from calls */
 
448
 
 
449
        while (fgets(env_string, sizeof env_string, env_read)) {
 
450
 
 
451
                /* parse environment definitions */
 
452
 
 
453
                if (env_write >= 0)
 
454
                        write(env_write, env_string, strlen(env_string));
 
455
                if (!(env_value = strchr(env_string, '=')) ||
 
456
                    !(env_end = strchr(env_value, ';')))
 
457
                        continue;
 
458
                *env_end = '\0';
 
459
 
 
460
                /* pass to the application */
 
461
 
 
462
                if ((retval = pam_putenv(pamh, env_string)) != PAM_SUCCESS)
 
463
                        return retval;
 
464
 
 
465
                *env_value++ = '\0';
 
466
 
 
467
                /* save the agent socket so we can connect to it and add
 
468
                   the keys as well as the PID so we can kill the agent on
 
469
                   session close. */
 
470
 
 
471
                agent_pid = NULL;
 
472
                if (strcmp(&env_string[strlen(env_string) -
 
473
                    strlen(ENV_SOCKET_SUFFIX)], ENV_SOCKET_SUFFIX) == 0 &&
 
474
                    !(*agent_socket = strdup(env_value))) {
 
475
                        pam_ssh_log(LOG_CRIT, "out of memory");
 
476
                        return PAM_SERVICE_ERR;
 
477
                } else if (strcmp(&env_string[strlen(env_string) -
 
478
                    strlen(ENV_PID_SUFFIX)], ENV_PID_SUFFIX) == 0 &&
 
479
                    (!(agent_pid = strdup(env_value)) ||
 
480
                    (retval = pam_set_data(pamh, "ssh_agent_pid",
 
481
                    agent_pid, ssh_cleanup)) != PAM_SUCCESS)) {
 
482
                        if (agent_pid)
 
483
                                free(agent_pid);
 
484
                        else {
 
485
                                pam_ssh_log(LOG_CRIT, "out of memory");
 
486
                                return PAM_SERVICE_ERR;
 
487
                        }
 
488
                        if (agent_socket)
 
489
                                free(agent_socket);
 
490
                        return retval;
 
491
                }
 
492
 
 
493
        }
 
494
 
 
495
        return PAM_SUCCESS;
 
496
}
 
497
 
 
498
 
 
499
static void
 
500
unlock_standard_keys(pam_handle_t *pamh, const char *pass, const char *dotdir,
 
501
                     int allow_blank)
 
502
{
 
503
        char *files[] = {"id_dsa", "id_rsa", "identity", NULL};
 
504
        int i;
 
505
        char *path;
 
506
        if (0 == pass) {
 
507
                debug("No preceding password.");
 
508
                return;
 
509
        }
 
510
        debug("Looking for SSH keys in '%s'.",dotdir);
 
511
        for (i = 0; files[i]; ++i) {
 
512
                debug2("SSH key candidate '%s'.", files[i]);
 
513
                /* Locate the user's private key file. */
 
514
                if (asprintf(&path, "%s/%s", dotdir, files[i]) == -1) {
 
515
                        pam_ssh_log(LOG_CRIT, "out of memory");
 
516
                        return;
 
517
                }
 
518
                if (PAM_SUCCESS == auth_via_key(pamh, path, files[i], pass,
 
519
                                                allow_blank)) {
 
520
                        debug("SSH key '%s' decrypted.", files[i]);
 
521
                } else {
 
522
                        debug2("SSH key candidate '%s' failed.", files[i]);
 
523
                }
 
524
                free(path);
 
525
        }
 
526
}
 
527
 
 
528
 
 
529
static int
 
530
unlock_at_least_one_key(pam_handle_t *pamh, const char *pass, const char *dotdir,
 
531
                        struct dirent **namelist, int n, int allow_blank)
 
532
{
 
533
        struct stat buf;
 
534
        const char * file;
 
535
        char *path;
 
536
        int result = PAM_AUTH_ERR;
 
537
        if (0 == pass) {
 
538
                debug("No preceding password.");
 
539
                return PAM_AUTH_ERR;
 
540
        }
 
541
        debug("Looking for SSH keys in '%s'.",dotdir);
 
542
        /* Any key will authenticate us, but if we can decrypt all of the
 
543
           specified keys, we will do so here so we can cache them in the
 
544
           session phase. */
 
545
        while (n--) {
 
546
                file = namelist[n]->d_name;
 
547
                debug2("SSH login key candidate '%s'.", file);
 
548
                /* Locate the user's private key file. */
 
549
                if (asprintf(&path, "%s/%s", dotdir, file) == -1) {
 
550
                        pam_ssh_log(LOG_CRIT, "out of memory");
 
551
                        return PAM_SERVICE_ERR;
 
552
                }
 
553
                /* Discard non-regular/non-linked files. */
 
554
                if (stat(path, &buf) == -1) {
 
555
                        debug("Could not stat '%s'", file);
 
556
                } else if (!S_ISREG(buf.st_mode) && !S_ISLNK(buf.st_mode) ) {
 
557
                        debug2("'%s' is not a link or a regular file", file);
 
558
                } else if (PAM_SUCCESS == auth_via_key(pamh, path, file, pass,
 
559
                                                       allow_blank)) {
 
560
                        debug("SSH key '%s' decrypted.", file);
 
561
                        result = PAM_SUCCESS;
 
562
                } else {
 
563
                        debug("SSH key candidate '%s' failed.", file);
 
564
                }
 
565
                free(path);
 
566
        }
 
567
        return result;
 
568
}
 
569
 
 
570
 
 
571
#define CLEANUP_AND_RETURN(retcode) \
 
572
        while (n--) free(namelist[n]); \
 
573
        free(namelist); \
 
574
        free(dotdir); \
 
575
        free(logindir); \
 
576
        openpam_restore_cred(pamh); \
 
577
        return retcode
 
578
 
353
579
 
354
580
PAM_EXTERN int
355
581
pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc,
356
 
    const char **argv)
 
582
                    const char **argv)
357
583
{
358
584
        int allow_blank_passphrase;     /* allow blank passphrases? */
359
 
        int authenticated;              /* user authenticated? */
360
585
        char *dotdir;                   /* .ssh dir name */
361
 
        char *file;                     /* current key file */
362
 
        char *keyfiles;                 /* list of key files to add */
363
 
#if HAVE_OPENPAM
364
 
        const char *kfspec;             /* list of key files to add */
365
 
#elif HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
366
 
        char *kfspec;                   /* list of key files to add */
 
586
        char *logindir;                 /* login-key dir name */
 
587
        int n;                          /* count of ssh keys */
 
588
        struct dirent **namelist;       /* ssh keys */
 
589
#       if HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
367
590
        struct options options;         /* options for pam_get_pass() */
368
 
#else
369
 
        char *kfspec;                   /* list of key files to add */
 
591
#       else
370
592
        int options;                    /* options for pam_get_pass() */
371
 
#endif
 
593
#       endif
372
594
        const char *pass;               /* passphrase */
373
595
        const struct passwd *pwent;     /* user's passwd entry */
374
 
        struct passwd *pwent_keep;      /* our own copy */
375
596
        int retval;                     /* from calls */
376
597
        const char *user;               /* username */
377
598
 
378
 
        log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
379
 
 
 
599
        dotdir = logindir = NULL;
380
600
        allow_blank_passphrase = 0;
381
 
        keyfiles = kfspec = NULL;
382
 
#if HAVE_OPENPAM
383
 
        if ((kfspec = openpam_get_option(pamh, PAM_OPT_KEYFILES_NAME))) {
384
 
                if (!(kfspec = opt_arg(kfspec))) {
385
 
                        openpam_log(PAM_LOG_ERROR, "invalid keyfile list");
386
 
                        return PAM_SERVICE_ERR;
387
 
                }
388
 
        } else
389
 
                kfspec = DEF_KEYFILES;
390
 
        if ((kfspec = openpam_get_option(pamh, PAM_OPT_BLANK_PASSPHRASE)))
 
601
#       if HAVE_OPENPAM
 
602
        log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
 
603
        if (openpam_get_option(pamh, PAM_OPT_BLANK_PASSPHRASE))
391
604
                allow_blank_passphrase = 1;
392
 
#elif HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
 
605
#       elif HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
393
606
        memset(&options, 0, sizeof options);
394
607
        pam_std_option(&options, other_options, argc, argv);
395
 
        if (!pam_test_option(&options, PAM_OPT_KEYFILES, &kfspec))
396
 
                kfspec = DEF_KEYFILES;
 
608
        /* Set LOG level to DEBUG if 'debug' option is given to module. */
 
609
        if (pam_test_option(&options, PAM_OPT_DEBUG, NULL)) {
 
610
                log_init(MODULE_NAME, SYSLOG_LEVEL_DEBUG1,
 
611
                         SYSLOG_FACILITY_AUTHPRIV, 0);
 
612
                debug("Authentication debugging.");
 
613
        } else
 
614
                log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR,
 
615
                         SYSLOG_FACILITY_AUTHPRIV, 0);
397
616
        allow_blank_passphrase =
398
617
                pam_test_option(&options, PAM_OPT_BLANK_PASSPHRASE, NULL);
399
 
#else
 
618
#       else
 
619
        log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
400
620
        options = 0;
401
621
        for (; argc; argc--, argv++) {
402
622
                struct opttab *p;
405
625
                        if (strcmp(*argv, p->name) != 0)
406
626
                                continue;
407
627
                        switch (p->value) {
408
 
                        PAM_OPT_KEYFILES:
409
 
                                if (!(kfspec = opt_arg(*argv))) {
410
 
                                        pam_ssh_log(LOG_ERR,
411
 
                                            "invalid keyfile list");
412
 
                                        return PAM_SERVICE_ERR;
413
 
                                }
414
 
                                break;
415
628
                        PAM_OPT_BLANK_PASSPHRASE:
416
629
                                allow_blank_passphrase = 1;
417
630
                                break;
419
632
                }
420
633
                pam_std_option(&options, *argv);
421
634
        }
422
 
        if (!kfspec)
423
 
                kfspec = DEF_KEYFILES;
424
 
#endif
 
635
#       endif
425
636
 
426
637
        if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
427
638
                return retval;
428
639
        if (!(user && (pwent = getpwnam(user)) && pwent->pw_dir &&
429
 
            *pwent->pw_dir))
 
640
            *pwent->pw_dir)) {
 
641
                debug2("User unknown.");
 
642
                if (! pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL)) {
 
643
                        /* Asking for passphrase anyway to not leak
 
644
                           information. */
 
645
                        pam_conv_pass(pamh, NEED_PASSPHRASE, &options);
 
646
                }
 
647
                openpam_restore_cred(pamh);
430
648
                return PAM_AUTH_ERR;
 
649
        }
431
650
 
432
651
        retval = openpam_borrow_cred(pamh, pwent);
433
652
        if (retval != PAM_SUCCESS && retval != PAM_PERM_DENIED) {
435
654
                return retval;
436
655
        }
437
656
 
438
 
        /* pass prompt message to application and receive passphrase */
439
 
 
440
 
#if HAVE_OPENPAM
441
 
        retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, NEED_PASSPHRASE);
442
 
#elif HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
443
 
        retval = pam_get_pass(pamh, &pass, NEED_PASSPHRASE, &options);
444
 
#else
445
 
        retval = pam_get_pass(pamh, &pass, NEED_PASSPHRASE, options);
446
 
#endif
447
 
        if (retval != PAM_SUCCESS) {
448
 
                openpam_restore_cred(pamh);
449
 
                return retval;
450
 
        }
451
 
        if (!pass) {
452
 
                openpam_restore_cred(pamh);
453
 
                return PAM_AUTH_ERR;
 
657
        /* Locate SSH directory. */
 
658
        if (asprintf(&dotdir, "%s/%s", pwent->pw_dir, SSH_DIR) == -1) {
 
659
                pam_ssh_log(LOG_CRIT, "out of memory");
 
660
                openpam_restore_cred(pamh);
 
661
                return PAM_SERVICE_ERR;
 
662
        }
 
663
        /* Locate SSH login-keys directory. */
 
664
        if (asprintf(&logindir, "%s/%s", dotdir, SSH_LOGIN_KEYS_DIR) == -1) {
 
665
                pam_ssh_log(LOG_CRIT, "out of memory");
 
666
                openpam_restore_cred(pamh);
 
667
                return PAM_SERVICE_ERR;
 
668
        }
 
669
        namelist = NULL;
 
670
        n = scandir(logindir, &namelist, NULL, alphasort);
 
671
        if (-1 == n) {
 
672
                if (ENOMEM == errno) {
 
673
                        pam_ssh_log(LOG_CRIT, "out of memory");
 
674
                        openpam_restore_cred(pamh);
 
675
                        return PAM_SERVICE_ERR;
 
676
                } else {
 
677
                        debug("No SSH login-keys directory.");
 
678
                        n = 0;
 
679
                }
454
680
        }
455
681
 
456
682
        OpenSSL_add_all_algorithms(); /* required for DSA */
457
683
 
458
 
        /* any key will authenticate us, but if we can decrypt all of the
459
 
           specified keys, we'll do so here so we can cache them in the
460
 
           session phase */
461
 
 
462
 
        if (asprintf(&dotdir, "%s/%s", pwent->pw_dir, SSH_CLIENT_DIR) == -1) {
463
 
                pam_ssh_log(LOG_CRIT, "out of memory");
464
 
                openpam_restore_cred(pamh);
465
 
                return PAM_SERVICE_ERR;
466
 
        }
467
 
        authenticated = 0;
468
 
        if (!(keyfiles = strdup(kfspec))) {
469
 
                pam_ssh_log(LOG_CRIT, "out of memory");
470
 
                openpam_restore_cred(pamh);
471
 
                return PAM_SERVICE_ERR;
472
 
        }
473
 
        for (file = strtok(keyfiles, SEP_KEYFILES); file;
474
 
             file = strtok(NULL, SEP_KEYFILES))
475
 
                if (auth_via_key(pamh, file, dotdir, pwent, pass,
476
 
                    allow_blank_passphrase) == PAM_SUCCESS)
477
 
                        authenticated = 1;
478
 
        free(dotdir);
479
 
        free(keyfiles);
480
 
        if (!authenticated) {
481
 
                openpam_restore_cred(pamh);
482
 
                return PAM_AUTH_ERR;
483
 
        }
484
 
 
485
 
        /* copy the passwd entry (in case successive calls are made) and
486
 
           save it for the session phase */
487
 
 
488
 
        if (!(pwent_keep = malloc(sizeof *pwent))) {
489
 
                pam_ssh_log(LOG_CRIT, "out of memory");
490
 
                openpam_restore_cred(pamh);
491
 
                return PAM_SERVICE_ERR;
492
 
        }
493
 
        memcpy(pwent_keep, pwent, sizeof *pwent_keep);
494
 
        if ((retval = pam_set_data(pamh, "ssh_passwd_entry", pwent_keep,
495
 
            ssh_cleanup)) != PAM_SUCCESS) {
496
 
                free(pwent_keep);
497
 
                openpam_restore_cred(pamh);
498
 
                return retval;
499
 
        }
500
 
 
501
 
        openpam_restore_cred(pamh);
502
 
        return PAM_SUCCESS;
 
684
        /* Grab an already-entered password from a previous module if the user
 
685
           wants to use it for the SSH keys. */
 
686
        if (pam_test_option(&options, PAM_OPT_TRY_FIRST_PASS, NULL) ||
 
687
            pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL)) {
 
688
                debug("Grabbing password from preceding auth module.");
 
689
                retval = pam_get_item(pamh, PAM_AUTHTOK, (void**)&pass);
 
690
                if (retval != PAM_SUCCESS) {
 
691
                        debug("Could not grab password from preceding auth "
 
692
                              "module.");
 
693
                        CLEANUP_AND_RETURN(retval);
 
694
                }
 
695
        }
 
696
 
 
697
        /* If use_first_password option given, then go through all keys and
 
698
           unlock each that matches.  If none matches, fail, otherwise
 
699
           succeed. */
 
700
        if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL)) {
 
701
                debug("Using previous password for SSH keys.");
 
702
                /* Unlock any of the standard SSH keys with the password from the
 
703
                   previous PAM module. */
 
704
                unlock_standard_keys(pamh,pass,dotdir,allow_blank_passphrase);
 
705
                /* Fail if there are no SSH login keys. */
 
706
                if (0 == n) {
 
707
                        CLEANUP_AND_RETURN(PAM_AUTH_ERR);
 
708
                }
 
709
                retval = unlock_at_least_one_key(pamh,pass,logindir,namelist,n,
 
710
                                                 allow_blank_passphrase);
 
711
                CLEANUP_AND_RETURN(retval);
 
712
        }
 
713
 
 
714
        /* If try_first_password option given, then go through all keys and
 
715
           unlock each that matches.  If any matches, then succeed. */
 
716
        if (pam_test_option(&options, PAM_OPT_TRY_FIRST_PASS, NULL)) {
 
717
                debug("Trying previous password for SSH keys.");
 
718
                /* Unlock any of the standard SSH keys with the password from the
 
719
                   previous PAM module. */
 
720
                unlock_standard_keys(pamh,pass,dotdir,allow_blank_passphrase);
 
721
                /* Fail if there are no SSH login keys. */
 
722
                if (0 == n) {
 
723
                        CLEANUP_AND_RETURN(PAM_AUTH_ERR);
 
724
                }
 
725
                retval = unlock_at_least_one_key(pamh,pass,logindir,namelist,n,
 
726
                                                 allow_blank_passphrase);
 
727
                if (PAM_SUCCESS == retval) {
 
728
                        CLEANUP_AND_RETURN(PAM_SUCCESS);
 
729
                }
 
730
        }
 
731
 
 
732
        /* Either no option was given, or try_first_password was given but could
 
733
           not use the previous password, so we need to get a specific SSH key
 
734
           passphrase. */
 
735
        debug("Asking for SSH key passphrase.");
 
736
        retval = pam_conv_pass(pamh, NEED_PASSPHRASE, &options);
 
737
        if (retval != PAM_SUCCESS) {
 
738
                debug("Could not get SSH key passphrase.");
 
739
                CLEANUP_AND_RETURN(retval);
 
740
        }
 
741
        retval = pam_get_item(pamh, PAM_AUTHTOK, (void**)&pass);
 
742
        if (retval != PAM_SUCCESS) {
 
743
                debug("Could not obtain passphrase.");
 
744
                CLEANUP_AND_RETURN(retval);
 
745
        }
 
746
        /* Unlock any of the standard SSH keys with the password. */
 
747
        unlock_standard_keys(pamh,pass,dotdir,allow_blank_passphrase);
 
748
        /* Unlock login keys with the password. */
 
749
        retval = unlock_at_least_one_key(pamh,pass,logindir,namelist,n,
 
750
                                         allow_blank_passphrase);
 
751
        CLEANUP_AND_RETURN(retval);
503
752
}
504
753
 
505
754
 
506
755
PAM_EXTERN int
507
756
pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
508
 
    int argc __unused, const char **argv __unused)
 
757
               int argc __unused, const char **argv __unused)
509
758
{
510
759
        return PAM_SUCCESS;
511
760
}
513
762
 
514
763
PAM_EXTERN int
515
764
pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
516
 
    int argc __unused, const char **argv __unused)
 
765
                    int argc, const char **argv)
517
766
{
518
 
        char *agent_pid;                /* copy of agent PID */
 
767
        AuthenticationConnection *ac;   /* connection to ssh-agent */
519
768
        char *agent_socket;             /* agent socket */
520
 
        char *arg[3], *env[1];          /* to pass to execve() */
521
 
        pid_t child_pid;                /* child process that spawns agent */
522
 
        int child_pipe[2];              /* pipe to child process */
523
 
        int child_status;               /* child process status */
524
769
        char *cp;                       /* scratch */
525
 
        char *env_end;                  /* end of env */
526
770
        FILE *env_read;                 /* env data source */
527
 
        char env_string[BUFSIZ];        /* environment string */
528
 
        char *env_value;                /* envariable value */
529
771
        int env_write;                  /* env file descriptor */
530
772
        char hname[MAXHOSTNAMELEN];     /* local hostname */
531
773
        int no_link;                    /* link per-agent file? */
536
778
        int start_agent;                /* start agent? */
537
779
        const char *tty_raw;            /* raw tty or display name */
538
780
        char *tty_nodir;                /* tty without / chars */
 
781
        int attempt;                    /* No. of attempt to contact agent */
 
782
        const char *user;               /* username */
539
783
 
 
784
#       if HAVE_PAM_STRUCT_OPTIONS || !HAVE_PAM_STD_OPTION
 
785
        struct options options;
 
786
        memset(&options, 0, sizeof options);
 
787
        pam_std_option(&options, other_options, argc, argv);
 
788
        /* Set LOG level to DEBUG if 'debug' option given to module. */
 
789
        if (pam_test_option(&options, PAM_OPT_DEBUG, NULL)) {
 
790
                log_init(MODULE_NAME, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTHPRIV, 0);
 
791
                debug("Session debugging.");
 
792
        } else
 
793
                log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
 
794
#       else
540
795
        log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
 
796
#       endif
541
797
 
542
798
        /* dump output of ssh-agent in ~/.ssh */
543
 
        if ((retval = pam_get_data(pamh, "ssh_passwd_entry",
544
 
            (const void **)(void *)&pwent))
545
 
            != PAM_SUCCESS)
 
799
        if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
546
800
                return retval;
547
801
 
 
802
        if (!(user && (pwent = getpwnam(user)) && pwent->pw_dir && *pwent->pw_dir))
 
803
                return PAM_SESSION_ERR;
 
804
 
548
805
        retval = openpam_borrow_cred(pamh, pwent);
549
806
        if (retval != PAM_SUCCESS && retval != PAM_PERM_DENIED) {
550
807
                pam_ssh_log(LOG_ERR, "can't drop privileges: %m");
575
832
        }
576
833
 
577
834
        /* save the per-agent filename in case we want to delete it on
578
 
           session close */
 
835
           session close */
579
836
 
580
837
        if ((retval = pam_set_data(pamh, "ssh_agent_env_agent", per_agent,
581
838
            ssh_cleanup)) != PAM_SUCCESS) {
585
842
        }
586
843
 
587
844
        /* Try to create the per-agent file or open it for reading if it
588
 
           exists.  If we can't do either, we won't try to link a
589
 
           per-session filename later.  Start the agent if we can't open
 
845
           exists.  If we can't do either, we won't try to link a
 
846
           per-session filename later.  Start the agent if we can't open
590
847
           the file for reading. */
591
848
 
592
 
        env_write = child_pid = no_link = start_agent = 0;
593
 
        env_read = NULL;
594
 
        if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR))
595
 
            < 0 && !(env_read = fopen(per_agent, "r")))
596
 
                no_link = 1;
597
 
        if (!env_read) {
598
 
                start_agent = 1;
599
 
                if (pipe(child_pipe) < 0) {
600
 
                        pam_ssh_log(LOG_ERR, "pipe: %m");
601
 
                        close(env_write);
602
 
                        openpam_restore_cred(pamh);
603
 
                        return PAM_SERVICE_ERR;
604
 
                }
605
 
                switch (child_pid = fork()) {
606
 
                case -1:        /* error */
607
 
                        pam_ssh_log(LOG_ERR, "fork: %m");
608
 
                        close(child_pipe[0]);
609
 
                        close(child_pipe[1]);
610
 
                        close(env_write);
611
 
                        openpam_restore_cred(pamh);
612
 
                        return PAM_SERVICE_ERR;
613
 
                        /* NOTREACHED */
614
 
                case 0:         /* child */
615
 
 
616
 
                        /* Permanently drop privileges using setuid()
617
 
                           before executing ssh-agent so that root
618
 
                           privileges can't possibly be regained (some
619
 
                           ssh-agents insist that euid == ruid
620
 
                           anyway).  System V won't let us use
621
 
                           setuid() unless euid == 0, so we
622
 
                           temporarily regain root privileges first
623
 
                           with openpam_restore_cred() (which calls
624
 
                           seteuid()). */
625
 
 
626
 
                        switch (openpam_restore_cred(pamh)) {
627
 
                        case PAM_SYSTEM_ERR:
628
 
                                pam_ssh_log(LOG_ERR,
629
 
                                    "can't restore privileges: %m");
630
 
                                _exit(EX_OSERR);
631
 
                                /* NOTREACHED */
632
 
                        case PAM_SUCCESS:
633
 
                                if (setuid(pwent->pw_uid) == -1) {
634
 
                                        pam_ssh_log(LOG_ERR,
635
 
                                            "can't drop privileges: %m",
636
 
                                            pwent->pw_uid);
637
 
                                        _exit(EX_NOPERM);
638
 
                                }
639
 
                                break;
640
 
                        }
641
 
 
642
 
                        if (close(child_pipe[0]) == -1) {
643
 
                                pam_ssh_log(LOG_ERR, "close: %m");
644
 
                                _exit(EX_OSERR);
645
 
                        }
646
 
                        if (child_pipe[1] != STDOUT_FILENO) {
647
 
                                if (dup2(child_pipe[1], STDOUT_FILENO) == -1) {
648
 
                                        pam_ssh_log(LOG_ERR, "dup: %m");
649
 
                                        _exit(EX_OSERR);
650
 
                                }
651
 
                                if (close(child_pipe[1]) == -1) {
652
 
                                        pam_ssh_log(LOG_ERR, "close: %m");
653
 
                                        _exit(EX_OSERR);
654
 
                                }
655
 
                        }
656
 
                        arg[0] = "ssh-agent";
657
 
                        arg[1] = "-s";
658
 
                        arg[2] = NULL;
659
 
                        env[0] = NULL;
660
 
                        execve(PATH_SSH_AGENT, arg, env);
661
 
                        pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
662
 
                        _exit(127);
663
 
                        /* NOTREACHED */
664
 
                }
665
 
                if (close(child_pipe[1]) == -1) {
666
 
                        pam_ssh_log(LOG_ERR, "close: %m");
667
 
                        openpam_restore_cred(pamh);
668
 
                        return PAM_SESSION_ERR;
669
 
                }
670
 
                if (!(env_read = fdopen(child_pipe[0], "r"))) {
671
 
                        pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
672
 
                        close(env_write);
673
 
                        openpam_restore_cred(pamh);
674
 
                        return PAM_SESSION_ERR;
675
 
                }
676
 
        }
677
 
 
678
 
        /* save environment for application with pam_putenv() */
679
 
 
680
 
        agent_socket = NULL;
681
 
        while (fgets(env_string, sizeof env_string, env_read)) {
682
 
 
683
 
                /* parse environment definitions */
684
 
 
685
 
                if (env_write >= 0)
686
 
                        write(env_write, env_string, strlen(env_string));
687
 
                if (!(env_value = strchr(env_string, '=')) ||
688
 
                    !(env_end = strchr(env_value, ';')))
689
 
                        continue;
690
 
                *env_end = '\0';
691
 
 
692
 
                /* pass to the application */
693
 
 
694
 
                if ((retval = pam_putenv(pamh, env_string)) != PAM_SUCCESS) {
695
 
                        fclose(env_read);
696
 
                        if (start_agent)
697
 
                                waitpid_intr(child_pid, &child_status, 0);
698
 
                        close(env_write);
699
 
                        if (agent_socket)
700
 
                                free(agent_socket);
701
 
                        openpam_restore_cred(pamh);
702
 
                        return retval;
703
 
                }
704
 
 
705
 
                *env_value++ = '\0';
706
 
 
707
 
                /* save the agent socket so we can connect to it and add
708
 
                   the keys as well as the PID so we can kill the agent on
709
 
                   session close. */
710
 
 
711
 
                agent_pid = NULL;
712
 
                if (strcmp(&env_string[strlen(env_string) -
713
 
                    strlen(ENV_SOCKET_SUFFIX)], ENV_SOCKET_SUFFIX) == 0 &&
714
 
                    !(agent_socket = strdup(env_value))) {
715
 
                        pam_ssh_log(LOG_CRIT, "out of memory");
716
 
                        fclose(env_read);
717
 
                        if (start_agent)
718
 
                                waitpid_intr(child_pid, &child_status, 0);
719
 
                        close(env_write);
720
 
                        if (agent_socket)
721
 
                                free(agent_socket);
722
 
                        openpam_restore_cred(pamh);
723
 
                        return PAM_SERVICE_ERR;
724
 
                } else if (strcmp(&env_string[strlen(env_string) -
725
 
                    strlen(ENV_PID_SUFFIX)], ENV_PID_SUFFIX) == 0 &&
726
 
                    (!(agent_pid = strdup(env_value)) ||
727
 
                    (retval = pam_set_data(pamh, "ssh_agent_pid",
728
 
                    agent_pid, ssh_cleanup)) != PAM_SUCCESS)) {
729
 
                        fclose(env_read);
730
 
                        if (start_agent)
731
 
                                waitpid_intr(child_pid, &child_status, 0);
732
 
                        close(env_write);
733
 
                        if (agent_pid)
734
 
                                free(agent_pid);
735
 
                        else {
736
 
                                pam_ssh_log(LOG_CRIT, "out of memory");
 
849
        for ( attempt = 0; attempt < 2; ++attempt ) {
 
850
                env_write = no_link = start_agent = 0;
 
851
                env_read = NULL;
 
852
                if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR))
 
853
                                < 0 && !(env_read = fopen(per_agent, "r")))
 
854
                        no_link = 1;
 
855
                if (!env_read) {
 
856
                        start_agent = 1;
 
857
                        if ((retval = start_ssh_agent(pamh, pwent->pw_uid, &env_read))
 
858
                                        != PAM_SUCCESS) {
 
859
                                close(env_write);
737
860
                                openpam_restore_cred(pamh);
738
 
                                return PAM_SERVICE_ERR;
 
861
                                return retval;
739
862
                        }
 
863
                }
 
864
 
 
865
                agent_socket = NULL;
 
866
                retval = read_write_agent_env(pamh, env_read, env_write, &agent_socket);
 
867
                close(env_write);
 
868
                if (retval != PAM_SUCCESS) {
740
869
                        if (agent_socket)
741
870
                                free(agent_socket);
 
871
                        fclose(env_read);
742
872
                        openpam_restore_cred(pamh);
743
873
                        return retval;
744
874
                }
745
875
 
746
 
        }
747
 
        close(env_write);
748
 
 
749
 
        if (fclose(env_read) != 0) {
750
 
                pam_ssh_log(LOG_ERR, "fclose: %m");
751
 
                openpam_restore_cred(pamh);
752
 
                return PAM_SESSION_ERR;
753
 
        }
754
 
 
755
 
        if (start_agent) {
756
 
 
757
 
                /* Ignore ECHILD in case a SIGCHLD handler is installed. */
758
 
 
759
 
                child_status = 0;
760
 
                if (waitpid_intr(child_pid, &child_status, 0) == -1 &&
761
 
                    errno != ECHILD) {
762
 
                        pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
763
 
                        if (agent_socket)
764
 
                                free(agent_socket);
765
 
                        openpam_restore_cred(pamh);
766
 
                        return PAM_SESSION_ERR;
767
 
                }
768
 
 
769
 
                if (child_status != 0) {
770
 
                        if (WIFSIGNALED(child_status))
771
 
                                pam_ssh_log(LOG_ERR, "%s exited on signal %d",
772
 
                                    PATH_SSH_AGENT, WTERMSIG(child_status));
773
 
                        else
774
 
                                if (WEXITSTATUS(retval) == 127)
775
 
                                        pam_ssh_log(LOG_ERR,
776
 
                                            "cannot execute %s",
777
 
                                            PATH_SSH_AGENT);
778
 
                                else
779
 
                                        pam_ssh_log(LOG_ERR,
780
 
                                            "%s exited with status %d",
781
 
                                            PATH_SSH_AGENT,
782
 
                                            WEXITSTATUS(child_status));
783
 
                        if (agent_socket)
784
 
                                free(agent_socket);
785
 
                        openpam_restore_cred(pamh);
786
 
                        return PAM_SESSION_ERR;
787
 
                }
788
 
        }
789
 
 
790
 
        if (!agent_socket) {
791
 
                openpam_restore_cred(pamh);
792
 
                return PAM_SESSION_ERR;
793
 
        }
794
 
 
795
 
        if (start_agent && (retval = add_keys(pamh, agent_socket))
796
 
            != PAM_SUCCESS) {
 
876
                if (fclose(env_read) != 0) {
 
877
                        pam_ssh_log(LOG_ERR, "fclose: %m");
 
878
                        if (agent_socket)
 
879
                                free(agent_socket);
 
880
                        openpam_restore_cred(pamh);
 
881
                        return PAM_SESSION_ERR;
 
882
                }
 
883
 
 
884
                if (!agent_socket) {
 
885
                        openpam_restore_cred(pamh);
 
886
                        return PAM_SESSION_ERR;
 
887
                }
 
888
 
 
889
                ac = ssh_get_authentication_connection(agent_socket);
 
890
                if (ac) {
 
891
                        free(agent_socket);
 
892
                        break;
 
893
                }
 
894
                pam_ssh_log(LOG_ERR, "%s: %m", agent_socket);
 
895
                free(agent_socket);
 
896
                if (start_agent)
 
897
                        break;
 
898
                unlink(per_agent);
 
899
        }
 
900
 
 
901
        if (!ac)
 
902
                return PAM_SESSION_ERR;
 
903
 
 
904
        if (start_agent)
 
905
                retval = add_keys(pamh, ac);
 
906
 
 
907
        ssh_close_authentication_connection(ac);
 
908
 
 
909
        if (start_agent && retval != PAM_SUCCESS) {
 
910
                debug("could not start SSH agent");
797
911
                openpam_restore_cred(pamh);
798
912
                return retval;
799
913
        }
800
 
        free(agent_socket);
801
914
 
802
915
        /* if we couldn't access the per-agent file, don't link a
803
 
           per-session filename to it */
 
916
           per-session filename to it */
804
917
 
805
918
        if (no_link) {
806
919
                openpam_restore_cred(pamh);
808
921
        }
809
922
 
810
923
        /* the per-session file contains the display name or tty name as
811
 
           well as the hostname */
 
924
           well as the hostname */
812
925
 
813
926
        if ((retval = pam_get_item(pamh, PAM_TTY,
814
927
            (const void **)(void *)&tty_raw)) != PAM_SUCCESS) {
816
929
                return retval;
817
930
        }
818
931
 
819
 
        /* set tty_nodir to the tty with / replaced by _ */
820
 
 
821
 
        if (!(tty_nodir = strdup(tty_raw))) {
822
 
                pam_ssh_log(LOG_CRIT, "out of memory");
823
 
                openpam_restore_cred(pamh);
824
 
                return PAM_SERVICE_ERR;
825
 
        }
826
 
        for (cp = tty_nodir; (cp = strchr(cp, '/')); )
827
 
                *cp = '_';
 
932
        /* if there is no controlling tty, then use the process id */
 
933
 
 
934
        if (tty_raw == NULL) {
 
935
                debug("no controlling tty");
 
936
                if (asprintf(&tty_nodir, "pid%ld", (long) getpid()) == -1) {
 
937
                        pam_ssh_log(LOG_CRIT, "out of memory");
 
938
                        openpam_restore_cred(pamh);
 
939
                        return PAM_SERVICE_ERR;
 
940
                }
 
941
        }
 
942
 
 
943
        /* else set tty_nodir to the tty with / replaced by _ */
 
944
 
 
945
        else {
 
946
                if (!(tty_nodir = strdup(tty_raw))) {
 
947
                        pam_ssh_log(LOG_CRIT, "out of memory");
 
948
                        openpam_restore_cred(pamh);
 
949
                        return PAM_SERVICE_ERR;
 
950
                }
 
951
                for (cp = tty_nodir; (cp = strchr(cp, '/')); )
 
952
                        *cp = '_';
 
953
        }
828
954
 
829
955
        if (asprintf(&per_session, "%s/.ssh/agent-%s-%s", pwent->pw_dir, hname,
830
956
            tty_nodir) == -1) {
836
962
        free(tty_nodir);
837
963
 
838
964
        /* save the per-session filename so we can delete it on session
839
 
           close */
 
965
           close */
840
966
 
841
967
        if ((retval = pam_set_data(pamh, "ssh_agent_env_session", per_session,
842
968
            ssh_cleanup)) != PAM_SUCCESS) {
855
981
 
856
982
PAM_EXTERN int
857
983
pam_sm_close_session(pam_handle_t *pamh, int flags __unused,
858
 
    int argc __unused, const char **argv __unused)
 
984
                     int argc __unused, const char **argv __unused)
859
985
{
860
986
        const char *env_file;           /* ssh-agent environment */
861
987
        pid_t pid;                      /* ssh-agent process id */
863
989
        const char *ssh_agent_pid;      /* ssh-agent pid string */
864
990
        const struct passwd *pwent;     /* user's passwd entry */
865
991
        struct stat sb;                 /* to check st_nlink */
 
992
        const char *user;               /* username */
866
993
 
867
 
        if ((retval = pam_get_data(pamh, "ssh_passwd_entry",
868
 
            (const void **)(void *)&pwent)) != PAM_SUCCESS)
 
994
        if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
869
995
                return retval;
870
996
 
 
997
        if (!(user && (pwent = getpwnam(user)) && pwent->pw_dir && *pwent->pw_dir))
 
998
                return PAM_SESSION_ERR;
 
999
 
871
1000
        retval = openpam_borrow_cred(pamh, pwent);
872
1001
        if (retval != PAM_SUCCESS && retval != PAM_PERM_DENIED) {
873
1002
                pam_ssh_log(LOG_ERR, "can't drop privileges: %m");
879
1008
                unlink(env_file);
880
1009
 
881
1010
        /* Retrieve per-agent filename and check link count.  If it's
882
 
           greater than unity, other sessions are still using this
883
 
           agent. */
 
1011
           greater than unity, other sessions are still using this
 
1012
           agent. */
884
1013
 
885
1014
        if (pam_get_data(pamh, "ssh_agent_env_agent",
886
1015
            (const void **)(void *)&env_file)
904
1033
        }
905
1034
 
906
1035
        /* Kill the agent.  SSH's ssh-agent does not have a -k option, so
907
 
           just call kill(). */
 
1036
           just call kill(). */
908
1037
 
909
1038
        pid = atoi(ssh_agent_pid);
910
1039
        if (pid <= 0) {
925
1054
#if !HAVE_OPENPAM
926
1055
PAM_EXTERN int
927
1056
pam_sm_acct_mgmt(pam_handle_t *pamh __unused, int flags __unused,
928
 
    int argc __unused, const char **argv __unused)
 
1057
                 int argc __unused, const char **argv __unused)
929
1058
{
930
1059
        return PAM_IGNORE;
931
1060
}
933
1062
 
934
1063
PAM_EXTERN int
935
1064
pam_sm_chauthtok(pam_handle_t *pamh __unused, int flags __unused,
936
 
    int argc __unused, const char **argv __unused)
 
1065
                 int argc __unused, const char **argv __unused)
937
1066
{
938
1067
        return PAM_IGNORE;
939
1068
}