1*cefc293eShannken /* $NetBSD: pam_ssh.c,v 1.30 2022/06/15 08:31:34 hannken Exp $ */
2e7d22a2eSchristos
36f11bdf1Schristos /*-
46f11bdf1Schristos * Copyright (c) 2003 Networks Associates Technology, Inc.
56f11bdf1Schristos * All rights reserved.
66f11bdf1Schristos *
76f11bdf1Schristos * This software was developed for the FreeBSD Project by ThinkSec AS and
86f11bdf1Schristos * NAI Labs, the Security Research Division of Network Associates, Inc.
96f11bdf1Schristos * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
106f11bdf1Schristos * DARPA CHATS research program.
116f11bdf1Schristos *
126f11bdf1Schristos * Redistribution and use in source and binary forms, with or without
136f11bdf1Schristos * modification, are permitted provided that the following conditions
146f11bdf1Schristos * are met:
156f11bdf1Schristos * 1. Redistributions of source code must retain the above copyright
166f11bdf1Schristos * notice, this list of conditions and the following disclaimer.
176f11bdf1Schristos * 2. Redistributions in binary form must reproduce the above copyright
186f11bdf1Schristos * notice, this list of conditions and the following disclaimer in the
196f11bdf1Schristos * documentation and/or other materials provided with the distribution.
206f11bdf1Schristos * 3. The name of the author may not be used to endorse or promote
216f11bdf1Schristos * products derived from this software without specific prior written
226f11bdf1Schristos * permission.
236f11bdf1Schristos *
246f11bdf1Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
256f11bdf1Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
266f11bdf1Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
276f11bdf1Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
286f11bdf1Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
296f11bdf1Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
306f11bdf1Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
316f11bdf1Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
326f11bdf1Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
336f11bdf1Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
346f11bdf1Schristos * SUCH DAMAGE.
356f11bdf1Schristos */
366f11bdf1Schristos
376f11bdf1Schristos #include <sys/cdefs.h>
38474dd6daSlukem #ifdef __FreeBSD__
396f11bdf1Schristos __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_ssh/pam_ssh.c,v 1.40 2004/02/10 10:13:21 des Exp $");
40e7d22a2eSchristos #else
41*cefc293eShannken __RCSID("$NetBSD: pam_ssh.c,v 1.30 2022/06/15 08:31:34 hannken Exp $");
42e7d22a2eSchristos #endif
436f11bdf1Schristos
446f11bdf1Schristos #include <sys/param.h>
456f11bdf1Schristos #include <sys/wait.h>
466f11bdf1Schristos
476f11bdf1Schristos #include <errno.h>
486f11bdf1Schristos #include <fcntl.h>
496f11bdf1Schristos #include <paths.h>
506f11bdf1Schristos #include <pwd.h>
516f11bdf1Schristos #include <signal.h>
526f11bdf1Schristos #include <stdio.h>
536f11bdf1Schristos #include <string.h>
546f11bdf1Schristos #include <unistd.h>
556f11bdf1Schristos
566f11bdf1Schristos #define PAM_SM_AUTH
576f11bdf1Schristos #define PAM_SM_SESSION
586f11bdf1Schristos
596f11bdf1Schristos #include <security/pam_appl.h>
606f11bdf1Schristos #include <security/pam_modules.h>
616f11bdf1Schristos #include <security/openpam.h>
626f11bdf1Schristos
636f11bdf1Schristos #include <openssl/evp.h>
646f11bdf1Schristos
658ea42f66Schristos #include "sshkey.h"
668ea42f66Schristos #include "sshbuf.h"
676f11bdf1Schristos #include "authfd.h"
686f11bdf1Schristos #include "authfile.h"
696f11bdf1Schristos
70705315cdSdrochner #define ssh_add_identity(auth, key, comment) \
71*cefc293eShannken ssh_add_identity_constrained(auth, key, comment, 0, 0, 0, NULL, NULL, 00)
72705315cdSdrochner
736f11bdf1Schristos extern char **environ;
746f11bdf1Schristos
756f11bdf1Schristos struct pam_ssh_key {
768ea42f66Schristos struct sshkey *key;
776f11bdf1Schristos char *comment;
786f11bdf1Schristos };
796f11bdf1Schristos
806f11bdf1Schristos static const char *pam_ssh_prompt = "SSH passphrase: ";
816f11bdf1Schristos static const char *pam_ssh_have_keys = "pam_ssh_have_keys";
826f11bdf1Schristos
836f11bdf1Schristos static const char *pam_ssh_keyfiles[] = {
846f11bdf1Schristos ".ssh/identity", /* SSH1 RSA key */
856f11bdf1Schristos ".ssh/id_rsa", /* SSH2 RSA key */
866f11bdf1Schristos ".ssh/id_dsa", /* SSH2 DSA key */
87cb4d5f3dSdrochner ".ssh/id_ecdsa", /* SSH2 ECDSA key */
886f11bdf1Schristos NULL
896f11bdf1Schristos };
906f11bdf1Schristos
916f11bdf1Schristos static const char *pam_ssh_agent = "/usr/bin/ssh-agent";
92705315cdSdrochner static const char *const pam_ssh_agent_argv[] = { "ssh_agent", "-s", NULL };
93705315cdSdrochner static const char *const pam_ssh_agent_envp[] = { NULL };
946f11bdf1Schristos
956f11bdf1Schristos /*
966f11bdf1Schristos * Attempts to load a private key from the specified file in the specified
976f11bdf1Schristos * directory, using the specified passphrase. If successful, returns a
986f11bdf1Schristos * struct pam_ssh_key containing the key and its comment.
996f11bdf1Schristos */
1006f11bdf1Schristos static struct pam_ssh_key *
pam_ssh_load_key(const char * dir,const char * kfn,const char * passphrase,int nullok)1012462eb04Sdrochner pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase,
1022462eb04Sdrochner int nullok)
1036f11bdf1Schristos {
1046f11bdf1Schristos struct pam_ssh_key *psk;
1056f11bdf1Schristos char fn[PATH_MAX];
1068ea42f66Schristos int r;
1076f11bdf1Schristos char *comment;
1088ea42f66Schristos struct sshkey *key;
1096f11bdf1Schristos
110705315cdSdrochner if (snprintf(fn, sizeof(fn), "%s/%s", dir, kfn) > (int)sizeof(fn))
1116f11bdf1Schristos return (NULL);
1126f11bdf1Schristos comment = NULL;
1132462eb04Sdrochner /*
1142462eb04Sdrochner * If the key is unencrypted, OpenSSL ignores the passphrase, so
1152462eb04Sdrochner * it will seem like the user typed in the right one. This allows
1162462eb04Sdrochner * a user to circumvent nullok by providing a dummy passphrase.
1172462eb04Sdrochner * Verify that the key really *is* encrypted by trying to load it
1182462eb04Sdrochner * with an empty passphrase, and if the key is not encrypted,
1192462eb04Sdrochner * accept only an empty passphrase.
1202462eb04Sdrochner */
1218ea42f66Schristos r = sshkey_load_private(fn, "", &key, &comment);
1228137552aSmlelstv if (r == 0 && !(*passphrase == '\0' && nullok)) {
1238137552aSmlelstv openpam_log(PAM_LOG_DEBUG, "rejected unencrypted key from %s", fn);
1248ea42f66Schristos sshkey_free(key);
1252462eb04Sdrochner free(comment);
1262462eb04Sdrochner return (NULL);
1272462eb04Sdrochner }
1288ea42f66Schristos if (r)
1298137552aSmlelstv r = sshkey_load_private(fn, passphrase, &key, &comment);
1308ea42f66Schristos if (r) {
13169de8de8Sdrochner openpam_log(PAM_LOG_DEBUG, "failed to load key from %s", fn);
1320c47a675Sjnemeth if (comment != NULL)
1330c47a675Sjnemeth free(comment);
1346f11bdf1Schristos return (NULL);
1356f11bdf1Schristos }
1366f11bdf1Schristos
13769de8de8Sdrochner openpam_log(PAM_LOG_DEBUG, "loaded '%s' from %s", comment, fn);
1386f11bdf1Schristos if ((psk = malloc(sizeof(*psk))) == NULL) {
1398ea42f66Schristos sshkey_free(key);
1406f11bdf1Schristos free(comment);
1416f11bdf1Schristos return (NULL);
1426f11bdf1Schristos }
1436f11bdf1Schristos psk->key = key;
1446f11bdf1Schristos psk->comment = comment;
1456f11bdf1Schristos return (psk);
1466f11bdf1Schristos }
1476f11bdf1Schristos
1486f11bdf1Schristos /*
1496f11bdf1Schristos * Wipes a private key and frees the associated resources.
1506f11bdf1Schristos */
1516f11bdf1Schristos static void
pam_ssh_free_key(pam_handle_t * pamh __unused,void * data,int pam_err __unused)1526f11bdf1Schristos pam_ssh_free_key(pam_handle_t *pamh __unused,
1536f11bdf1Schristos void *data, int pam_err __unused)
1546f11bdf1Schristos {
1556f11bdf1Schristos struct pam_ssh_key *psk;
1566f11bdf1Schristos
1576f11bdf1Schristos psk = data;
1588ea42f66Schristos sshkey_free(psk->key);
1596f11bdf1Schristos free(psk->comment);
1606f11bdf1Schristos free(psk);
1616f11bdf1Schristos }
1626f11bdf1Schristos
1636f11bdf1Schristos PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)1646f11bdf1Schristos pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
1656f11bdf1Schristos int argc __unused, const char *argv[] __unused)
1666f11bdf1Schristos {
1676f11bdf1Schristos const char **kfn, *passphrase, *user;
168705315cdSdrochner const void *item;
16959cbc9e2Sthorpej struct passwd *pwd, pwres;
1706f11bdf1Schristos struct pam_ssh_key *psk;
1712462eb04Sdrochner int nkeys, nullok, pam_err, pass;
17259cbc9e2Sthorpej char pwbuf[1024];
1736f11bdf1Schristos
1742462eb04Sdrochner nullok = (openpam_get_option(pamh, "nullok") != NULL);
1752462eb04Sdrochner
1766f11bdf1Schristos /* PEM is not loaded by default */
1776f11bdf1Schristos OpenSSL_add_all_algorithms();
1786f11bdf1Schristos
1796f11bdf1Schristos /* get user name and home directory */
1806f11bdf1Schristos pam_err = pam_get_user(pamh, &user, NULL);
1816f11bdf1Schristos if (pam_err != PAM_SUCCESS)
1826f11bdf1Schristos return (pam_err);
1832a62e4e1Schristos if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
1842a62e4e1Schristos pwd == NULL)
1856f11bdf1Schristos return (PAM_USER_UNKNOWN);
1866f11bdf1Schristos if (pwd->pw_dir == NULL)
1876f11bdf1Schristos return (PAM_AUTH_ERR);
1886f11bdf1Schristos
1892462eb04Sdrochner nkeys = 0;
190705315cdSdrochner pass = (pam_get_item(pamh, PAM_AUTHTOK, &item) == PAM_SUCCESS &&
191705315cdSdrochner item != NULL);
1926f11bdf1Schristos load_keys:
1936f11bdf1Schristos /* get passphrase */
1946f11bdf1Schristos pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
1956f11bdf1Schristos &passphrase, pam_ssh_prompt);
1962a0c9a37Sdrochner if (pam_err != PAM_SUCCESS)
1976f11bdf1Schristos return (pam_err);
1982a0c9a37Sdrochner
1992a0c9a37Sdrochner /* switch to user credentials */
2002a0c9a37Sdrochner pam_err = openpam_borrow_cred(pamh, pwd);
2012a0c9a37Sdrochner if (pam_err != PAM_SUCCESS)
2022a0c9a37Sdrochner return (pam_err);
2036f11bdf1Schristos
2046f11bdf1Schristos /* try to load keys from all keyfiles we know of */
2056f11bdf1Schristos for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) {
2062462eb04Sdrochner psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase, nullok);
2076f11bdf1Schristos if (psk != NULL) {
2086f11bdf1Schristos pam_set_data(pamh, *kfn, psk, pam_ssh_free_key);
2096f11bdf1Schristos ++nkeys;
2106f11bdf1Schristos }
2116f11bdf1Schristos }
2126f11bdf1Schristos
2132a0c9a37Sdrochner /* switch back to arbitrator credentials */
2142a0c9a37Sdrochner openpam_restore_cred(pamh);
2152a0c9a37Sdrochner
2166f11bdf1Schristos /*
2176f11bdf1Schristos * If we tried an old token and didn't get anything, and
2186f11bdf1Schristos * try_first_pass was specified, try again after prompting the
2196f11bdf1Schristos * user for a new passphrase.
2206f11bdf1Schristos */
2216f11bdf1Schristos if (nkeys == 0 && pass == 1 &&
2226f11bdf1Schristos openpam_get_option(pamh, "try_first_pass") != NULL) {
2236f11bdf1Schristos pam_set_item(pamh, PAM_AUTHTOK, NULL);
2246f11bdf1Schristos pass = 0;
2256f11bdf1Schristos goto load_keys;
2266f11bdf1Schristos }
2276f11bdf1Schristos
2286f11bdf1Schristos /* no keys? */
2296f11bdf1Schristos if (nkeys == 0)
2306f11bdf1Schristos return (PAM_AUTH_ERR);
2316f11bdf1Schristos
2326f11bdf1Schristos pam_set_data(pamh, pam_ssh_have_keys, NULL, NULL);
2336f11bdf1Schristos return (PAM_SUCCESS);
2346f11bdf1Schristos }
2356f11bdf1Schristos
2366f11bdf1Schristos PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh __unused,int flags __unused,int argc __unused,const char * argv[]__unused)2376f11bdf1Schristos pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
2386f11bdf1Schristos int argc __unused, const char *argv[] __unused)
2396f11bdf1Schristos {
2406f11bdf1Schristos
2416f11bdf1Schristos return (PAM_SUCCESS);
2426f11bdf1Schristos }
2436f11bdf1Schristos
2446f11bdf1Schristos /*
2456f11bdf1Schristos * Parses a line from ssh-agent's output.
2466f11bdf1Schristos */
2476f11bdf1Schristos static void
pam_ssh_process_agent_output(pam_handle_t * pamh,FILE * f)2486f11bdf1Schristos pam_ssh_process_agent_output(pam_handle_t *pamh, FILE *f)
2496f11bdf1Schristos {
2506f11bdf1Schristos char *line, *p, *key, *val;
2516f11bdf1Schristos size_t len;
2526f11bdf1Schristos
2536f11bdf1Schristos while ((line = fgetln(f, &len)) != NULL) {
2546f11bdf1Schristos if (len < 4 || strncmp(line, "SSH_", 4) != 0)
2556f11bdf1Schristos continue;
2566f11bdf1Schristos
2576f11bdf1Schristos /* find equal sign at end of key */
2586f11bdf1Schristos for (p = key = line; p < line + len; ++p)
2596f11bdf1Schristos if (*p == '=')
2606f11bdf1Schristos break;
2616f11bdf1Schristos if (p == line + len || *p != '=')
2626f11bdf1Schristos continue;
2636f11bdf1Schristos *p = '\0';
2646f11bdf1Schristos
2656f11bdf1Schristos /* find semicolon at end of value */
2666f11bdf1Schristos for (val = ++p; p < line + len; ++p)
2676f11bdf1Schristos if (*p == ';')
2686f11bdf1Schristos break;
2696f11bdf1Schristos if (p == line + len || *p != ';')
2706f11bdf1Schristos continue;
2716f11bdf1Schristos *p = '\0';
2726f11bdf1Schristos
2736f11bdf1Schristos /* store key-value pair in environment */
2746f11bdf1Schristos openpam_log(PAM_LOG_DEBUG, "got %s: %s", key, val);
2756f11bdf1Schristos pam_setenv(pamh, key, val, 1);
2766f11bdf1Schristos }
2776f11bdf1Schristos }
2786f11bdf1Schristos
2796f11bdf1Schristos /*
2806f11bdf1Schristos * Starts an ssh agent and stores the environment variables derived from
2816f11bdf1Schristos * its output.
2826f11bdf1Schristos */
2836f11bdf1Schristos static int
pam_ssh_start_agent(pam_handle_t * pamh,struct passwd * pwd)284901ebd51Schristos pam_ssh_start_agent(pam_handle_t *pamh, struct passwd *pwd)
2856f11bdf1Schristos {
2866f11bdf1Schristos int agent_pipe[2];
2876f11bdf1Schristos pid_t pid;
2886f11bdf1Schristos FILE *f;
2896f11bdf1Schristos
2906f11bdf1Schristos /* get a pipe which we will use to read the agent's output */
291901ebd51Schristos if (pipe(agent_pipe) == -1)
2926f11bdf1Schristos return (PAM_SYSTEM_ERR);
2936f11bdf1Schristos
2946f11bdf1Schristos /* start the agent */
2956f11bdf1Schristos openpam_log(PAM_LOG_DEBUG, "starting an ssh agent");
2966f11bdf1Schristos pid = fork();
2976f11bdf1Schristos if (pid == (pid_t)-1) {
2986f11bdf1Schristos /* failed */
2996f11bdf1Schristos close(agent_pipe[0]);
3006f11bdf1Schristos close(agent_pipe[1]);
3016f11bdf1Schristos return (PAM_SYSTEM_ERR);
3026f11bdf1Schristos }
3036f11bdf1Schristos if (pid == 0) {
304e7d22a2eSchristos #ifndef F_CLOSEM
3056f11bdf1Schristos int fd;
306e7d22a2eSchristos #endif
3076f11bdf1Schristos /* child: drop privs, close fds and start agent */
308901ebd51Schristos if (setgid(pwd->pw_gid) == -1) {
3091b695acdSchristos openpam_log(PAM_LOG_DEBUG, "%s: Cannot setgid %d (%s)",
3101b695acdSchristos __func__, (int)pwd->pw_gid, strerror(errno));
311901ebd51Schristos goto done;
312901ebd51Schristos }
313901ebd51Schristos if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
314901ebd51Schristos openpam_log(PAM_LOG_DEBUG,
3151b695acdSchristos "%s: Cannot initgroups for %s (%s)",
3161b695acdSchristos __func__, pwd->pw_name, strerror(errno));
317901ebd51Schristos goto done;
318901ebd51Schristos }
319901ebd51Schristos if (setuid(pwd->pw_uid) == -1) {
3201b695acdSchristos openpam_log(PAM_LOG_DEBUG, "%s: Cannot setuid %d (%s)",
3211b695acdSchristos __func__, (int)pwd->pw_uid, strerror(errno));
322901ebd51Schristos goto done;
323901ebd51Schristos }
324901ebd51Schristos (void)close(STDIN_FILENO);
325901ebd51Schristos (void)open(_PATH_DEVNULL, O_RDONLY);
326901ebd51Schristos (void)dup2(agent_pipe[1], STDOUT_FILENO);
327901ebd51Schristos (void)dup2(agent_pipe[1], STDERR_FILENO);
328e7d22a2eSchristos #ifdef F_CLOSEM
329e7d22a2eSchristos (void)fcntl(3, F_CLOSEM, 0);
330e7d22a2eSchristos #else
3316f11bdf1Schristos for (fd = 3; fd < getdtablesize(); ++fd)
332901ebd51Schristos (void)close(fd);
333e7d22a2eSchristos #endif
334901ebd51Schristos (void)execve(pam_ssh_agent,
335901ebd51Schristos (char **)__UNCONST(pam_ssh_agent_argv),
336e7d22a2eSchristos (char **)__UNCONST(pam_ssh_agent_envp));
337901ebd51Schristos done:
3386f11bdf1Schristos _exit(127);
3396f11bdf1Schristos }
3406f11bdf1Schristos
3416f11bdf1Schristos /* parent */
3426f11bdf1Schristos close(agent_pipe[1]);
3436f11bdf1Schristos if ((f = fdopen(agent_pipe[0], "r")) == NULL)
3446f11bdf1Schristos return (PAM_SYSTEM_ERR);
3456f11bdf1Schristos pam_ssh_process_agent_output(pamh, f);
3466f11bdf1Schristos fclose(f);
3476f11bdf1Schristos
3486f11bdf1Schristos return (PAM_SUCCESS);
3496f11bdf1Schristos }
3506f11bdf1Schristos
3516f11bdf1Schristos /*
3526f11bdf1Schristos * Adds previously stored keys to a running agent.
3536f11bdf1Schristos */
3546f11bdf1Schristos static int
pam_ssh_add_keys_to_agent(pam_handle_t * pamh)3556f11bdf1Schristos pam_ssh_add_keys_to_agent(pam_handle_t *pamh)
3566f11bdf1Schristos {
3574466f6b5Schristos const struct pam_ssh_key *psk;
3586f11bdf1Schristos const char **kfn;
3596f11bdf1Schristos char **envlist, **env;
3606f11bdf1Schristos int pam_err;
361f804baf7Schristos int agent_fd;
3626f11bdf1Schristos
3636f11bdf1Schristos /* switch to PAM environment */
3646f11bdf1Schristos envlist = environ;
3656f11bdf1Schristos if ((environ = pam_getenvlist(pamh)) == NULL) {
366901ebd51Schristos openpam_log(PAM_LOG_DEBUG, "%s: cannot get envlist",
367592fd7eaSragge __func__);
3686f11bdf1Schristos environ = envlist;
3696f11bdf1Schristos return (PAM_SYSTEM_ERR);
3706f11bdf1Schristos }
3716f11bdf1Schristos
3726f11bdf1Schristos /* get a connection to the agent */
373f804baf7Schristos if (ssh_get_authentication_socket(&agent_fd) != 0) {
374901ebd51Schristos openpam_log(PAM_LOG_DEBUG,
375901ebd51Schristos "%s: cannot get authentication connection",
376592fd7eaSragge __func__);
3776f11bdf1Schristos pam_err = PAM_SYSTEM_ERR;
378f804baf7Schristos agent_fd = -1;
3796f11bdf1Schristos goto end;
3806f11bdf1Schristos }
3816f11bdf1Schristos
3826f11bdf1Schristos /* look for keys to add to it */
3836f11bdf1Schristos for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) {
3844466f6b5Schristos const void *vp;
3854466f6b5Schristos pam_err = pam_get_data(pamh, *kfn, &vp);
3864466f6b5Schristos psk = vp;
3876f11bdf1Schristos if (pam_err == PAM_SUCCESS && psk != NULL) {
3889cbfe7dcSchristos if (ssh_add_identity(agent_fd, psk->key, psk->comment))
3896f11bdf1Schristos openpam_log(PAM_LOG_DEBUG,
3906f11bdf1Schristos "added %s to ssh agent", psk->comment);
3916f11bdf1Schristos else
3926f11bdf1Schristos openpam_log(PAM_LOG_DEBUG, "failed "
3936f11bdf1Schristos "to add %s to ssh agent", psk->comment);
3946f11bdf1Schristos /* we won't need the key again, so wipe it */
3956f11bdf1Schristos pam_set_data(pamh, *kfn, NULL, NULL);
3966f11bdf1Schristos }
3976f11bdf1Schristos }
3986f11bdf1Schristos pam_err = PAM_SUCCESS;
3996f11bdf1Schristos end:
4006f11bdf1Schristos /* disconnect from agent */
401f804baf7Schristos if (agent_fd != -1)
402f804baf7Schristos ssh_close_authentication_socket(agent_fd);
4036f11bdf1Schristos
4046f11bdf1Schristos /* switch back to original environment */
4056f11bdf1Schristos for (env = environ; *env != NULL; ++env)
4066f11bdf1Schristos free(*env);
4076f11bdf1Schristos free(environ);
4086f11bdf1Schristos environ = envlist;
4096f11bdf1Schristos
4106f11bdf1Schristos return (pam_err);
4116f11bdf1Schristos }
4126f11bdf1Schristos
4136f11bdf1Schristos PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)4146f11bdf1Schristos pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
4156f11bdf1Schristos int argc __unused, const char *argv[] __unused)
4166f11bdf1Schristos {
41759cbc9e2Sthorpej struct passwd *pwd, pwres;
4186f11bdf1Schristos const char *user;
4194466f6b5Schristos const void *data;
420901ebd51Schristos int pam_err = PAM_SUCCESS;
42159cbc9e2Sthorpej char pwbuf[1024];
4226f11bdf1Schristos
4236f11bdf1Schristos /* no keys, no work */
4246f11bdf1Schristos if (pam_get_data(pamh, pam_ssh_have_keys, &data) != PAM_SUCCESS &&
4256f11bdf1Schristos openpam_get_option(pamh, "want_agent") == NULL)
4266f11bdf1Schristos return (PAM_SUCCESS);
4276f11bdf1Schristos
4286f11bdf1Schristos /* switch to user credentials */
4296f11bdf1Schristos pam_err = pam_get_user(pamh, &user, NULL);
4306f11bdf1Schristos if (pam_err != PAM_SUCCESS)
4316f11bdf1Schristos return (pam_err);
4322a62e4e1Schristos if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
4332a62e4e1Schristos pwd == NULL)
4346f11bdf1Schristos return (PAM_USER_UNKNOWN);
4356f11bdf1Schristos
4366f11bdf1Schristos /* start the agent */
437901ebd51Schristos pam_err = pam_ssh_start_agent(pamh, pwd);
438901ebd51Schristos if (pam_err != PAM_SUCCESS)
439901ebd51Schristos return pam_err;
440901ebd51Schristos
441901ebd51Schristos pam_err = openpam_borrow_cred(pamh, pwd);
442901ebd51Schristos if (pam_err != PAM_SUCCESS)
443901ebd51Schristos return pam_err;
4446f11bdf1Schristos
4456f11bdf1Schristos /* we have an agent, see if we can add any keys to it */
4466f11bdf1Schristos pam_err = pam_ssh_add_keys_to_agent(pamh);
4476f11bdf1Schristos if (pam_err != PAM_SUCCESS) {
4486f11bdf1Schristos /* XXX ignore failures */
449901ebd51Schristos openpam_log(PAM_LOG_DEBUG, "failed adding keys to ssh agent");
450901ebd51Schristos pam_err = PAM_SUCCESS;
4516f11bdf1Schristos }
4526f11bdf1Schristos
4536f11bdf1Schristos openpam_restore_cred(pamh);
454901ebd51Schristos return pam_err;
4556f11bdf1Schristos }
4566f11bdf1Schristos
4576f11bdf1Schristos PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)4586f11bdf1Schristos pam_sm_close_session(pam_handle_t *pamh, int flags __unused,
4596f11bdf1Schristos int argc __unused, const char *argv[] __unused)
4606f11bdf1Schristos {
4616f11bdf1Schristos const char *ssh_agent_pid;
4626f11bdf1Schristos char *end;
4636f11bdf1Schristos int status;
4646f11bdf1Schristos pid_t pid;
4656f11bdf1Schristos
4666f11bdf1Schristos if ((ssh_agent_pid = pam_getenv(pamh, "SSH_AGENT_PID")) == NULL) {
4676f11bdf1Schristos openpam_log(PAM_LOG_DEBUG, "no ssh agent");
4686f11bdf1Schristos return (PAM_SUCCESS);
4696f11bdf1Schristos }
4706f11bdf1Schristos pid = (pid_t)strtol(ssh_agent_pid, &end, 10);
4716f11bdf1Schristos if (*ssh_agent_pid == '\0' || *end != '\0') {
4726f11bdf1Schristos openpam_log(PAM_LOG_DEBUG, "invalid ssh agent pid");
4736f11bdf1Schristos return (PAM_SESSION_ERR);
4746f11bdf1Schristos }
4756f11bdf1Schristos openpam_log(PAM_LOG_DEBUG, "killing ssh agent %d", (int)pid);
4766f11bdf1Schristos if (kill(pid, SIGTERM) == -1 ||
4776f11bdf1Schristos (waitpid(pid, &status, 0) == -1 && errno != ECHILD))
4786f11bdf1Schristos return (PAM_SYSTEM_ERR);
4796f11bdf1Schristos return (PAM_SUCCESS);
4806f11bdf1Schristos }
4816f11bdf1Schristos
4826f11bdf1Schristos PAM_MODULE_ENTRY("pam_ssh");
483