1 1.30 hannken /* $NetBSD: pam_ssh.c,v 1.30 2022/06/15 08:31:34 hannken Exp $ */ 2 1.2 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2003 Networks Associates Technology, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This software was developed for the FreeBSD Project by ThinkSec AS and 8 1.1 christos * NAI Labs, the Security Research Division of Network Associates, Inc. 9 1.1 christos * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 10 1.1 christos * DARPA CHATS research program. 11 1.1 christos * 12 1.1 christos * Redistribution and use in source and binary forms, with or without 13 1.1 christos * modification, are permitted provided that the following conditions 14 1.1 christos * are met: 15 1.1 christos * 1. Redistributions of source code must retain the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer. 17 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 18 1.1 christos * notice, this list of conditions and the following disclaimer in the 19 1.1 christos * documentation and/or other materials provided with the distribution. 20 1.1 christos * 3. The name of the author may not be used to endorse or promote 21 1.1 christos * products derived from this software without specific prior written 22 1.1 christos * permission. 23 1.1 christos * 24 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 1.1 christos * SUCH DAMAGE. 35 1.1 christos */ 36 1.1 christos 37 1.1 christos #include <sys/cdefs.h> 38 1.3 lukem #ifdef __FreeBSD__ 39 1.1 christos __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_ssh/pam_ssh.c,v 1.40 2004/02/10 10:13:21 des Exp $"); 40 1.2 christos #else 41 1.30 hannken __RCSID("$NetBSD: pam_ssh.c,v 1.30 2022/06/15 08:31:34 hannken Exp $"); 42 1.2 christos #endif 43 1.1 christos 44 1.1 christos #include <sys/param.h> 45 1.1 christos #include <sys/wait.h> 46 1.1 christos 47 1.1 christos #include <errno.h> 48 1.1 christos #include <fcntl.h> 49 1.1 christos #include <paths.h> 50 1.1 christos #include <pwd.h> 51 1.1 christos #include <signal.h> 52 1.1 christos #include <stdio.h> 53 1.1 christos #include <string.h> 54 1.1 christos #include <unistd.h> 55 1.1 christos 56 1.1 christos #define PAM_SM_AUTH 57 1.1 christos #define PAM_SM_SESSION 58 1.1 christos 59 1.1 christos #include <security/pam_appl.h> 60 1.1 christos #include <security/pam_modules.h> 61 1.1 christos #include <security/openpam.h> 62 1.1 christos 63 1.1 christos #include <openssl/evp.h> 64 1.1 christos 65 1.26 christos #include "sshkey.h" 66 1.26 christos #include "sshbuf.h" 67 1.1 christos #include "authfd.h" 68 1.1 christos #include "authfile.h" 69 1.1 christos 70 1.18 drochner #define ssh_add_identity(auth, key, comment) \ 71 1.30 hannken ssh_add_identity_constrained(auth, key, comment, 0, 0, 0, NULL, NULL, 00) 72 1.18 drochner 73 1.1 christos extern char **environ; 74 1.1 christos 75 1.1 christos struct pam_ssh_key { 76 1.26 christos struct sshkey *key; 77 1.1 christos char *comment; 78 1.1 christos }; 79 1.1 christos 80 1.1 christos static const char *pam_ssh_prompt = "SSH passphrase: "; 81 1.1 christos static const char *pam_ssh_have_keys = "pam_ssh_have_keys"; 82 1.1 christos 83 1.1 christos static const char *pam_ssh_keyfiles[] = { 84 1.1 christos ".ssh/identity", /* SSH1 RSA key */ 85 1.1 christos ".ssh/id_rsa", /* SSH2 RSA key */ 86 1.1 christos ".ssh/id_dsa", /* SSH2 DSA key */ 87 1.20 drochner ".ssh/id_ecdsa", /* SSH2 ECDSA key */ 88 1.1 christos NULL 89 1.1 christos }; 90 1.1 christos 91 1.1 christos static const char *pam_ssh_agent = "/usr/bin/ssh-agent"; 92 1.18 drochner static const char *const pam_ssh_agent_argv[] = { "ssh_agent", "-s", NULL }; 93 1.18 drochner static const char *const pam_ssh_agent_envp[] = { NULL }; 94 1.1 christos 95 1.1 christos /* 96 1.1 christos * Attempts to load a private key from the specified file in the specified 97 1.1 christos * directory, using the specified passphrase. If successful, returns a 98 1.1 christos * struct pam_ssh_key containing the key and its comment. 99 1.1 christos */ 100 1.1 christos static struct pam_ssh_key * 101 1.19 drochner pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase, 102 1.19 drochner int nullok) 103 1.1 christos { 104 1.1 christos struct pam_ssh_key *psk; 105 1.1 christos char fn[PATH_MAX]; 106 1.26 christos int r; 107 1.1 christos char *comment; 108 1.26 christos struct sshkey *key; 109 1.1 christos 110 1.18 drochner if (snprintf(fn, sizeof(fn), "%s/%s", dir, kfn) > (int)sizeof(fn)) 111 1.1 christos return (NULL); 112 1.1 christos comment = NULL; 113 1.19 drochner /* 114 1.19 drochner * If the key is unencrypted, OpenSSL ignores the passphrase, so 115 1.19 drochner * it will seem like the user typed in the right one. This allows 116 1.19 drochner * a user to circumvent nullok by providing a dummy passphrase. 117 1.19 drochner * Verify that the key really *is* encrypted by trying to load it 118 1.19 drochner * with an empty passphrase, and if the key is not encrypted, 119 1.19 drochner * accept only an empty passphrase. 120 1.19 drochner */ 121 1.26 christos r = sshkey_load_private(fn, "", &key, &comment); 122 1.27 mlelstv if (r == 0 && !(*passphrase == '\0' && nullok)) { 123 1.27 mlelstv openpam_log(PAM_LOG_DEBUG, "rejected unencrypted key from %s", fn); 124 1.26 christos sshkey_free(key); 125 1.19 drochner free(comment); 126 1.19 drochner return (NULL); 127 1.19 drochner } 128 1.26 christos if (r) 129 1.27 mlelstv r = sshkey_load_private(fn, passphrase, &key, &comment); 130 1.26 christos if (r) { 131 1.17 drochner openpam_log(PAM_LOG_DEBUG, "failed to load key from %s", fn); 132 1.12 jnemeth if (comment != NULL) 133 1.12 jnemeth free(comment); 134 1.1 christos return (NULL); 135 1.1 christos } 136 1.1 christos 137 1.17 drochner openpam_log(PAM_LOG_DEBUG, "loaded '%s' from %s", comment, fn); 138 1.1 christos if ((psk = malloc(sizeof(*psk))) == NULL) { 139 1.26 christos sshkey_free(key); 140 1.1 christos free(comment); 141 1.1 christos return (NULL); 142 1.1 christos } 143 1.1 christos psk->key = key; 144 1.1 christos psk->comment = comment; 145 1.1 christos return (psk); 146 1.1 christos } 147 1.1 christos 148 1.1 christos /* 149 1.1 christos * Wipes a private key and frees the associated resources. 150 1.1 christos */ 151 1.1 christos static void 152 1.1 christos pam_ssh_free_key(pam_handle_t *pamh __unused, 153 1.1 christos void *data, int pam_err __unused) 154 1.1 christos { 155 1.1 christos struct pam_ssh_key *psk; 156 1.1 christos 157 1.1 christos psk = data; 158 1.26 christos sshkey_free(psk->key); 159 1.1 christos free(psk->comment); 160 1.1 christos free(psk); 161 1.1 christos } 162 1.1 christos 163 1.1 christos PAM_EXTERN int 164 1.1 christos pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 165 1.1 christos int argc __unused, const char *argv[] __unused) 166 1.1 christos { 167 1.1 christos const char **kfn, *passphrase, *user; 168 1.18 drochner const void *item; 169 1.10 thorpej struct passwd *pwd, pwres; 170 1.1 christos struct pam_ssh_key *psk; 171 1.19 drochner int nkeys, nullok, pam_err, pass; 172 1.10 thorpej char pwbuf[1024]; 173 1.1 christos 174 1.19 drochner nullok = (openpam_get_option(pamh, "nullok") != NULL); 175 1.19 drochner 176 1.1 christos /* PEM is not loaded by default */ 177 1.1 christos OpenSSL_add_all_algorithms(); 178 1.1 christos 179 1.1 christos /* get user name and home directory */ 180 1.1 christos pam_err = pam_get_user(pamh, &user, NULL); 181 1.1 christos if (pam_err != PAM_SUCCESS) 182 1.1 christos return (pam_err); 183 1.11 christos if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 || 184 1.11 christos pwd == NULL) 185 1.1 christos return (PAM_USER_UNKNOWN); 186 1.1 christos if (pwd->pw_dir == NULL) 187 1.1 christos return (PAM_AUTH_ERR); 188 1.1 christos 189 1.19 drochner nkeys = 0; 190 1.18 drochner pass = (pam_get_item(pamh, PAM_AUTHTOK, &item) == PAM_SUCCESS && 191 1.18 drochner item != NULL); 192 1.1 christos load_keys: 193 1.1 christos /* get passphrase */ 194 1.1 christos pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, 195 1.1 christos &passphrase, pam_ssh_prompt); 196 1.22 drochner if (pam_err != PAM_SUCCESS) 197 1.22 drochner return (pam_err); 198 1.22 drochner 199 1.22 drochner /* switch to user credentials */ 200 1.22 drochner pam_err = openpam_borrow_cred(pamh, pwd); 201 1.22 drochner if (pam_err != PAM_SUCCESS) 202 1.1 christos return (pam_err); 203 1.1 christos 204 1.1 christos /* try to load keys from all keyfiles we know of */ 205 1.1 christos for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) { 206 1.19 drochner psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase, nullok); 207 1.1 christos if (psk != NULL) { 208 1.1 christos pam_set_data(pamh, *kfn, psk, pam_ssh_free_key); 209 1.1 christos ++nkeys; 210 1.1 christos } 211 1.1 christos } 212 1.1 christos 213 1.22 drochner /* switch back to arbitrator credentials */ 214 1.22 drochner openpam_restore_cred(pamh); 215 1.22 drochner 216 1.1 christos /* 217 1.1 christos * If we tried an old token and didn't get anything, and 218 1.1 christos * try_first_pass was specified, try again after prompting the 219 1.1 christos * user for a new passphrase. 220 1.1 christos */ 221 1.1 christos if (nkeys == 0 && pass == 1 && 222 1.1 christos openpam_get_option(pamh, "try_first_pass") != NULL) { 223 1.1 christos pam_set_item(pamh, PAM_AUTHTOK, NULL); 224 1.1 christos pass = 0; 225 1.1 christos goto load_keys; 226 1.1 christos } 227 1.1 christos 228 1.1 christos /* no keys? */ 229 1.1 christos if (nkeys == 0) 230 1.1 christos return (PAM_AUTH_ERR); 231 1.1 christos 232 1.1 christos pam_set_data(pamh, pam_ssh_have_keys, NULL, NULL); 233 1.1 christos return (PAM_SUCCESS); 234 1.1 christos } 235 1.1 christos 236 1.1 christos PAM_EXTERN int 237 1.1 christos pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 238 1.1 christos int argc __unused, const char *argv[] __unused) 239 1.1 christos { 240 1.1 christos 241 1.1 christos return (PAM_SUCCESS); 242 1.1 christos } 243 1.1 christos 244 1.1 christos /* 245 1.1 christos * Parses a line from ssh-agent's output. 246 1.1 christos */ 247 1.1 christos static void 248 1.1 christos pam_ssh_process_agent_output(pam_handle_t *pamh, FILE *f) 249 1.1 christos { 250 1.1 christos char *line, *p, *key, *val; 251 1.1 christos size_t len; 252 1.1 christos 253 1.1 christos while ((line = fgetln(f, &len)) != NULL) { 254 1.1 christos if (len < 4 || strncmp(line, "SSH_", 4) != 0) 255 1.1 christos continue; 256 1.1 christos 257 1.1 christos /* find equal sign at end of key */ 258 1.1 christos for (p = key = line; p < line + len; ++p) 259 1.1 christos if (*p == '=') 260 1.1 christos break; 261 1.1 christos if (p == line + len || *p != '=') 262 1.1 christos continue; 263 1.1 christos *p = '\0'; 264 1.1 christos 265 1.1 christos /* find semicolon at end of value */ 266 1.1 christos for (val = ++p; p < line + len; ++p) 267 1.1 christos if (*p == ';') 268 1.1 christos break; 269 1.1 christos if (p == line + len || *p != ';') 270 1.1 christos continue; 271 1.1 christos *p = '\0'; 272 1.1 christos 273 1.1 christos /* store key-value pair in environment */ 274 1.1 christos openpam_log(PAM_LOG_DEBUG, "got %s: %s", key, val); 275 1.1 christos pam_setenv(pamh, key, val, 1); 276 1.1 christos } 277 1.1 christos } 278 1.1 christos 279 1.1 christos /* 280 1.1 christos * Starts an ssh agent and stores the environment variables derived from 281 1.1 christos * its output. 282 1.1 christos */ 283 1.1 christos static int 284 1.4 christos pam_ssh_start_agent(pam_handle_t *pamh, struct passwd *pwd) 285 1.1 christos { 286 1.1 christos int agent_pipe[2]; 287 1.1 christos pid_t pid; 288 1.1 christos FILE *f; 289 1.1 christos 290 1.1 christos /* get a pipe which we will use to read the agent's output */ 291 1.4 christos if (pipe(agent_pipe) == -1) 292 1.1 christos return (PAM_SYSTEM_ERR); 293 1.1 christos 294 1.1 christos /* start the agent */ 295 1.1 christos openpam_log(PAM_LOG_DEBUG, "starting an ssh agent"); 296 1.1 christos pid = fork(); 297 1.1 christos if (pid == (pid_t)-1) { 298 1.1 christos /* failed */ 299 1.1 christos close(agent_pipe[0]); 300 1.1 christos close(agent_pipe[1]); 301 1.1 christos return (PAM_SYSTEM_ERR); 302 1.1 christos } 303 1.1 christos if (pid == 0) { 304 1.2 christos #ifndef F_CLOSEM 305 1.1 christos int fd; 306 1.2 christos #endif 307 1.1 christos /* child: drop privs, close fds and start agent */ 308 1.4 christos if (setgid(pwd->pw_gid) == -1) { 309 1.21 christos openpam_log(PAM_LOG_DEBUG, "%s: Cannot setgid %d (%s)", 310 1.21 christos __func__, (int)pwd->pw_gid, strerror(errno)); 311 1.4 christos goto done; 312 1.4 christos } 313 1.4 christos if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { 314 1.4 christos openpam_log(PAM_LOG_DEBUG, 315 1.21 christos "%s: Cannot initgroups for %s (%s)", 316 1.21 christos __func__, pwd->pw_name, strerror(errno)); 317 1.4 christos goto done; 318 1.4 christos } 319 1.4 christos if (setuid(pwd->pw_uid) == -1) { 320 1.21 christos openpam_log(PAM_LOG_DEBUG, "%s: Cannot setuid %d (%s)", 321 1.21 christos __func__, (int)pwd->pw_uid, strerror(errno)); 322 1.4 christos goto done; 323 1.4 christos } 324 1.4 christos (void)close(STDIN_FILENO); 325 1.4 christos (void)open(_PATH_DEVNULL, O_RDONLY); 326 1.4 christos (void)dup2(agent_pipe[1], STDOUT_FILENO); 327 1.4 christos (void)dup2(agent_pipe[1], STDERR_FILENO); 328 1.2 christos #ifdef F_CLOSEM 329 1.2 christos (void)fcntl(3, F_CLOSEM, 0); 330 1.2 christos #else 331 1.1 christos for (fd = 3; fd < getdtablesize(); ++fd) 332 1.4 christos (void)close(fd); 333 1.2 christos #endif 334 1.4 christos (void)execve(pam_ssh_agent, 335 1.4 christos (char **)__UNCONST(pam_ssh_agent_argv), 336 1.2 christos (char **)__UNCONST(pam_ssh_agent_envp)); 337 1.4 christos done: 338 1.1 christos _exit(127); 339 1.1 christos } 340 1.1 christos 341 1.1 christos /* parent */ 342 1.1 christos close(agent_pipe[1]); 343 1.1 christos if ((f = fdopen(agent_pipe[0], "r")) == NULL) 344 1.1 christos return (PAM_SYSTEM_ERR); 345 1.1 christos pam_ssh_process_agent_output(pamh, f); 346 1.1 christos fclose(f); 347 1.1 christos 348 1.1 christos return (PAM_SUCCESS); 349 1.1 christos } 350 1.1 christos 351 1.1 christos /* 352 1.1 christos * Adds previously stored keys to a running agent. 353 1.1 christos */ 354 1.1 christos static int 355 1.1 christos pam_ssh_add_keys_to_agent(pam_handle_t *pamh) 356 1.1 christos { 357 1.15 christos const struct pam_ssh_key *psk; 358 1.1 christos const char **kfn; 359 1.1 christos char **envlist, **env; 360 1.1 christos int pam_err; 361 1.23 christos int agent_fd; 362 1.1 christos 363 1.1 christos /* switch to PAM environment */ 364 1.1 christos envlist = environ; 365 1.1 christos if ((environ = pam_getenvlist(pamh)) == NULL) { 366 1.4 christos openpam_log(PAM_LOG_DEBUG, "%s: cannot get envlist", 367 1.14 ragge __func__); 368 1.1 christos environ = envlist; 369 1.1 christos return (PAM_SYSTEM_ERR); 370 1.1 christos } 371 1.1 christos 372 1.1 christos /* get a connection to the agent */ 373 1.23 christos if (ssh_get_authentication_socket(&agent_fd) != 0) { 374 1.4 christos openpam_log(PAM_LOG_DEBUG, 375 1.4 christos "%s: cannot get authentication connection", 376 1.14 ragge __func__); 377 1.1 christos pam_err = PAM_SYSTEM_ERR; 378 1.23 christos agent_fd = -1; 379 1.1 christos goto end; 380 1.1 christos } 381 1.1 christos 382 1.1 christos /* look for keys to add to it */ 383 1.1 christos for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) { 384 1.15 christos const void *vp; 385 1.15 christos pam_err = pam_get_data(pamh, *kfn, &vp); 386 1.15 christos psk = vp; 387 1.1 christos if (pam_err == PAM_SUCCESS && psk != NULL) { 388 1.25 christos if (ssh_add_identity(agent_fd, psk->key, psk->comment)) 389 1.1 christos openpam_log(PAM_LOG_DEBUG, 390 1.1 christos "added %s to ssh agent", psk->comment); 391 1.1 christos else 392 1.1 christos openpam_log(PAM_LOG_DEBUG, "failed " 393 1.1 christos "to add %s to ssh agent", psk->comment); 394 1.1 christos /* we won't need the key again, so wipe it */ 395 1.1 christos pam_set_data(pamh, *kfn, NULL, NULL); 396 1.1 christos } 397 1.1 christos } 398 1.1 christos pam_err = PAM_SUCCESS; 399 1.1 christos end: 400 1.1 christos /* disconnect from agent */ 401 1.23 christos if (agent_fd != -1) 402 1.23 christos ssh_close_authentication_socket(agent_fd); 403 1.1 christos 404 1.1 christos /* switch back to original environment */ 405 1.1 christos for (env = environ; *env != NULL; ++env) 406 1.1 christos free(*env); 407 1.1 christos free(environ); 408 1.1 christos environ = envlist; 409 1.1 christos 410 1.1 christos return (pam_err); 411 1.1 christos } 412 1.1 christos 413 1.1 christos PAM_EXTERN int 414 1.1 christos pam_sm_open_session(pam_handle_t *pamh, int flags __unused, 415 1.1 christos int argc __unused, const char *argv[] __unused) 416 1.1 christos { 417 1.10 thorpej struct passwd *pwd, pwres; 418 1.1 christos const char *user; 419 1.15 christos const void *data; 420 1.4 christos int pam_err = PAM_SUCCESS; 421 1.10 thorpej char pwbuf[1024]; 422 1.1 christos 423 1.1 christos /* no keys, no work */ 424 1.1 christos if (pam_get_data(pamh, pam_ssh_have_keys, &data) != PAM_SUCCESS && 425 1.1 christos openpam_get_option(pamh, "want_agent") == NULL) 426 1.1 christos return (PAM_SUCCESS); 427 1.1 christos 428 1.1 christos /* switch to user credentials */ 429 1.1 christos pam_err = pam_get_user(pamh, &user, NULL); 430 1.1 christos if (pam_err != PAM_SUCCESS) 431 1.1 christos return (pam_err); 432 1.11 christos if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 || 433 1.11 christos pwd == NULL) 434 1.1 christos return (PAM_USER_UNKNOWN); 435 1.4 christos 436 1.4 christos /* start the agent */ 437 1.4 christos pam_err = pam_ssh_start_agent(pamh, pwd); 438 1.4 christos if (pam_err != PAM_SUCCESS) 439 1.4 christos return pam_err; 440 1.4 christos 441 1.1 christos pam_err = openpam_borrow_cred(pamh, pwd); 442 1.1 christos if (pam_err != PAM_SUCCESS) 443 1.4 christos return pam_err; 444 1.1 christos 445 1.1 christos /* we have an agent, see if we can add any keys to it */ 446 1.1 christos pam_err = pam_ssh_add_keys_to_agent(pamh); 447 1.1 christos if (pam_err != PAM_SUCCESS) { 448 1.1 christos /* XXX ignore failures */ 449 1.4 christos openpam_log(PAM_LOG_DEBUG, "failed adding keys to ssh agent"); 450 1.4 christos pam_err = PAM_SUCCESS; 451 1.1 christos } 452 1.1 christos 453 1.1 christos openpam_restore_cred(pamh); 454 1.4 christos return pam_err; 455 1.1 christos } 456 1.1 christos 457 1.1 christos PAM_EXTERN int 458 1.1 christos pam_sm_close_session(pam_handle_t *pamh, int flags __unused, 459 1.1 christos int argc __unused, const char *argv[] __unused) 460 1.1 christos { 461 1.1 christos const char *ssh_agent_pid; 462 1.1 christos char *end; 463 1.1 christos int status; 464 1.1 christos pid_t pid; 465 1.1 christos 466 1.1 christos if ((ssh_agent_pid = pam_getenv(pamh, "SSH_AGENT_PID")) == NULL) { 467 1.1 christos openpam_log(PAM_LOG_DEBUG, "no ssh agent"); 468 1.1 christos return (PAM_SUCCESS); 469 1.1 christos } 470 1.1 christos pid = (pid_t)strtol(ssh_agent_pid, &end, 10); 471 1.1 christos if (*ssh_agent_pid == '\0' || *end != '\0') { 472 1.1 christos openpam_log(PAM_LOG_DEBUG, "invalid ssh agent pid"); 473 1.1 christos return (PAM_SESSION_ERR); 474 1.1 christos } 475 1.1 christos openpam_log(PAM_LOG_DEBUG, "killing ssh agent %d", (int)pid); 476 1.1 christos if (kill(pid, SIGTERM) == -1 || 477 1.1 christos (waitpid(pid, &status, 0) == -1 && errno != ECHILD)) 478 1.1 christos return (PAM_SYSTEM_ERR); 479 1.1 christos return (PAM_SUCCESS); 480 1.1 christos } 481 1.1 christos 482 1.1 christos PAM_MODULE_ENTRY("pam_ssh"); 483