348
ssh_close_authentication_connection(ac);
350
327
return final ? PAM_SUCCESS : PAM_SESSION_ERR;
331
start_ssh_agent(pam_handle_t *pamh, uid_t uid, FILE **env_read)
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() */
338
if (pipe(child_pipe) < 0) {
339
pam_ssh_log(LOG_ERR, "pipe: %m");
340
return PAM_SERVICE_ERR;
342
switch (child_pid = fork()) {
344
pam_ssh_log(LOG_ERR, "fork: %m");
345
close(child_pipe[0]);
346
close(child_pipe[1]);
347
return PAM_SERVICE_ERR;
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
361
switch (openpam_restore_cred(pamh)) {
364
"can't restore privileges: %m");
368
if (setuid(uid) == -1) {
370
"can't drop privileges: %m",
377
if (close(child_pipe[0]) == -1) {
378
pam_ssh_log(LOG_ERR, "close: %m");
381
if (child_pipe[1] != STDOUT_FILENO) {
382
if (dup2(child_pipe[1], STDOUT_FILENO) == -1) {
383
pam_ssh_log(LOG_ERR, "dup: %m");
386
if (close(child_pipe[1]) == -1) {
387
pam_ssh_log(LOG_ERR, "close: %m");
391
arg[0] = "ssh-agent";
395
debug("Starting ssh-agent.");
396
execve(PATH_SSH_AGENT, arg, env);
397
pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
401
if (close(child_pipe[1]) == -1) {
402
pam_ssh_log(LOG_ERR, "close: %m");
403
return PAM_SESSION_ERR;
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;
411
if (waitpid_intr(child_pid, &child_status, 0) == -1 &&
413
pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
414
return PAM_SESSION_ERR;
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));
422
if (WEXITSTATUS(child_status) == 127)
428
"%s exited with status %d",
430
WEXITSTATUS(child_status));
431
return PAM_SESSION_ERR;
438
read_write_agent_env(pam_handle_t *pamh,
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 */
449
while (fgets(env_string, sizeof env_string, env_read)) {
451
/* parse environment definitions */
454
write(env_write, env_string, strlen(env_string));
455
if (!(env_value = strchr(env_string, '=')) ||
456
!(env_end = strchr(env_value, ';')))
460
/* pass to the application */
462
if ((retval = pam_putenv(pamh, env_string)) != PAM_SUCCESS)
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
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)) {
485
pam_ssh_log(LOG_CRIT, "out of memory");
486
return PAM_SERVICE_ERR;
500
unlock_standard_keys(pam_handle_t *pamh, const char *pass, const char *dotdir,
503
char *files[] = {"id_dsa", "id_rsa", "identity", NULL};
507
debug("No preceding password.");
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");
518
if (PAM_SUCCESS == auth_via_key(pamh, path, files[i], pass,
520
debug("SSH key '%s' decrypted.", files[i]);
522
debug2("SSH key candidate '%s' failed.", files[i]);
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)
536
int result = PAM_AUTH_ERR;
538
debug("No preceding password.");
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
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;
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,
560
debug("SSH key '%s' decrypted.", file);
561
result = PAM_SUCCESS;
563
debug("SSH key candidate '%s' failed.", file);
571
#define CLEANUP_AND_RETURN(retcode) \
572
while (n--) free(namelist[n]); \
576
openpam_restore_cred(pamh); \
355
581
pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc,
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 */
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() */
369
char *kfspec; /* list of key files to add */
370
592
int options; /* options for pam_get_pass() */
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 */
378
log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
599
dotdir = logindir = NULL;
380
600
allow_blank_passphrase = 0;
381
keyfiles = kfspec = NULL;
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;
389
kfspec = DEF_KEYFILES;
390
if ((kfspec = openpam_get_option(pamh, PAM_OPT_BLANK_PASSPHRASE)))
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.");
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);
619
log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
401
621
for (; argc; argc--, argv++) {
402
622
struct opttab *p;
438
/* pass prompt message to application and receive passphrase */
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);
445
retval = pam_get_pass(pamh, &pass, NEED_PASSPHRASE, options);
447
if (retval != PAM_SUCCESS) {
448
openpam_restore_cred(pamh);
452
openpam_restore_cred(pamh);
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;
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;
670
n = scandir(logindir, &namelist, NULL, alphasort);
672
if (ENOMEM == errno) {
673
pam_ssh_log(LOG_CRIT, "out of memory");
674
openpam_restore_cred(pamh);
675
return PAM_SERVICE_ERR;
677
debug("No SSH login-keys directory.");
456
682
OpenSSL_add_all_algorithms(); /* required for DSA */
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
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;
468
if (!(keyfiles = strdup(kfspec))) {
469
pam_ssh_log(LOG_CRIT, "out of memory");
470
openpam_restore_cred(pamh);
471
return PAM_SERVICE_ERR;
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)
480
if (!authenticated) {
481
openpam_restore_cred(pamh);
485
/* copy the passwd entry (in case successive calls are made) and
486
save it for the session phase */
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;
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) {
497
openpam_restore_cred(pamh);
501
openpam_restore_cred(pamh);
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 "
693
CLEANUP_AND_RETURN(retval);
697
/* If use_first_password option given, then go through all keys and
698
unlock each that matches. If none matches, fail, otherwise
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. */
707
CLEANUP_AND_RETURN(PAM_AUTH_ERR);
709
retval = unlock_at_least_one_key(pamh,pass,logindir,namelist,n,
710
allow_blank_passphrase);
711
CLEANUP_AND_RETURN(retval);
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. */
723
CLEANUP_AND_RETURN(PAM_AUTH_ERR);
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);
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
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);
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);
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);
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)
510
759
return PAM_SUCCESS;
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. */
592
env_write = child_pid = no_link = start_agent = 0;
594
if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR))
595
< 0 && !(env_read = fopen(per_agent, "r")))
599
if (pipe(child_pipe) < 0) {
600
pam_ssh_log(LOG_ERR, "pipe: %m");
602
openpam_restore_cred(pamh);
603
return PAM_SERVICE_ERR;
605
switch (child_pid = fork()) {
607
pam_ssh_log(LOG_ERR, "fork: %m");
608
close(child_pipe[0]);
609
close(child_pipe[1]);
611
openpam_restore_cred(pamh);
612
return PAM_SERVICE_ERR;
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
626
switch (openpam_restore_cred(pamh)) {
629
"can't restore privileges: %m");
633
if (setuid(pwent->pw_uid) == -1) {
635
"can't drop privileges: %m",
642
if (close(child_pipe[0]) == -1) {
643
pam_ssh_log(LOG_ERR, "close: %m");
646
if (child_pipe[1] != STDOUT_FILENO) {
647
if (dup2(child_pipe[1], STDOUT_FILENO) == -1) {
648
pam_ssh_log(LOG_ERR, "dup: %m");
651
if (close(child_pipe[1]) == -1) {
652
pam_ssh_log(LOG_ERR, "close: %m");
656
arg[0] = "ssh-agent";
660
execve(PATH_SSH_AGENT, arg, env);
661
pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
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;
670
if (!(env_read = fdopen(child_pipe[0], "r"))) {
671
pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
673
openpam_restore_cred(pamh);
674
return PAM_SESSION_ERR;
678
/* save environment for application with pam_putenv() */
681
while (fgets(env_string, sizeof env_string, env_read)) {
683
/* parse environment definitions */
686
write(env_write, env_string, strlen(env_string));
687
if (!(env_value = strchr(env_string, '=')) ||
688
!(env_end = strchr(env_value, ';')))
692
/* pass to the application */
694
if ((retval = pam_putenv(pamh, env_string)) != PAM_SUCCESS) {
697
waitpid_intr(child_pid, &child_status, 0);
701
openpam_restore_cred(pamh);
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
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");
718
waitpid_intr(child_pid, &child_status, 0);
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)) {
731
waitpid_intr(child_pid, &child_status, 0);
736
pam_ssh_log(LOG_CRIT, "out of memory");
849
for ( attempt = 0; attempt < 2; ++attempt ) {
850
env_write = no_link = start_agent = 0;
852
if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR))
853
< 0 && !(env_read = fopen(per_agent, "r")))
857
if ((retval = start_ssh_agent(pamh, pwent->pw_uid, &env_read))
737
860
openpam_restore_cred(pamh);
738
return PAM_SERVICE_ERR;
866
retval = read_write_agent_env(pamh, env_read, env_write, &agent_socket);
868
if (retval != PAM_SUCCESS) {
740
869
if (agent_socket)
741
870
free(agent_socket);
742
872
openpam_restore_cred(pamh);
749
if (fclose(env_read) != 0) {
750
pam_ssh_log(LOG_ERR, "fclose: %m");
751
openpam_restore_cred(pamh);
752
return PAM_SESSION_ERR;
757
/* Ignore ECHILD in case a SIGCHLD handler is installed. */
760
if (waitpid_intr(child_pid, &child_status, 0) == -1 &&
762
pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
765
openpam_restore_cred(pamh);
766
return PAM_SESSION_ERR;
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));
774
if (WEXITSTATUS(retval) == 127)
780
"%s exited with status %d",
782
WEXITSTATUS(child_status));
785
openpam_restore_cred(pamh);
786
return PAM_SESSION_ERR;
791
openpam_restore_cred(pamh);
792
return PAM_SESSION_ERR;
795
if (start_agent && (retval = add_keys(pamh, agent_socket))
876
if (fclose(env_read) != 0) {
877
pam_ssh_log(LOG_ERR, "fclose: %m");
880
openpam_restore_cred(pamh);
881
return PAM_SESSION_ERR;
885
openpam_restore_cred(pamh);
886
return PAM_SESSION_ERR;
889
ac = ssh_get_authentication_connection(agent_socket);
894
pam_ssh_log(LOG_ERR, "%s: %m", agent_socket);
902
return PAM_SESSION_ERR;
905
retval = add_keys(pamh, ac);
907
ssh_close_authentication_connection(ac);
909
if (start_agent && retval != PAM_SUCCESS) {
910
debug("could not start SSH agent");
797
911
openpam_restore_cred(pamh);
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 */
806
919
openpam_restore_cred(pamh);