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