File: | src/usr.bin/ssh/ssh-agent/../ssh-agent.c |
Warning: | line 1991, column 2 The return value from the call to 'setgid' is not checked. If an error occurs in 'setgid', the following code may execute with unexpected privileges |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ssh-agent.c,v 1.286 2022/01/12 03:30:32 dtucker Exp $ */ |
2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 | * All rights reserved |
6 | * The authentication agent program. |
7 | * |
8 | * As far as I am concerned, the code I have written for this software |
9 | * can be used freely for any purpose. Any derived versions of this |
10 | * software must be clearly marked as such, and if the derived work is |
11 | * incompatible with the protocol description in the RFC file, it must be |
12 | * called by a name other than "ssh" or "Secure Shell". |
13 | * |
14 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. |
15 | * |
16 | * Redistribution and use in source and binary forms, with or without |
17 | * modification, are permitted provided that the following conditions |
18 | * are met: |
19 | * 1. Redistributions of source code must retain the above copyright |
20 | * notice, this list of conditions and the following disclaimer. |
21 | * 2. Redistributions in binary form must reproduce the above copyright |
22 | * notice, this list of conditions and the following disclaimer in the |
23 | * documentation and/or other materials provided with the distribution. |
24 | * |
25 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
26 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
27 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
28 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
30 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
32 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
33 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
34 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
35 | */ |
36 | |
37 | #include <sys/types.h> |
38 | #include <sys/time.h> |
39 | #include <sys/queue.h> |
40 | #include <sys/resource.h> |
41 | #include <sys/socket.h> |
42 | #include <sys/stat.h> |
43 | #include <sys/un.h> |
44 | #include <sys/wait.h> |
45 | |
46 | #ifdef WITH_OPENSSL1 |
47 | #include <openssl/evp.h> |
48 | #endif |
49 | |
50 | #include <errno(*__errno()).h> |
51 | #include <fcntl.h> |
52 | #include <paths.h> |
53 | #include <poll.h> |
54 | #include <signal.h> |
55 | #include <stdlib.h> |
56 | #include <stdio.h> |
57 | #include <string.h> |
58 | #include <stdarg.h> |
59 | #include <limits.h> |
60 | #include <time.h> |
61 | #include <unistd.h> |
62 | #include <util.h> |
63 | |
64 | #include "xmalloc.h" |
65 | #include "ssh.h" |
66 | #include "ssh2.h" |
67 | #include "sshbuf.h" |
68 | #include "sshkey.h" |
69 | #include "authfd.h" |
70 | #include "compat.h" |
71 | #include "log.h" |
72 | #include "misc.h" |
73 | #include "digest.h" |
74 | #include "ssherr.h" |
75 | #include "match.h" |
76 | #include "msg.h" |
77 | #include "ssherr.h" |
78 | #include "pathnames.h" |
79 | #include "ssh-pkcs11.h" |
80 | #include "sk-api.h" |
81 | #include "myproposal.h" |
82 | |
83 | #ifndef DEFAULT_ALLOWED_PROVIDERS"/usr/lib*/*,/usr/local/lib*/*" |
84 | # define DEFAULT_ALLOWED_PROVIDERS"/usr/lib*/*,/usr/local/lib*/*" "/usr/lib*/*,/usr/local/lib*/*" |
85 | #endif |
86 | |
87 | /* Maximum accepted message length */ |
88 | #define AGENT_MAX_LEN(256*1024) (256*1024) |
89 | /* Maximum bytes to read from client socket */ |
90 | #define AGENT_RBUF_LEN(4096) (4096) |
91 | /* Maximum number of recorded session IDs/hostkeys per connection */ |
92 | #define AGENT_MAX_SESSION_IDS16 16 |
93 | /* Maximum size of session ID */ |
94 | #define AGENT_MAX_SID_LEN128 128 |
95 | /* Maximum number of destination constraints to accept on a key */ |
96 | #define AGENT_MAX_DEST_CONSTRAINTS1024 1024 |
97 | |
98 | /* XXX store hostkey_sid in a refcounted tree */ |
99 | |
100 | typedef enum { |
101 | AUTH_UNUSED = 0, |
102 | AUTH_SOCKET = 1, |
103 | AUTH_CONNECTION = 2, |
104 | } sock_type; |
105 | |
106 | struct hostkey_sid { |
107 | struct sshkey *key; |
108 | struct sshbuf *sid; |
109 | int forwarded; |
110 | }; |
111 | |
112 | typedef struct socket_entry { |
113 | int fd; |
114 | sock_type type; |
115 | struct sshbuf *input; |
116 | struct sshbuf *output; |
117 | struct sshbuf *request; |
118 | size_t nsession_ids; |
119 | struct hostkey_sid *session_ids; |
120 | } SocketEntry; |
121 | |
122 | u_int sockets_alloc = 0; |
123 | SocketEntry *sockets = NULL((void*)0); |
124 | |
125 | typedef struct identity { |
126 | TAILQ_ENTRY(identity)struct { struct identity *tqe_next; struct identity **tqe_prev ; } next; |
127 | struct sshkey *key; |
128 | char *comment; |
129 | char *provider; |
130 | time_t death; |
131 | u_int confirm; |
132 | char *sk_provider; |
133 | struct dest_constraint *dest_constraints; |
134 | size_t ndest_constraints; |
135 | } Identity; |
136 | |
137 | struct idtable { |
138 | int nentries; |
139 | TAILQ_HEAD(idqueue, identity)struct idqueue { struct identity *tqh_first; struct identity * *tqh_last; } idlist; |
140 | }; |
141 | |
142 | /* private key table */ |
143 | struct idtable *idtab; |
144 | |
145 | int max_fd = 0; |
146 | |
147 | /* pid of shell == parent of agent */ |
148 | pid_t parent_pid = -1; |
149 | time_t parent_alive_interval = 0; |
150 | |
151 | /* pid of process for which cleanup_socket is applicable */ |
152 | pid_t cleanup_pid = 0; |
153 | |
154 | /* pathname and directory for AUTH_SOCKET */ |
155 | char socket_name[PATH_MAX1024]; |
156 | char socket_dir[PATH_MAX1024]; |
157 | |
158 | /* Pattern-list of allowed PKCS#11/Security key paths */ |
159 | static char *allowed_providers; |
160 | |
161 | /* locking */ |
162 | #define LOCK_SIZE32 32 |
163 | #define LOCK_SALT_SIZE16 16 |
164 | #define LOCK_ROUNDS1 1 |
165 | int locked = 0; |
166 | u_char lock_pwhash[LOCK_SIZE32]; |
167 | u_char lock_salt[LOCK_SALT_SIZE16]; |
168 | |
169 | extern char *__progname; |
170 | |
171 | /* Default lifetime in seconds (0 == forever) */ |
172 | static int lifetime = 0; |
173 | |
174 | static int fingerprint_hash = SSH_FP_HASH_DEFAULT2; |
175 | |
176 | /* Refuse signing of non-SSH messages for web-origin FIDO keys */ |
177 | static int restrict_websafe = 1; |
178 | |
179 | static void |
180 | close_socket(SocketEntry *e) |
181 | { |
182 | size_t i; |
183 | |
184 | close(e->fd); |
185 | sshbuf_free(e->input); |
186 | sshbuf_free(e->output); |
187 | sshbuf_free(e->request); |
188 | for (i = 0; i < e->nsession_ids; i++) { |
189 | sshkey_free(e->session_ids[i].key); |
190 | sshbuf_free(e->session_ids[i].sid); |
191 | } |
192 | free(e->session_ids); |
193 | memset(e, '\0', sizeof(*e)); |
194 | e->fd = -1; |
195 | e->type = AUTH_UNUSED; |
196 | } |
197 | |
198 | static void |
199 | idtab_init(void) |
200 | { |
201 | idtab = xcalloc(1, sizeof(*idtab)); |
202 | TAILQ_INIT(&idtab->idlist)do { (&idtab->idlist)->tqh_first = ((void*)0); (& idtab->idlist)->tqh_last = &(&idtab->idlist) ->tqh_first; } while (0); |
203 | idtab->nentries = 0; |
204 | } |
205 | |
206 | static void |
207 | free_dest_constraint_hop(struct dest_constraint_hop *dch) |
208 | { |
209 | u_int i; |
210 | |
211 | if (dch == NULL((void*)0)) |
212 | return; |
213 | free(dch->user); |
214 | free(dch->hostname); |
215 | for (i = 0; i < dch->nkeys; i++) |
216 | sshkey_free(dch->keys[i]); |
217 | free(dch->keys); |
218 | free(dch->key_is_ca); |
219 | } |
220 | |
221 | static void |
222 | free_dest_constraints(struct dest_constraint *dcs, size_t ndcs) |
223 | { |
224 | size_t i; |
225 | |
226 | for (i = 0; i < ndcs; i++) { |
227 | free_dest_constraint_hop(&dcs[i].from); |
228 | free_dest_constraint_hop(&dcs[i].to); |
229 | } |
230 | free(dcs); |
231 | } |
232 | |
233 | static void |
234 | free_identity(Identity *id) |
235 | { |
236 | sshkey_free(id->key); |
237 | free(id->provider); |
238 | free(id->comment); |
239 | free(id->sk_provider); |
240 | free_dest_constraints(id->dest_constraints, id->ndest_constraints); |
241 | free(id); |
242 | } |
243 | |
244 | /* |
245 | * Match 'key' against the key/CA list in a destination constraint hop |
246 | * Returns 0 on success or -1 otherwise. |
247 | */ |
248 | static int |
249 | match_key_hop(const char *tag, const struct sshkey *key, |
250 | const struct dest_constraint_hop *dch) |
251 | { |
252 | const char *reason = NULL((void*)0); |
253 | const char *hostname = dch->hostname ? dch->hostname : "(ORIGIN)"; |
254 | u_int i; |
255 | char *fp; |
256 | |
257 | if (key == NULL((void*)0)) |
258 | return -1; |
259 | /* XXX logspam */ |
260 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT2, |
261 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
262 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 262, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
263 | debug3_f("%s: entering hostname %s, requested key %s %s, %u keys avail",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 264, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s: entering hostname %s, requested key %s %s, %u keys avail" , tag, hostname, sshkey_type(key), fp, dch->nkeys) |
264 | tag, hostname, sshkey_type(key), fp, dch->nkeys)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 264, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s: entering hostname %s, requested key %s %s, %u keys avail" , tag, hostname, sshkey_type(key), fp, dch->nkeys); |
265 | free(fp); |
266 | for (i = 0; i < dch->nkeys; i++) { |
267 | if (dch->keys[i] == NULL((void*)0)) |
268 | return -1; |
269 | /* XXX logspam */ |
270 | if ((fp = sshkey_fingerprint(dch->keys[i], SSH_FP_HASH_DEFAULT2, |
271 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
272 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 272, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
273 | debug3_f("%s: key %u: %s%s %s", tag, i,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 275, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s: key %u: %s%s %s" , tag, i, dch->key_is_ca[i] ? "CA " : "", sshkey_type(dch-> keys[i]), fp) |
274 | dch->key_is_ca[i] ? "CA " : "",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 275, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s: key %u: %s%s %s" , tag, i, dch->key_is_ca[i] ? "CA " : "", sshkey_type(dch-> keys[i]), fp) |
275 | sshkey_type(dch->keys[i]), fp)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 275, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s: key %u: %s%s %s" , tag, i, dch->key_is_ca[i] ? "CA " : "", sshkey_type(dch-> keys[i]), fp); |
276 | free(fp); |
277 | if (!sshkey_is_cert(key)) { |
278 | /* plain key */ |
279 | if (dch->key_is_ca[i] || |
280 | !sshkey_equal(key, dch->keys[i])) |
281 | continue; |
282 | return 0; |
283 | } |
284 | /* certificate */ |
285 | if (!dch->key_is_ca[i]) |
286 | continue; |
287 | if (key->cert == NULL((void*)0) || key->cert->signature_key == NULL((void*)0)) |
288 | return -1; /* shouldn't happen */ |
289 | if (!sshkey_equal(key->cert->signature_key, dch->keys[i])) |
290 | continue; |
291 | if (sshkey_cert_check_host(key, hostname, 1, |
292 | SSH_ALLOWED_CA_SIGALGS"ssh-ed25519," "ecdsa-sha2-nistp256," "ecdsa-sha2-nistp384," "ecdsa-sha2-nistp521," "sk-ssh-ed25519@openssh.com," "sk-ecdsa-sha2-nistp256@openssh.com," "rsa-sha2-512," "rsa-sha2-256", &reason) != 0) { |
293 | debug_f("cert %s / hostname %s rejected: %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 294, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "cert %s / hostname %s rejected: %s" , key->cert->key_id, hostname, reason) |
294 | key->cert->key_id, hostname, reason)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 294, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "cert %s / hostname %s rejected: %s" , key->cert->key_id, hostname, reason); |
295 | continue; |
296 | } |
297 | return 0; |
298 | } |
299 | return -1; |
300 | } |
301 | |
302 | /* Check destination constraints on an identity against the hostkey/user */ |
303 | static int |
304 | permitted_by_dest_constraints(const struct sshkey *fromkey, |
305 | const struct sshkey *tokey, Identity *id, const char *user, |
306 | const char **hostnamep) |
307 | { |
308 | size_t i; |
309 | struct dest_constraint *d; |
310 | |
311 | if (hostnamep != NULL((void*)0)) |
312 | *hostnamep = NULL((void*)0); |
313 | for (i = 0; i < id->ndest_constraints; i++) { |
314 | d = id->dest_constraints + i; |
315 | /* XXX remove logspam */ |
316 | debug2_f("constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys) |
317 | i, d->from.user ? d->from.user : "",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys) |
318 | d->from.user ? "@" : "",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys) |
319 | d->from.hostname ? d->from.hostname : "(ORIGIN)",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys) |
320 | d->from.nkeys,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys) |
321 | d->to.user ? d->to.user : "", d->to.user ? "@" : "",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys) |
322 | d->to.hostname ? d->to.hostname : "(ANY)", d->to.nkeys)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 322, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)" , i, d->from.user ? d->from.user : "", d->from.user ? "@" : "", d->from.hostname ? d->from.hostname : "(ORIGIN)" , d->from.nkeys, d->to.user ? d->to.user : "", d-> to.user ? "@" : "", d->to.hostname ? d->to.hostname : "(ANY)" , d->to.nkeys); |
323 | |
324 | /* Match 'from' key */ |
325 | if (fromkey == NULL((void*)0)) { |
326 | /* We are matching the first hop */ |
327 | if (d->from.hostname != NULL((void*)0) || d->from.nkeys != 0) |
328 | continue; |
329 | } else if (match_key_hop("from", fromkey, &d->from) != 0) |
330 | continue; |
331 | |
332 | /* Match 'to' key */ |
333 | if (tokey != NULL((void*)0) && match_key_hop("to", tokey, &d->to) != 0) |
334 | continue; |
335 | |
336 | /* Match user if specified */ |
337 | if (d->to.user != NULL((void*)0) && user != NULL((void*)0) && |
338 | !match_pattern(user, d->to.user)) |
339 | continue; |
340 | |
341 | /* successfully matched this constraint */ |
342 | if (hostnamep != NULL((void*)0)) |
343 | *hostnamep = d->to.hostname; |
344 | debug2_f("allowed for hostname %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 345, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "allowed for hostname %s" , d->to.hostname == ((void*)0) ? "*" : d->to.hostname) |
345 | d->to.hostname == NULL ? "*" : d->to.hostname)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 345, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "allowed for hostname %s" , d->to.hostname == ((void*)0) ? "*" : d->to.hostname); |
346 | return 0; |
347 | } |
348 | /* no match */ |
349 | debug2_f("%s identity \"%s\" not permitted for this destination",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 350, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "%s identity \"%s\" not permitted for this destination" , sshkey_type(id->key), id->comment) |
350 | sshkey_type(id->key), id->comment)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 350, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "%s identity \"%s\" not permitted for this destination" , sshkey_type(id->key), id->comment); |
351 | return -1; |
352 | } |
353 | |
354 | /* |
355 | * Check whether hostkeys on a SocketEntry and the optionally specified user |
356 | * are permitted by the destination constraints on the Identity. |
357 | * Returns 0 on success or -1 otherwise. |
358 | */ |
359 | static int |
360 | identity_permitted(Identity *id, SocketEntry *e, char *user, |
361 | const char **forward_hostnamep, const char **last_hostnamep) |
362 | { |
363 | size_t i; |
364 | const char **hp; |
365 | struct hostkey_sid *hks; |
366 | const struct sshkey *fromkey = NULL((void*)0); |
367 | const char *test_user; |
368 | char *fp1, *fp2; |
369 | |
370 | /* XXX remove logspam */ |
371 | debug3_f("entering: key %s comment \"%s\", %zu socket bindings, "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 373, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "entering: key %s comment \"%s\", %zu socket bindings, " "%zu constraints", sshkey_type(id->key), id->comment, e ->nsession_ids, id->ndest_constraints) |
372 | "%zu constraints", sshkey_type(id->key), id->comment,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 373, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "entering: key %s comment \"%s\", %zu socket bindings, " "%zu constraints", sshkey_type(id->key), id->comment, e ->nsession_ids, id->ndest_constraints) |
373 | e->nsession_ids, id->ndest_constraints)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 373, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "entering: key %s comment \"%s\", %zu socket bindings, " "%zu constraints", sshkey_type(id->key), id->comment, e ->nsession_ids, id->ndest_constraints); |
374 | if (id->ndest_constraints == 0) |
375 | return 0; /* unconstrained */ |
376 | if (e->nsession_ids == 0) |
377 | return 0; /* local use */ |
378 | /* |
379 | * Walk through the hops recorded by session_id and try to find a |
380 | * constraint that satisfies each. |
381 | */ |
382 | for (i = 0; i < e->nsession_ids; i++) { |
383 | hks = e->session_ids + i; |
384 | if (hks->key == NULL((void*)0)) |
385 | fatal_f("internal error: no bound key")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 385, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "internal error: no bound key" ); |
386 | /* XXX remove logspam */ |
387 | fp1 = fp2 = NULL((void*)0); |
388 | if (fromkey != NULL((void*)0) && |
389 | (fp1 = sshkey_fingerprint(fromkey, SSH_FP_HASH_DEFAULT2, |
390 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
391 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 391, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
392 | if ((fp2 = sshkey_fingerprint(hks->key, SSH_FP_HASH_DEFAULT2, |
393 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
394 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 394, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
395 | debug3_f("socketentry fd=%d, entry %zu %s, "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 400, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "socketentry fd=%d, entry %zu %s, " "from hostkey %s %s to user %s hostkey %s %s", e->fd, i, hks ->forwarded ? "FORWARD" : "AUTH", fromkey ? sshkey_type(fromkey ) : "(ORIGIN)", fromkey ? fp1 : "", user ? user : "(ANY)", sshkey_type (hks->key), fp2) |
396 | "from hostkey %s %s to user %s hostkey %s %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 400, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "socketentry fd=%d, entry %zu %s, " "from hostkey %s %s to user %s hostkey %s %s", e->fd, i, hks ->forwarded ? "FORWARD" : "AUTH", fromkey ? sshkey_type(fromkey ) : "(ORIGIN)", fromkey ? fp1 : "", user ? user : "(ANY)", sshkey_type (hks->key), fp2) |
397 | e->fd, i, hks->forwarded ? "FORWARD" : "AUTH",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 400, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "socketentry fd=%d, entry %zu %s, " "from hostkey %s %s to user %s hostkey %s %s", e->fd, i, hks ->forwarded ? "FORWARD" : "AUTH", fromkey ? sshkey_type(fromkey ) : "(ORIGIN)", fromkey ? fp1 : "", user ? user : "(ANY)", sshkey_type (hks->key), fp2) |
398 | fromkey ? sshkey_type(fromkey) : "(ORIGIN)",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 400, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "socketentry fd=%d, entry %zu %s, " "from hostkey %s %s to user %s hostkey %s %s", e->fd, i, hks ->forwarded ? "FORWARD" : "AUTH", fromkey ? sshkey_type(fromkey ) : "(ORIGIN)", fromkey ? fp1 : "", user ? user : "(ANY)", sshkey_type (hks->key), fp2) |
399 | fromkey ? fp1 : "", user ? user : "(ANY)",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 400, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "socketentry fd=%d, entry %zu %s, " "from hostkey %s %s to user %s hostkey %s %s", e->fd, i, hks ->forwarded ? "FORWARD" : "AUTH", fromkey ? sshkey_type(fromkey ) : "(ORIGIN)", fromkey ? fp1 : "", user ? user : "(ANY)", sshkey_type (hks->key), fp2) |
400 | sshkey_type(hks->key), fp2)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 400, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "socketentry fd=%d, entry %zu %s, " "from hostkey %s %s to user %s hostkey %s %s", e->fd, i, hks ->forwarded ? "FORWARD" : "AUTH", fromkey ? sshkey_type(fromkey ) : "(ORIGIN)", fromkey ? fp1 : "", user ? user : "(ANY)", sshkey_type (hks->key), fp2); |
401 | free(fp1); |
402 | free(fp2); |
403 | /* |
404 | * Record the hostnames for the initial forwarding and |
405 | * the final destination. |
406 | */ |
407 | hp = NULL((void*)0); |
408 | if (i == e->nsession_ids - 1) |
409 | hp = last_hostnamep; |
410 | else if (i == 0) |
411 | hp = forward_hostnamep; |
412 | /* Special handling for final recorded binding */ |
413 | test_user = NULL((void*)0); |
414 | if (i == e->nsession_ids - 1) { |
415 | /* Can only check user at final hop */ |
416 | test_user = user; |
417 | /* |
418 | * user is only presented for signature requests. |
419 | * If this is the case, make sure last binding is not |
420 | * for a forwarding. |
421 | */ |
422 | if (hks->forwarded && user != NULL((void*)0)) { |
423 | error_f("tried to sign on forwarding hop")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 423, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "tried to sign on forwarding hop" ); |
424 | return -1; |
425 | } |
426 | } else if (!hks->forwarded) { |
427 | error_f("tried to forward though signing bind")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 427, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "tried to forward though signing bind" ); |
428 | return -1; |
429 | } |
430 | if (permitted_by_dest_constraints(fromkey, hks->key, id, |
431 | test_user, hp) != 0) |
432 | return -1; |
433 | fromkey = hks->key; |
434 | } |
435 | /* |
436 | * Another special case: if the last bound session ID was for a |
437 | * forwarding, and this function is not being called to check a sign |
438 | * request (i.e. no 'user' supplied), then only permit the key if |
439 | * there is a permission that would allow it to be used at another |
440 | * destination. This hides keys that are allowed to be used to |
441 | * authenticate *to* a host but not permitted for *use* beyond it. |
442 | */ |
443 | hks = &e->session_ids[e->nsession_ids - 1]; |
444 | if (hks->forwarded && user == NULL((void*)0) && |
445 | permitted_by_dest_constraints(hks->key, NULL((void*)0), id, |
446 | NULL((void*)0), NULL((void*)0)) != 0) { |
447 | debug3_f("key permitted at host but not after")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 447, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "key permitted at host but not after" ); |
448 | return -1; |
449 | } |
450 | |
451 | /* success */ |
452 | return 0; |
453 | } |
454 | |
455 | /* return matching private key for given public key */ |
456 | static Identity * |
457 | lookup_identity(struct sshkey *key) |
458 | { |
459 | Identity *id; |
460 | |
461 | TAILQ_FOREACH(id, &idtab->idlist, next)for((id) = ((&idtab->idlist)->tqh_first); (id) != ( (void*)0); (id) = ((id)->next.tqe_next)) { |
462 | if (sshkey_equal(key, id->key)) |
463 | return (id); |
464 | } |
465 | return (NULL((void*)0)); |
466 | } |
467 | |
468 | /* Check confirmation of keysign request */ |
469 | static int |
470 | confirm_key(Identity *id, const char *extra) |
471 | { |
472 | char *p; |
473 | int ret = -1; |
474 | |
475 | p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT); |
476 | if (p != NULL((void*)0) && |
477 | ask_permission("Allow use of key %s?\nKey fingerprint %s.%s%s", |
478 | id->comment, p, |
479 | extra == NULL((void*)0) ? "" : "\n", extra == NULL((void*)0) ? "" : extra)) |
480 | ret = 0; |
481 | free(p); |
482 | |
483 | return (ret); |
484 | } |
485 | |
486 | static void |
487 | send_status(SocketEntry *e, int success) |
488 | { |
489 | int r; |
490 | |
491 | if ((r = sshbuf_put_u32(e->output, 1)) != 0 || |
492 | (r = sshbuf_put_u8(e->output, success ? |
493 | SSH_AGENT_SUCCESS6 : SSH_AGENT_FAILURE5)) != 0) |
494 | fatal_fr(r, "compose")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 494, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "compose"); |
495 | } |
496 | |
497 | /* send list of supported public keys to 'client' */ |
498 | static void |
499 | process_request_identities(SocketEntry *e) |
500 | { |
501 | Identity *id; |
502 | struct sshbuf *msg, *keys; |
503 | int r; |
504 | u_int nentries = 0; |
505 | |
506 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 506, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
507 | |
508 | if ((msg = sshbuf_new()) == NULL((void*)0) || (keys = sshbuf_new()) == NULL((void*)0)) |
509 | fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 509, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_new failed" ); |
510 | TAILQ_FOREACH(id, &idtab->idlist, next)for((id) = ((&idtab->idlist)->tqh_first); (id) != ( (void*)0); (id) = ((id)->next.tqe_next)) { |
511 | /* identity not visible, don't include in response */ |
512 | if (identity_permitted(id, e, NULL((void*)0), NULL((void*)0), NULL((void*)0)) != 0) |
513 | continue; |
514 | if ((r = sshkey_puts_opts(id->key, keys, |
515 | SSHKEY_SERIALIZE_INFO)) != 0 || |
516 | (r = sshbuf_put_cstring(keys, id->comment)) != 0) { |
517 | error_fr(r, "compose key/comment")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 517, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "compose key/comment" ); |
518 | continue; |
519 | } |
520 | nentries++; |
521 | } |
522 | debug2_f("replying with %u allowed of %u available keys",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 523, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "replying with %u allowed of %u available keys" , nentries, idtab->nentries) |
523 | nentries, idtab->nentries)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 523, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "replying with %u allowed of %u available keys" , nentries, idtab->nentries); |
524 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER12)) != 0 || |
525 | (r = sshbuf_put_u32(msg, nentries)) != 0 || |
526 | (r = sshbuf_putb(msg, keys)) != 0) |
527 | fatal_fr(r, "compose")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 527, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "compose"); |
528 | if ((r = sshbuf_put_stringb(e->output, msg)) != 0) |
529 | fatal_fr(r, "enqueue")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 529, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "enqueue"); |
530 | sshbuf_free(msg); |
531 | sshbuf_free(keys); |
532 | } |
533 | |
534 | |
535 | static char * |
536 | agent_decode_alg(struct sshkey *key, u_int flags) |
537 | { |
538 | if (key->type == KEY_RSA) { |
539 | if (flags & SSH_AGENT_RSA_SHA2_2560x02) |
540 | return "rsa-sha2-256"; |
541 | else if (flags & SSH_AGENT_RSA_SHA2_5120x04) |
542 | return "rsa-sha2-512"; |
543 | } else if (key->type == KEY_RSA_CERT) { |
544 | if (flags & SSH_AGENT_RSA_SHA2_2560x02) |
545 | return "rsa-sha2-256-cert-v01@openssh.com"; |
546 | else if (flags & SSH_AGENT_RSA_SHA2_5120x04) |
547 | return "rsa-sha2-512-cert-v01@openssh.com"; |
548 | } |
549 | return NULL((void*)0); |
550 | } |
551 | |
552 | /* |
553 | * Attempt to parse the contents of a buffer as a SSH publickey userauth |
554 | * request, checking its contents for consistency and matching the embedded |
555 | * key against the one that is being used for signing. |
556 | * Note: does not modify msg buffer. |
557 | * Optionally extract the username, session ID and/or hostkey from the request. |
558 | */ |
559 | static int |
560 | parse_userauth_request(struct sshbuf *msg, const struct sshkey *expected_key, |
561 | char **userp, struct sshbuf **sess_idp, struct sshkey **hostkeyp) |
562 | { |
563 | struct sshbuf *b = NULL((void*)0), *sess_id = NULL((void*)0); |
564 | char *user = NULL((void*)0), *service = NULL((void*)0), *method = NULL((void*)0), *pkalg = NULL((void*)0); |
565 | int r; |
566 | u_char t, sig_follows; |
567 | struct sshkey *mkey = NULL((void*)0), *hostkey = NULL((void*)0); |
568 | |
569 | if (userp != NULL((void*)0)) |
570 | *userp = NULL((void*)0); |
571 | if (sess_idp != NULL((void*)0)) |
572 | *sess_idp = NULL((void*)0); |
573 | if (hostkeyp != NULL((void*)0)) |
574 | *hostkeyp = NULL((void*)0); |
575 | if ((b = sshbuf_fromb(msg)) == NULL((void*)0)) |
576 | fatal_f("sshbuf_fromb")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 576, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_fromb"); |
577 | |
578 | /* SSH userauth request */ |
579 | if ((r = sshbuf_froms(b, &sess_id)) != 0) |
580 | goto out; |
581 | if (sshbuf_len(sess_id) == 0) { |
582 | r = SSH_ERR_INVALID_FORMAT-4; |
583 | goto out; |
584 | } |
585 | if ((r = sshbuf_get_u8(b, &t)) != 0 || /* SSH2_MSG_USERAUTH_REQUEST */ |
586 | (r = sshbuf_get_cstring(b, &user, NULL((void*)0))) != 0 || /* server user */ |
587 | (r = sshbuf_get_cstring(b, &service, NULL((void*)0))) != 0 || /* service */ |
588 | (r = sshbuf_get_cstring(b, &method, NULL((void*)0))) != 0 || /* method */ |
589 | (r = sshbuf_get_u8(b, &sig_follows)) != 0 || /* sig-follows */ |
590 | (r = sshbuf_get_cstring(b, &pkalg, NULL((void*)0))) != 0 || /* alg */ |
591 | (r = sshkey_froms(b, &mkey)) != 0) /* key */ |
592 | goto out; |
593 | if (t != SSH2_MSG_USERAUTH_REQUEST50 || |
594 | sig_follows != 1 || |
595 | strcmp(service, "ssh-connection") != 0 || |
596 | !sshkey_equal(expected_key, mkey) || |
597 | sshkey_type_from_name(pkalg) != expected_key->type) { |
598 | r = SSH_ERR_INVALID_FORMAT-4; |
599 | goto out; |
600 | } |
601 | if (strcmp(method, "publickey-hostbound-v00@openssh.com") == 0) { |
602 | if ((r = sshkey_froms(b, &hostkey)) != 0) |
603 | goto out; |
604 | } else if (strcmp(method, "publickey") != 0) { |
605 | r = SSH_ERR_INVALID_FORMAT-4; |
606 | goto out; |
607 | } |
608 | if (sshbuf_len(b) != 0) { |
609 | r = SSH_ERR_INVALID_FORMAT-4; |
610 | goto out; |
611 | } |
612 | /* success */ |
613 | r = 0; |
614 | debug3_f("well formed userauth")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 614, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "well formed userauth" ); |
615 | if (userp != NULL((void*)0)) { |
616 | *userp = user; |
617 | user = NULL((void*)0); |
618 | } |
619 | if (sess_idp != NULL((void*)0)) { |
620 | *sess_idp = sess_id; |
621 | sess_id = NULL((void*)0); |
622 | } |
623 | if (hostkeyp != NULL((void*)0)) { |
624 | *hostkeyp = hostkey; |
625 | hostkey = NULL((void*)0); |
626 | } |
627 | out: |
628 | sshbuf_free(b); |
629 | sshbuf_free(sess_id); |
630 | free(user); |
631 | free(service); |
632 | free(method); |
633 | free(pkalg); |
634 | sshkey_free(mkey); |
635 | sshkey_free(hostkey); |
636 | return r; |
637 | } |
638 | |
639 | /* |
640 | * Attempt to parse the contents of a buffer as a SSHSIG signature request. |
641 | * Note: does not modify buffer. |
642 | */ |
643 | static int |
644 | parse_sshsig_request(struct sshbuf *msg) |
645 | { |
646 | int r; |
647 | struct sshbuf *b; |
648 | |
649 | if ((b = sshbuf_fromb(msg)) == NULL((void*)0)) |
650 | fatal_f("sshbuf_fromb")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 650, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_fromb"); |
651 | |
652 | if ((r = sshbuf_cmp(b, 0, "SSHSIG", 6)) != 0 || |
653 | (r = sshbuf_consume(b, 6)) != 0 || |
654 | (r = sshbuf_get_cstring(b, NULL((void*)0), NULL((void*)0))) != 0 || /* namespace */ |
655 | (r = sshbuf_get_string_direct(b, NULL((void*)0), NULL((void*)0))) != 0 || /* reserved */ |
656 | (r = sshbuf_get_cstring(b, NULL((void*)0), NULL((void*)0))) != 0 || /* hashalg */ |
657 | (r = sshbuf_get_string_direct(b, NULL((void*)0), NULL((void*)0))) != 0) /* H(msg) */ |
658 | goto out; |
659 | if (sshbuf_len(b) != 0) { |
660 | r = SSH_ERR_INVALID_FORMAT-4; |
661 | goto out; |
662 | } |
663 | /* success */ |
664 | r = 0; |
665 | out: |
666 | sshbuf_free(b); |
667 | return r; |
668 | } |
669 | |
670 | /* |
671 | * This function inspects a message to be signed by a FIDO key that has a |
672 | * web-like application string (i.e. one that does not begin with "ssh:". |
673 | * It checks that the message is one of those expected for SSH operations |
674 | * (pubkey userauth, sshsig, CA key signing) to exclude signing challenges |
675 | * for the web. |
676 | */ |
677 | static int |
678 | check_websafe_message_contents(struct sshkey *key, struct sshbuf *data) |
679 | { |
680 | if (parse_userauth_request(data, key, NULL((void*)0), NULL((void*)0), NULL((void*)0)) == 0) { |
681 | debug_f("signed data matches public key userauth request")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 681, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "signed data matches public key userauth request" ); |
682 | return 1; |
683 | } |
684 | if (parse_sshsig_request(data) == 0) { |
685 | debug_f("signed data matches SSHSIG signature request")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 685, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "signed data matches SSHSIG signature request" ); |
686 | return 1; |
687 | } |
688 | |
689 | /* XXX check CA signature operation */ |
690 | |
691 | error("web-origin key attempting to sign non-SSH message")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 691, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "web-origin key attempting to sign non-SSH message" ); |
692 | return 0; |
693 | } |
694 | |
695 | static int |
696 | buf_equal(const struct sshbuf *a, const struct sshbuf *b) |
697 | { |
698 | if (sshbuf_ptr(a) == NULL((void*)0) || sshbuf_ptr(b) == NULL((void*)0)) |
699 | return SSH_ERR_INVALID_ARGUMENT-10; |
700 | if (sshbuf_len(a) != sshbuf_len(b)) |
701 | return SSH_ERR_INVALID_FORMAT-4; |
702 | if (timingsafe_bcmp(sshbuf_ptr(a), sshbuf_ptr(b), sshbuf_len(a)) != 0) |
703 | return SSH_ERR_INVALID_FORMAT-4; |
704 | return 0; |
705 | } |
706 | |
707 | /* ssh2 only */ |
708 | static void |
709 | process_sign_request2(SocketEntry *e) |
710 | { |
711 | u_char *signature = NULL((void*)0); |
712 | size_t slen = 0; |
713 | u_int compat = 0, flags; |
714 | int r, ok = -1; |
715 | char *fp = NULL((void*)0), *user = NULL((void*)0), *sig_dest = NULL((void*)0); |
716 | const char *fwd_host = NULL((void*)0), *dest_host = NULL((void*)0); |
717 | struct sshbuf *msg = NULL((void*)0), *data = NULL((void*)0), *sid = NULL((void*)0); |
718 | struct sshkey *key = NULL((void*)0), *hostkey = NULL((void*)0); |
719 | struct identity *id; |
720 | struct notifier_ctx *notifier = NULL((void*)0); |
721 | |
722 | debug_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 722, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "entering"); |
723 | |
724 | if ((msg = sshbuf_new()) == NULL((void*)0) || (data = sshbuf_new()) == NULL((void*)0)) |
725 | fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 725, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_new failed" ); |
726 | if ((r = sshkey_froms(e->request, &key)) != 0 || |
727 | (r = sshbuf_get_stringb(e->request, data)) != 0 || |
728 | (r = sshbuf_get_u32(e->request, &flags)) != 0) { |
729 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 729, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
730 | goto send; |
731 | } |
732 | |
733 | if ((id = lookup_identity(key)) == NULL((void*)0)) { |
734 | verbose_f("%s key not found", sshkey_type(key))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 734, 1, SYSLOG_LEVEL_VERBOSE, ((void*)0), "%s key not found" , sshkey_type(key)); |
735 | goto send; |
736 | } |
737 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT2, |
738 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
739 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 739, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
740 | |
741 | if (id->ndest_constraints != 0) { |
742 | if (e->nsession_ids == 0) { |
743 | logit_f("refusing use of destination-constrained key "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 744, 1, SYSLOG_LEVEL_INFO, ((void*)0), "refusing use of destination-constrained key " "to sign on unbound connection") |
744 | "to sign on unbound connection")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 744, 1, SYSLOG_LEVEL_INFO, ((void*)0), "refusing use of destination-constrained key " "to sign on unbound connection"); |
745 | goto send; |
746 | } |
747 | if (parse_userauth_request(data, key, &user, &sid, |
748 | &hostkey) != 0) { |
749 | logit_f("refusing use of destination-constrained key "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 750, 1, SYSLOG_LEVEL_INFO, ((void*)0), "refusing use of destination-constrained key " "to sign an unidentified signature") |
750 | "to sign an unidentified signature")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 750, 1, SYSLOG_LEVEL_INFO, ((void*)0), "refusing use of destination-constrained key " "to sign an unidentified signature"); |
751 | goto send; |
752 | } |
753 | /* XXX logspam */ |
754 | debug_f("user=%s", user)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 754, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "user=%s", user); |
755 | if (identity_permitted(id, e, user, &fwd_host, &dest_host) != 0) |
756 | goto send; |
757 | /* XXX display fwd_host/dest_host in askpass UI */ |
758 | /* |
759 | * Ensure that the session ID is the most recent one |
760 | * registered on the socket - it should have been bound by |
761 | * ssh immediately before userauth. |
762 | */ |
763 | if (buf_equal(sid, |
764 | e->session_ids[e->nsession_ids - 1].sid) != 0) { |
765 | error_f("unexpected session ID (%zu listed) on "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 768, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unexpected session ID (%zu listed) on " "signature request for target user %s with " "key %s %s", e-> nsession_ids, user, sshkey_type(id->key), fp) |
766 | "signature request for target user %s with "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 768, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unexpected session ID (%zu listed) on " "signature request for target user %s with " "key %s %s", e-> nsession_ids, user, sshkey_type(id->key), fp) |
767 | "key %s %s", e->nsession_ids, user,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 768, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unexpected session ID (%zu listed) on " "signature request for target user %s with " "key %s %s", e-> nsession_ids, user, sshkey_type(id->key), fp) |
768 | sshkey_type(id->key), fp)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 768, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unexpected session ID (%zu listed) on " "signature request for target user %s with " "key %s %s", e-> nsession_ids, user, sshkey_type(id->key), fp); |
769 | goto send; |
770 | } |
771 | /* |
772 | * Ensure that the hostkey embedded in the signature matches |
773 | * the one most recently bound to the socket. An exception is |
774 | * made for the initial forwarding hop. |
775 | */ |
776 | if (e->nsession_ids > 1 && hostkey == NULL((void*)0)) { |
777 | error_f("refusing use of destination-constrained key: "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 779, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "refusing use of destination-constrained key: " "no hostkey recorded in signature for forwarded " "connection" ) |
778 | "no hostkey recorded in signature for forwarded "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 779, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "refusing use of destination-constrained key: " "no hostkey recorded in signature for forwarded " "connection" ) |
779 | "connection")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 779, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "refusing use of destination-constrained key: " "no hostkey recorded in signature for forwarded " "connection" ); |
780 | goto send; |
781 | } |
782 | if (hostkey != NULL((void*)0) && !sshkey_equal(hostkey, |
783 | e->session_ids[e->nsession_ids - 1].key)) { |
784 | error_f("refusing use of destination-constrained key: "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 786, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "refusing use of destination-constrained key: " "mismatch between hostkey in request and most " "recently bound session" ) |
785 | "mismatch between hostkey in request and most "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 786, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "refusing use of destination-constrained key: " "mismatch between hostkey in request and most " "recently bound session" ) |
786 | "recently bound session")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 786, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "refusing use of destination-constrained key: " "mismatch between hostkey in request and most " "recently bound session" ); |
787 | goto send; |
788 | } |
789 | xasprintf(&sig_dest, "public key authentication request for " |
790 | "user \"%s\" to listed host", user); |
791 | } |
792 | if (id->confirm && confirm_key(id, sig_dest) != 0) { |
793 | verbose_f("user refused key")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 793, 1, SYSLOG_LEVEL_VERBOSE, ((void*)0), "user refused key" ); |
794 | goto send; |
795 | } |
796 | if (sshkey_is_sk(id->key)) { |
797 | if (strncmp(id->key->sk_application, "ssh:", 4) != 0 && |
798 | !check_websafe_message_contents(key, data)) { |
799 | /* error already logged */ |
800 | goto send; |
801 | } |
802 | if ((id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD0x01)) { |
803 | notifier = notify_start(0, |
804 | "Confirm user presence for key %s %s%s%s", |
805 | sshkey_type(id->key), fp, |
806 | sig_dest == NULL((void*)0) ? "" : "\n", |
807 | sig_dest == NULL((void*)0) ? "" : sig_dest); |
808 | } |
809 | } |
810 | /* XXX support PIN required FIDO keys */ |
811 | if ((r = sshkey_sign(id->key, &signature, &slen, |
812 | sshbuf_ptr(data), sshbuf_len(data), agent_decode_alg(key, flags), |
813 | id->sk_provider, NULL((void*)0), compat)) != 0) { |
814 | error_fr(r, "sshkey_sign")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 814, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "sshkey_sign"); |
815 | goto send; |
816 | } |
817 | /* Success */ |
818 | ok = 0; |
819 | send: |
820 | notify_complete(notifier, "User presence confirmed"); |
821 | |
822 | if (ok == 0) { |
823 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE14)) != 0 || |
824 | (r = sshbuf_put_string(msg, signature, slen)) != 0) |
825 | fatal_fr(r, "compose")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 825, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "compose"); |
826 | } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE5)) != 0) |
827 | fatal_fr(r, "compose failure")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 827, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "compose failure"); |
828 | |
829 | if ((r = sshbuf_put_stringb(e->output, msg)) != 0) |
830 | fatal_fr(r, "enqueue")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 830, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "enqueue"); |
831 | |
832 | sshbuf_free(sid); |
833 | sshbuf_free(data); |
834 | sshbuf_free(msg); |
835 | sshkey_free(key); |
836 | sshkey_free(hostkey); |
837 | free(fp); |
838 | free(signature); |
839 | free(sig_dest); |
840 | free(user); |
841 | } |
842 | |
843 | /* shared */ |
844 | static void |
845 | process_remove_identity(SocketEntry *e) |
846 | { |
847 | int r, success = 0; |
848 | struct sshkey *key = NULL((void*)0); |
849 | Identity *id; |
850 | |
851 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 851, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
852 | if ((r = sshkey_froms(e->request, &key)) != 0) { |
853 | error_fr(r, "parse key")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 853, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse key"); |
854 | goto done; |
855 | } |
856 | if ((id = lookup_identity(key)) == NULL((void*)0)) { |
857 | debug_f("key not found")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 857, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "key not found"); |
858 | goto done; |
859 | } |
860 | /* identity not visible, cannot be removed */ |
861 | if (identity_permitted(id, e, NULL((void*)0), NULL((void*)0), NULL((void*)0)) != 0) |
862 | goto done; /* error already logged */ |
863 | /* We have this key, free it. */ |
864 | if (idtab->nentries < 1) |
865 | fatal_f("internal error: nentries %d", idtab->nentries)sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 865, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "internal error: nentries %d" , idtab->nentries); |
866 | TAILQ_REMOVE(&idtab->idlist, id, next)do { if (((id)->next.tqe_next) != ((void*)0)) (id)->next .tqe_next->next.tqe_prev = (id)->next.tqe_prev; else (& idtab->idlist)->tqh_last = (id)->next.tqe_prev; *(id )->next.tqe_prev = (id)->next.tqe_next; ; ; } while (0); |
867 | free_identity(id); |
868 | idtab->nentries--; |
869 | success = 1; |
870 | done: |
871 | sshkey_free(key); |
872 | send_status(e, success); |
873 | } |
874 | |
875 | static void |
876 | process_remove_all_identities(SocketEntry *e) |
877 | { |
878 | Identity *id; |
879 | |
880 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 880, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
881 | /* Loop over all identities and clear the keys. */ |
882 | for (id = TAILQ_FIRST(&idtab->idlist)((&idtab->idlist)->tqh_first); id; |
883 | id = TAILQ_FIRST(&idtab->idlist)((&idtab->idlist)->tqh_first)) { |
884 | TAILQ_REMOVE(&idtab->idlist, id, next)do { if (((id)->next.tqe_next) != ((void*)0)) (id)->next .tqe_next->next.tqe_prev = (id)->next.tqe_prev; else (& idtab->idlist)->tqh_last = (id)->next.tqe_prev; *(id )->next.tqe_prev = (id)->next.tqe_next; ; ; } while (0); |
885 | free_identity(id); |
886 | } |
887 | |
888 | /* Mark that there are no identities. */ |
889 | idtab->nentries = 0; |
890 | |
891 | /* Send success. */ |
892 | send_status(e, 1); |
893 | } |
894 | |
895 | /* removes expired keys and returns number of seconds until the next expiry */ |
896 | static time_t |
897 | reaper(void) |
898 | { |
899 | time_t deadline = 0, now = monotime(); |
900 | Identity *id, *nxt; |
901 | |
902 | for (id = TAILQ_FIRST(&idtab->idlist)((&idtab->idlist)->tqh_first); id; id = nxt) { |
903 | nxt = TAILQ_NEXT(id, next)((id)->next.tqe_next); |
904 | if (id->death == 0) |
905 | continue; |
906 | if (now >= id->death) { |
907 | debug("expiring key '%s'", id->comment)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 907, 0, SYSLOG_LEVEL_DEBUG1, ((void*)0), "expiring key '%s'" , id->comment); |
908 | TAILQ_REMOVE(&idtab->idlist, id, next)do { if (((id)->next.tqe_next) != ((void*)0)) (id)->next .tqe_next->next.tqe_prev = (id)->next.tqe_prev; else (& idtab->idlist)->tqh_last = (id)->next.tqe_prev; *(id )->next.tqe_prev = (id)->next.tqe_next; ; ; } while (0); |
909 | free_identity(id); |
910 | idtab->nentries--; |
911 | } else |
912 | deadline = (deadline == 0) ? id->death : |
913 | MINIMUM(deadline, id->death)(((deadline) < (id->death)) ? (deadline) : (id->death )); |
914 | } |
915 | if (deadline == 0 || deadline <= now) |
916 | return 0; |
917 | else |
918 | return (deadline - now); |
919 | } |
920 | |
921 | static int |
922 | parse_dest_constraint_hop(struct sshbuf *b, struct dest_constraint_hop *dch) |
923 | { |
924 | u_char key_is_ca; |
925 | size_t elen = 0; |
926 | int r; |
927 | struct sshkey *k = NULL((void*)0); |
928 | char *fp; |
929 | |
930 | memset(dch, '\0', sizeof(*dch)); |
931 | if ((r = sshbuf_get_cstring(b, &dch->user, NULL((void*)0))) != 0 || |
932 | (r = sshbuf_get_cstring(b, &dch->hostname, NULL((void*)0))) != 0 || |
933 | (r = sshbuf_get_string_direct(b, NULL((void*)0), &elen)) != 0) { |
934 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 934, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
935 | goto out; |
936 | } |
937 | if (elen != 0) { |
938 | error_f("unsupported extensions (len %zu)", elen)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 938, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unsupported extensions (len %zu)" , elen); |
939 | r = SSH_ERR_FEATURE_UNSUPPORTED-59; |
940 | goto out; |
941 | } |
942 | if (*dch->hostname == '\0') { |
943 | free(dch->hostname); |
944 | dch->hostname = NULL((void*)0); |
945 | } |
946 | if (*dch->user == '\0') { |
947 | free(dch->user); |
948 | dch->user = NULL((void*)0); |
949 | } |
950 | while (sshbuf_len(b) != 0) { |
951 | dch->keys = xrecallocarray(dch->keys, dch->nkeys, |
952 | dch->nkeys + 1, sizeof(*dch->keys)); |
953 | dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys, |
954 | dch->nkeys + 1, sizeof(*dch->key_is_ca)); |
955 | if ((r = sshkey_froms(b, &k)) != 0 || |
956 | (r = sshbuf_get_u8(b, &key_is_ca)) != 0) |
957 | goto out; |
958 | if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT2, |
959 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
960 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 960, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
961 | debug3_f("%s%s%s: adding %skey %s %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 964, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s%s%s: adding %skey %s %s" , dch->user == ((void*)0) ? "" : dch->user, dch->user == ((void*)0) ? "" : "@", dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp) |
962 | dch->user == NULL ? "" : dch->user,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 964, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s%s%s: adding %skey %s %s" , dch->user == ((void*)0) ? "" : dch->user, dch->user == ((void*)0) ? "" : "@", dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp) |
963 | dch->user == NULL ? "" : "@",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 964, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s%s%s: adding %skey %s %s" , dch->user == ((void*)0) ? "" : dch->user, dch->user == ((void*)0) ? "" : "@", dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp) |
964 | dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 964, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "%s%s%s: adding %skey %s %s" , dch->user == ((void*)0) ? "" : dch->user, dch->user == ((void*)0) ? "" : "@", dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp); |
965 | free(fp); |
966 | dch->keys[dch->nkeys] = k; |
967 | dch->key_is_ca[dch->nkeys] = key_is_ca != 0; |
968 | dch->nkeys++; |
969 | k = NULL((void*)0); /* transferred */ |
970 | } |
971 | /* success */ |
972 | r = 0; |
973 | out: |
974 | sshkey_free(k); |
975 | return r; |
976 | } |
977 | |
978 | static int |
979 | parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc) |
980 | { |
981 | struct sshbuf *b = NULL((void*)0), *frombuf = NULL((void*)0), *tobuf = NULL((void*)0); |
982 | int r; |
983 | size_t elen = 0; |
984 | |
985 | debug3_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 985, 1, SYSLOG_LEVEL_DEBUG3, ((void*)0), "entering"); |
986 | |
987 | memset(dc, '\0', sizeof(*dc)); |
988 | if ((r = sshbuf_froms(m, &b)) != 0 || |
989 | (r = sshbuf_froms(b, &frombuf)) != 0 || |
990 | (r = sshbuf_froms(b, &tobuf)) != 0 || |
991 | (r = sshbuf_get_string_direct(b, NULL((void*)0), &elen)) != 0) { |
992 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 992, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
993 | goto out; |
994 | } |
995 | if ((r = parse_dest_constraint_hop(frombuf, &dc->from) != 0) || |
996 | (r = parse_dest_constraint_hop(tobuf, &dc->to) != 0)) |
997 | goto out; /* already logged */ |
998 | if (elen != 0) { |
999 | error_f("unsupported extensions (len %zu)", elen)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 999, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unsupported extensions (len %zu)" , elen); |
1000 | r = SSH_ERR_FEATURE_UNSUPPORTED-59; |
1001 | goto out; |
1002 | } |
1003 | debug2_f("parsed %s (%u keys) > %s%s%s (%u keys)",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1006, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "parsed %s (%u keys) > %s%s%s (%u keys)" , dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc ->from.nkeys, dc->to.user ? dc->to.user : "", dc-> to.user ? "@" : "", dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys) |
1004 | dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1006, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "parsed %s (%u keys) > %s%s%s (%u keys)" , dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc ->from.nkeys, dc->to.user ? dc->to.user : "", dc-> to.user ? "@" : "", dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys) |
1005 | dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1006, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "parsed %s (%u keys) > %s%s%s (%u keys)" , dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc ->from.nkeys, dc->to.user ? dc->to.user : "", dc-> to.user ? "@" : "", dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys) |
1006 | dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1006, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "parsed %s (%u keys) > %s%s%s (%u keys)" , dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc ->from.nkeys, dc->to.user ? dc->to.user : "", dc-> to.user ? "@" : "", dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys); |
1007 | /* check consistency */ |
1008 | if ((dc->from.hostname == NULL((void*)0)) != (dc->from.nkeys == 0) || |
1009 | dc->from.user != NULL((void*)0)) { |
1010 | error_f("inconsistent \"from\" specification")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1010, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "inconsistent \"from\" specification" ); |
1011 | r = SSH_ERR_INVALID_FORMAT-4; |
1012 | goto out; |
1013 | } |
1014 | if (dc->to.hostname == NULL((void*)0) || dc->to.nkeys == 0) { |
1015 | error_f("incomplete \"to\" specification")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1015, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "incomplete \"to\" specification" ); |
1016 | r = SSH_ERR_INVALID_FORMAT-4; |
1017 | goto out; |
1018 | } |
1019 | /* success */ |
1020 | r = 0; |
1021 | out: |
1022 | sshbuf_free(b); |
1023 | sshbuf_free(frombuf); |
1024 | sshbuf_free(tobuf); |
1025 | return r; |
1026 | } |
1027 | |
1028 | static int |
1029 | parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp, |
1030 | struct dest_constraint **dcsp, size_t *ndcsp) |
1031 | { |
1032 | char *ext_name = NULL((void*)0); |
1033 | int r; |
1034 | struct sshbuf *b = NULL((void*)0); |
1035 | |
1036 | if ((r = sshbuf_get_cstring(m, &ext_name, NULL((void*)0))) != 0) { |
1037 | error_fr(r, "parse constraint extension")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1037, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse constraint extension" ); |
1038 | goto out; |
1039 | } |
1040 | debug_f("constraint ext %s", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1040, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "constraint ext %s" , ext_name); |
1041 | if (strcmp(ext_name, "sk-provider@openssh.com") == 0) { |
1042 | if (sk_providerp == NULL((void*)0)) { |
1043 | error_f("%s not valid here", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1043, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "%s not valid here" , ext_name); |
1044 | r = SSH_ERR_INVALID_FORMAT-4; |
1045 | goto out; |
1046 | } |
1047 | if (*sk_providerp != NULL((void*)0)) { |
1048 | error_f("%s already set", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1048, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "%s already set", ext_name ); |
1049 | r = SSH_ERR_INVALID_FORMAT-4; |
1050 | goto out; |
1051 | } |
1052 | if ((r = sshbuf_get_cstring(m, sk_providerp, NULL((void*)0))) != 0) { |
1053 | error_fr(r, "parse %s", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1053, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse %s", ext_name ); |
1054 | goto out; |
1055 | } |
1056 | } else if (strcmp(ext_name, |
1057 | "restrict-destination-v00@openssh.com") == 0) { |
1058 | if (*dcsp != NULL((void*)0)) { |
1059 | error_f("%s already set", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1059, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "%s already set", ext_name ); |
1060 | goto out; |
1061 | } |
1062 | if ((r = sshbuf_froms(m, &b)) != 0) { |
1063 | error_fr(r, "parse %s outer", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1063, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse %s outer", ext_name ); |
1064 | goto out; |
1065 | } |
1066 | while (sshbuf_len(b) != 0) { |
1067 | if (*ndcsp >= AGENT_MAX_DEST_CONSTRAINTS1024) { |
1068 | error_f("too many %s constraints", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1068, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "too many %s constraints" , ext_name); |
1069 | goto out; |
1070 | } |
1071 | *dcsp = xrecallocarray(*dcsp, *ndcsp, *ndcsp + 1, |
1072 | sizeof(**dcsp)); |
1073 | if ((r = parse_dest_constraint(b, |
1074 | *dcsp + (*ndcsp)++)) != 0) |
1075 | goto out; /* error already logged */ |
1076 | } |
1077 | } else { |
1078 | error_f("unsupported constraint \"%s\"", ext_name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1078, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "unsupported constraint \"%s\"" , ext_name); |
1079 | r = SSH_ERR_FEATURE_UNSUPPORTED-59; |
1080 | goto out; |
1081 | } |
1082 | /* success */ |
1083 | r = 0; |
1084 | out: |
1085 | free(ext_name); |
1086 | sshbuf_free(b); |
1087 | return r; |
1088 | } |
1089 | |
1090 | static int |
1091 | parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp, |
1092 | u_int *secondsp, int *confirmp, char **sk_providerp, |
1093 | struct dest_constraint **dcsp, size_t *ndcsp) |
1094 | { |
1095 | u_char ctype; |
1096 | int r; |
1097 | u_int seconds, maxsign = 0; |
1098 | |
1099 | while (sshbuf_len(m)) { |
1100 | if ((r = sshbuf_get_u8(m, &ctype)) != 0) { |
1101 | error_fr(r, "parse constraint type")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1101, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse constraint type" ); |
1102 | goto out; |
1103 | } |
1104 | switch (ctype) { |
1105 | case SSH_AGENT_CONSTRAIN_LIFETIME1: |
1106 | if (*deathp != 0) { |
1107 | error_f("lifetime already set")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1107, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "lifetime already set" ); |
1108 | r = SSH_ERR_INVALID_FORMAT-4; |
1109 | goto out; |
1110 | } |
1111 | if ((r = sshbuf_get_u32(m, &seconds)) != 0) { |
1112 | error_fr(r, "parse lifetime constraint")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1112, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse lifetime constraint" ); |
1113 | goto out; |
1114 | } |
1115 | *deathp = monotime() + seconds; |
1116 | *secondsp = seconds; |
1117 | break; |
1118 | case SSH_AGENT_CONSTRAIN_CONFIRM2: |
1119 | if (*confirmp != 0) { |
1120 | error_f("confirm already set")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1120, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "confirm already set" ); |
1121 | r = SSH_ERR_INVALID_FORMAT-4; |
1122 | goto out; |
1123 | } |
1124 | *confirmp = 1; |
1125 | break; |
1126 | case SSH_AGENT_CONSTRAIN_MAXSIGN3: |
1127 | if (k == NULL((void*)0)) { |
1128 | error_f("maxsign not valid here")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1128, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "maxsign not valid here" ); |
1129 | r = SSH_ERR_INVALID_FORMAT-4; |
1130 | goto out; |
1131 | } |
1132 | if (maxsign != 0) { |
1133 | error_f("maxsign already set")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1133, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "maxsign already set" ); |
1134 | r = SSH_ERR_INVALID_FORMAT-4; |
1135 | goto out; |
1136 | } |
1137 | if ((r = sshbuf_get_u32(m, &maxsign)) != 0) { |
1138 | error_fr(r, "parse maxsign constraint")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1138, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse maxsign constraint" ); |
1139 | goto out; |
1140 | } |
1141 | if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) { |
1142 | error_fr(r, "enable maxsign")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1142, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "enable maxsign"); |
1143 | goto out; |
1144 | } |
1145 | break; |
1146 | case SSH_AGENT_CONSTRAIN_EXTENSION255: |
1147 | if ((r = parse_key_constraint_extension(m, |
1148 | sk_providerp, dcsp, ndcsp)) != 0) |
1149 | goto out; /* error already logged */ |
1150 | break; |
1151 | default: |
1152 | error_f("Unknown constraint %d", ctype)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1152, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "Unknown constraint %d" , ctype); |
1153 | r = SSH_ERR_FEATURE_UNSUPPORTED-59; |
1154 | goto out; |
1155 | } |
1156 | } |
1157 | /* success */ |
1158 | r = 0; |
1159 | out: |
1160 | return r; |
1161 | } |
1162 | |
1163 | static void |
1164 | process_add_identity(SocketEntry *e) |
1165 | { |
1166 | Identity *id; |
1167 | int success = 0, confirm = 0; |
1168 | char *fp, *comment = NULL((void*)0), *sk_provider = NULL((void*)0); |
1169 | char canonical_provider[PATH_MAX1024]; |
1170 | time_t death = 0; |
1171 | u_int seconds = 0; |
1172 | struct dest_constraint *dest_constraints = NULL((void*)0); |
1173 | size_t ndest_constraints = 0; |
1174 | struct sshkey *k = NULL((void*)0); |
1175 | int r = SSH_ERR_INTERNAL_ERROR-1; |
1176 | |
1177 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1177, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
1178 | if ((r = sshkey_private_deserialize(e->request, &k)) != 0 || |
1179 | k == NULL((void*)0) || |
1180 | (r = sshbuf_get_cstring(e->request, &comment, NULL((void*)0))) != 0) { |
1181 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1181, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
1182 | goto out; |
1183 | } |
1184 | if (parse_key_constraints(e->request, k, &death, &seconds, &confirm, |
1185 | &sk_provider, &dest_constraints, &ndest_constraints) != 0) { |
1186 | error_f("failed to parse constraints")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1186, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "failed to parse constraints" ); |
1187 | sshbuf_reset(e->request); |
1188 | goto out; |
1189 | } |
1190 | |
1191 | if (sk_provider != NULL((void*)0)) { |
1192 | if (!sshkey_is_sk(k)) { |
1193 | error("Cannot add provider: %s is not an "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1194, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "Cannot add provider: %s is not an " "authenticator-hosted key", sshkey_type(k)) |
1194 | "authenticator-hosted key", sshkey_type(k))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1194, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "Cannot add provider: %s is not an " "authenticator-hosted key", sshkey_type(k)); |
1195 | goto out; |
1196 | } |
1197 | if (strcasecmp(sk_provider, "internal") == 0) { |
1198 | debug_f("internal provider")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1198, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "internal provider" ); |
1199 | } else { |
1200 | if (realpath(sk_provider, canonical_provider) == NULL((void*)0)) { |
1201 | verbose("failed provider \"%.100s\": "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1203, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed provider \"%.100s\": " "realpath: %s", sk_provider, strerror((*__errno()))) |
1202 | "realpath: %s", sk_provider,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1203, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed provider \"%.100s\": " "realpath: %s", sk_provider, strerror((*__errno()))) |
1203 | strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1203, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed provider \"%.100s\": " "realpath: %s", sk_provider, strerror((*__errno()))); |
1204 | goto out; |
1205 | } |
1206 | free(sk_provider); |
1207 | sk_provider = xstrdup(canonical_provider); |
1208 | if (match_pattern_list(sk_provider, |
1209 | allowed_providers, 0) != 1) { |
1210 | error("Refusing add key: "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1211, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "Refusing add key: " "provider %s not allowed", sk_provider) |
1211 | "provider %s not allowed", sk_provider)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1211, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "Refusing add key: " "provider %s not allowed", sk_provider); |
1212 | goto out; |
1213 | } |
1214 | } |
1215 | } |
1216 | if ((r = sshkey_shield_private(k)) != 0) { |
1217 | error_fr(r, "shield private")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1217, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "shield private"); |
1218 | goto out; |
1219 | } |
1220 | if (lifetime && !death) |
1221 | death = monotime() + lifetime; |
1222 | if ((id = lookup_identity(k)) == NULL((void*)0)) { |
1223 | id = xcalloc(1, sizeof(Identity)); |
1224 | TAILQ_INSERT_TAIL(&idtab->idlist, id, next)do { (id)->next.tqe_next = ((void*)0); (id)->next.tqe_prev = (&idtab->idlist)->tqh_last; *(&idtab->idlist )->tqh_last = (id); (&idtab->idlist)->tqh_last = &(id)->next.tqe_next; } while (0); |
1225 | /* Increment the number of identities. */ |
1226 | idtab->nentries++; |
1227 | } else { |
1228 | /* identity not visible, do not update */ |
1229 | if (identity_permitted(id, e, NULL((void*)0), NULL((void*)0), NULL((void*)0)) != 0) |
1230 | goto out; /* error already logged */ |
1231 | /* key state might have been updated */ |
1232 | sshkey_free(id->key); |
1233 | free(id->comment); |
1234 | free(id->sk_provider); |
1235 | free_dest_constraints(id->dest_constraints, |
1236 | id->ndest_constraints); |
1237 | } |
1238 | /* success */ |
1239 | id->key = k; |
1240 | id->comment = comment; |
1241 | id->death = death; |
1242 | id->confirm = confirm; |
1243 | id->sk_provider = sk_provider; |
1244 | id->dest_constraints = dest_constraints; |
1245 | id->ndest_constraints = ndest_constraints; |
1246 | |
1247 | if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT2, |
1248 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
1249 | fatal_f("sshkey_fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1249, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshkey_fingerprint failed" ); |
1250 | debug_f("add %s %s \"%.100s\" (life: %u) (confirm: %u) "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1253, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "add %s %s \"%.100s\" (life: %u) (confirm: %u) " "(provider: %s) (destination constraints: %zu)", sshkey_ssh_name (k), fp, comment, seconds, confirm, sk_provider == ((void*)0) ? "none" : sk_provider, ndest_constraints) |
1251 | "(provider: %s) (destination constraints: %zu)",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1253, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "add %s %s \"%.100s\" (life: %u) (confirm: %u) " "(provider: %s) (destination constraints: %zu)", sshkey_ssh_name (k), fp, comment, seconds, confirm, sk_provider == ((void*)0) ? "none" : sk_provider, ndest_constraints) |
1252 | sshkey_ssh_name(k), fp, comment, seconds, confirm,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1253, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "add %s %s \"%.100s\" (life: %u) (confirm: %u) " "(provider: %s) (destination constraints: %zu)", sshkey_ssh_name (k), fp, comment, seconds, confirm, sk_provider == ((void*)0) ? "none" : sk_provider, ndest_constraints) |
1253 | sk_provider == NULL ? "none" : sk_provider, ndest_constraints)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1253, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "add %s %s \"%.100s\" (life: %u) (confirm: %u) " "(provider: %s) (destination constraints: %zu)", sshkey_ssh_name (k), fp, comment, seconds, confirm, sk_provider == ((void*)0) ? "none" : sk_provider, ndest_constraints); |
1254 | free(fp); |
1255 | /* transferred */ |
1256 | k = NULL((void*)0); |
1257 | comment = NULL((void*)0); |
1258 | sk_provider = NULL((void*)0); |
1259 | dest_constraints = NULL((void*)0); |
1260 | ndest_constraints = 0; |
1261 | success = 1; |
1262 | out: |
1263 | free(sk_provider); |
1264 | free(comment); |
1265 | sshkey_free(k); |
1266 | free_dest_constraints(dest_constraints, ndest_constraints); |
1267 | send_status(e, success); |
1268 | } |
1269 | |
1270 | /* XXX todo: encrypt sensitive data with passphrase */ |
1271 | static void |
1272 | process_lock_agent(SocketEntry *e, int lock) |
1273 | { |
1274 | int r, success = 0, delay; |
1275 | char *passwd; |
1276 | u_char passwdhash[LOCK_SIZE32]; |
1277 | static u_int fail_count = 0; |
1278 | size_t pwlen; |
1279 | |
1280 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1280, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
1281 | /* |
1282 | * This is deliberately fatal: the user has requested that we lock, |
1283 | * but we can't parse their request properly. The only safe thing to |
1284 | * do is abort. |
1285 | */ |
1286 | if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0) |
1287 | fatal_fr(r, "parse")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1287, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "parse"); |
1288 | if (pwlen == 0) { |
1289 | debug("empty password not supported")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1289, 0, SYSLOG_LEVEL_DEBUG1, ((void*)0), "empty password not supported" ); |
1290 | } else if (locked && !lock) { |
1291 | if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), |
1292 | passwdhash, sizeof(passwdhash), LOCK_ROUNDS1) < 0) |
1293 | fatal("bcrypt_pbkdf")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1293, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "bcrypt_pbkdf"); |
1294 | if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE32) == 0) { |
1295 | debug("agent unlocked")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1295, 0, SYSLOG_LEVEL_DEBUG1, ((void*)0), "agent unlocked"); |
1296 | locked = 0; |
1297 | fail_count = 0; |
1298 | explicit_bzero(lock_pwhash, sizeof(lock_pwhash)); |
1299 | success = 1; |
1300 | } else { |
1301 | /* delay in 0.1s increments up to 10s */ |
1302 | if (fail_count < 100) |
1303 | fail_count++; |
1304 | delay = 100000 * fail_count; |
1305 | debug("unlock failed, delaying %0.1lf seconds",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1306, 0, SYSLOG_LEVEL_DEBUG1, ((void*)0), "unlock failed, delaying %0.1lf seconds" , (double)delay/1000000) |
1306 | (double)delay/1000000)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1306, 0, SYSLOG_LEVEL_DEBUG1, ((void*)0), "unlock failed, delaying %0.1lf seconds" , (double)delay/1000000); |
1307 | usleep(delay); |
1308 | } |
1309 | explicit_bzero(passwdhash, sizeof(passwdhash)); |
1310 | } else if (!locked && lock) { |
1311 | debug("agent locked")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1311, 0, SYSLOG_LEVEL_DEBUG1, ((void*)0), "agent locked"); |
1312 | locked = 1; |
1313 | arc4random_buf(lock_salt, sizeof(lock_salt)); |
1314 | if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), |
1315 | lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS1) < 0) |
1316 | fatal("bcrypt_pbkdf")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1316, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "bcrypt_pbkdf"); |
1317 | success = 1; |
1318 | } |
1319 | freezero(passwd, pwlen); |
1320 | send_status(e, success); |
1321 | } |
1322 | |
1323 | static void |
1324 | no_identities(SocketEntry *e) |
1325 | { |
1326 | struct sshbuf *msg; |
1327 | int r; |
1328 | |
1329 | if ((msg = sshbuf_new()) == NULL((void*)0)) |
1330 | fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1330, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_new failed" ); |
1331 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER12)) != 0 || |
1332 | (r = sshbuf_put_u32(msg, 0)) != 0 || |
1333 | (r = sshbuf_put_stringb(e->output, msg)) != 0) |
1334 | fatal_fr(r, "compose")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1334, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "compose"); |
1335 | sshbuf_free(msg); |
1336 | } |
1337 | |
1338 | #ifdef ENABLE_PKCS111 |
1339 | static void |
1340 | process_add_smartcard_key(SocketEntry *e) |
1341 | { |
1342 | char *provider = NULL((void*)0), *pin = NULL((void*)0), canonical_provider[PATH_MAX1024]; |
1343 | char **comments = NULL((void*)0); |
1344 | int r, i, count = 0, success = 0, confirm = 0; |
1345 | u_int seconds = 0; |
1346 | time_t death = 0; |
1347 | struct sshkey **keys = NULL((void*)0), *k; |
1348 | Identity *id; |
1349 | struct dest_constraint *dest_constraints = NULL((void*)0); |
1350 | size_t ndest_constraints = 0; |
1351 | |
1352 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1352, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
1353 | if ((r = sshbuf_get_cstring(e->request, &provider, NULL((void*)0))) != 0 || |
1354 | (r = sshbuf_get_cstring(e->request, &pin, NULL((void*)0))) != 0) { |
1355 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1355, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
1356 | goto send; |
1357 | } |
1358 | if (parse_key_constraints(e->request, NULL((void*)0), &death, &seconds, &confirm, |
1359 | NULL((void*)0), &dest_constraints, &ndest_constraints) != 0) { |
1360 | error_f("failed to parse constraints")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1360, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "failed to parse constraints" ); |
1361 | goto send; |
1362 | } |
1363 | if (realpath(provider, canonical_provider) == NULL((void*)0)) { |
1364 | verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1365, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed PKCS#11 add of \"%.100s\": realpath: %s" , provider, strerror((*__errno()))) |
1365 | provider, strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1365, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed PKCS#11 add of \"%.100s\": realpath: %s" , provider, strerror((*__errno()))); |
1366 | goto send; |
1367 | } |
1368 | if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) { |
1369 | verbose("refusing PKCS#11 add of \"%.100s\": "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1370, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "refusing PKCS#11 add of \"%.100s\": " "provider not allowed", canonical_provider) |
1370 | "provider not allowed", canonical_provider)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1370, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "refusing PKCS#11 add of \"%.100s\": " "provider not allowed", canonical_provider); |
1371 | goto send; |
1372 | } |
1373 | debug_f("add %.100s", canonical_provider)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1373, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "add %.100s", canonical_provider ); |
1374 | if (lifetime && !death) |
1375 | death = monotime() + lifetime; |
1376 | |
1377 | count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments); |
1378 | for (i = 0; i < count; i++) { |
1379 | k = keys[i]; |
1380 | if (lookup_identity(k) == NULL((void*)0)) { |
1381 | id = xcalloc(1, sizeof(Identity)); |
1382 | id->key = k; |
1383 | keys[i] = NULL((void*)0); /* transferred */ |
1384 | id->provider = xstrdup(canonical_provider); |
1385 | if (*comments[i] != '\0') { |
1386 | id->comment = comments[i]; |
1387 | comments[i] = NULL((void*)0); /* transferred */ |
1388 | } else { |
1389 | id->comment = xstrdup(canonical_provider); |
1390 | } |
1391 | id->death = death; |
1392 | id->confirm = confirm; |
1393 | id->dest_constraints = dest_constraints; |
1394 | id->ndest_constraints = ndest_constraints; |
1395 | dest_constraints = NULL((void*)0); /* transferred */ |
1396 | ndest_constraints = 0; |
1397 | TAILQ_INSERT_TAIL(&idtab->idlist, id, next)do { (id)->next.tqe_next = ((void*)0); (id)->next.tqe_prev = (&idtab->idlist)->tqh_last; *(&idtab->idlist )->tqh_last = (id); (&idtab->idlist)->tqh_last = &(id)->next.tqe_next; } while (0); |
1398 | idtab->nentries++; |
1399 | success = 1; |
1400 | } |
1401 | /* XXX update constraints for existing keys */ |
1402 | sshkey_free(keys[i]); |
1403 | free(comments[i]); |
1404 | } |
1405 | send: |
1406 | free(pin); |
1407 | free(provider); |
1408 | free(keys); |
1409 | free(comments); |
1410 | free_dest_constraints(dest_constraints, ndest_constraints); |
1411 | send_status(e, success); |
1412 | } |
1413 | |
1414 | static void |
1415 | process_remove_smartcard_key(SocketEntry *e) |
1416 | { |
1417 | char *provider = NULL((void*)0), *pin = NULL((void*)0), canonical_provider[PATH_MAX1024]; |
1418 | int r, success = 0; |
1419 | Identity *id, *nxt; |
1420 | |
1421 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1421, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
1422 | if ((r = sshbuf_get_cstring(e->request, &provider, NULL((void*)0))) != 0 || |
1423 | (r = sshbuf_get_cstring(e->request, &pin, NULL((void*)0))) != 0) { |
1424 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1424, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
1425 | goto send; |
1426 | } |
1427 | free(pin); |
1428 | |
1429 | if (realpath(provider, canonical_provider) == NULL((void*)0)) { |
1430 | verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1431, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed PKCS#11 add of \"%.100s\": realpath: %s" , provider, strerror((*__errno()))) |
1431 | provider, strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1431, 0, SYSLOG_LEVEL_VERBOSE, ((void*)0), "failed PKCS#11 add of \"%.100s\": realpath: %s" , provider, strerror((*__errno()))); |
1432 | goto send; |
1433 | } |
1434 | |
1435 | debug_f("remove %.100s", canonical_provider)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1435, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "remove %.100s", canonical_provider ); |
1436 | for (id = TAILQ_FIRST(&idtab->idlist)((&idtab->idlist)->tqh_first); id; id = nxt) { |
1437 | nxt = TAILQ_NEXT(id, next)((id)->next.tqe_next); |
1438 | /* Skip file--based keys */ |
1439 | if (id->provider == NULL((void*)0)) |
1440 | continue; |
1441 | if (!strcmp(canonical_provider, id->provider)) { |
1442 | TAILQ_REMOVE(&idtab->idlist, id, next)do { if (((id)->next.tqe_next) != ((void*)0)) (id)->next .tqe_next->next.tqe_prev = (id)->next.tqe_prev; else (& idtab->idlist)->tqh_last = (id)->next.tqe_prev; *(id )->next.tqe_prev = (id)->next.tqe_next; ; ; } while (0); |
1443 | free_identity(id); |
1444 | idtab->nentries--; |
1445 | } |
1446 | } |
1447 | if (pkcs11_del_provider(canonical_provider) == 0) |
1448 | success = 1; |
1449 | else |
1450 | error_f("pkcs11_del_provider failed")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1450, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "pkcs11_del_provider failed" ); |
1451 | send: |
1452 | free(provider); |
1453 | send_status(e, success); |
1454 | } |
1455 | #endif /* ENABLE_PKCS11 */ |
1456 | |
1457 | static int |
1458 | process_ext_session_bind(SocketEntry *e) |
1459 | { |
1460 | int r, sid_match, key_match; |
1461 | struct sshkey *key = NULL((void*)0); |
1462 | struct sshbuf *sid = NULL((void*)0), *sig = NULL((void*)0); |
1463 | char *fp = NULL((void*)0); |
1464 | size_t i; |
1465 | u_char fwd = 0; |
1466 | |
1467 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1467, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
1468 | if ((r = sshkey_froms(e->request, &key)) != 0 || |
1469 | (r = sshbuf_froms(e->request, &sid)) != 0 || |
1470 | (r = sshbuf_froms(e->request, &sig)) != 0 || |
1471 | (r = sshbuf_get_u8(e->request, &fwd)) != 0) { |
1472 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1472, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
1473 | goto out; |
1474 | } |
1475 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT2, |
1476 | SSH_FP_DEFAULT)) == NULL((void*)0)) |
1477 | fatal_f("fingerprint failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1477, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "fingerprint failed" ); |
1478 | /* check signature with hostkey on session ID */ |
1479 | if ((r = sshkey_verify(key, sshbuf_ptr(sig), sshbuf_len(sig), |
1480 | sshbuf_ptr(sid), sshbuf_len(sid), NULL((void*)0), 0, NULL((void*)0))) != 0) { |
1481 | error_fr(r, "sshkey_verify for %s %s", sshkey_type(key), fp)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1481, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "sshkey_verify for %s %s" , sshkey_type(key), fp); |
1482 | goto out; |
1483 | } |
1484 | /* check whether sid/key already recorded */ |
1485 | for (i = 0; i < e->nsession_ids; i++) { |
1486 | if (!e->session_ids[i].forwarded) { |
1487 | error_f("attempt to bind session ID to socket "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1488, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "attempt to bind session ID to socket " "previously bound for authentication attempt") |
1488 | "previously bound for authentication attempt")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1488, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "attempt to bind session ID to socket " "previously bound for authentication attempt"); |
1489 | r = -1; |
1490 | goto out; |
1491 | } |
1492 | sid_match = buf_equal(sid, e->session_ids[i].sid) == 0; |
1493 | key_match = sshkey_equal(key, e->session_ids[i].key); |
1494 | if (sid_match && key_match) { |
1495 | debug_f("session ID already recorded for %s %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1496, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "session ID already recorded for %s %s" , sshkey_type(key), fp) |
1496 | sshkey_type(key), fp)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1496, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "session ID already recorded for %s %s" , sshkey_type(key), fp); |
1497 | r = 0; |
1498 | goto out; |
1499 | } else if (sid_match) { |
1500 | error_f("session ID recorded against different key "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1501, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "session ID recorded against different key " "for %s %s", sshkey_type(key), fp) |
1501 | "for %s %s", sshkey_type(key), fp)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1501, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "session ID recorded against different key " "for %s %s", sshkey_type(key), fp); |
1502 | r = -1; |
1503 | goto out; |
1504 | } |
1505 | /* |
1506 | * new sid with previously-seen key can happen, e.g. multiple |
1507 | * connections to the same host. |
1508 | */ |
1509 | } |
1510 | /* record new key/sid */ |
1511 | if (e->nsession_ids >= AGENT_MAX_SESSION_IDS16) { |
1512 | error_f("too many session IDs recorded")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1512, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "too many session IDs recorded" ); |
1513 | goto out; |
1514 | } |
1515 | e->session_ids = xrecallocarray(e->session_ids, e->nsession_ids, |
1516 | e->nsession_ids + 1, sizeof(*e->session_ids)); |
1517 | i = e->nsession_ids++; |
1518 | debug_f("recorded %s %s (slot %zu of %d)", sshkey_type(key), fp, i,sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1519, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "recorded %s %s (slot %zu of %d)" , sshkey_type(key), fp, i, 16) |
1519 | AGENT_MAX_SESSION_IDS)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1519, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "recorded %s %s (slot %zu of %d)" , sshkey_type(key), fp, i, 16); |
1520 | e->session_ids[i].key = key; |
1521 | e->session_ids[i].forwarded = fwd != 0; |
1522 | key = NULL((void*)0); /* transferred */ |
1523 | /* can't transfer sid; it's refcounted and scoped to request's life */ |
1524 | if ((e->session_ids[i].sid = sshbuf_new()) == NULL((void*)0)) |
1525 | fatal_f("sshbuf_new")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1525, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_new"); |
1526 | if ((r = sshbuf_putb(e->session_ids[i].sid, sid)) != 0) |
1527 | fatal_fr(r, "sshbuf_putb session ID")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1527, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "sshbuf_putb session ID" ); |
1528 | /* success */ |
1529 | r = 0; |
1530 | out: |
1531 | sshkey_free(key); |
1532 | sshbuf_free(sid); |
1533 | sshbuf_free(sig); |
1534 | return r == 0 ? 1 : 0; |
1535 | } |
1536 | |
1537 | static void |
1538 | process_extension(SocketEntry *e) |
1539 | { |
1540 | int r, success = 0; |
1541 | char *name; |
1542 | |
1543 | debug2_f("entering")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1543, 1, SYSLOG_LEVEL_DEBUG2, ((void*)0), "entering"); |
1544 | if ((r = sshbuf_get_cstring(e->request, &name, NULL((void*)0))) != 0) { |
1545 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1545, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
1546 | goto send; |
1547 | } |
1548 | if (strcmp(name, "session-bind@openssh.com") == 0) |
1549 | success = process_ext_session_bind(e); |
1550 | else |
1551 | debug_f("unsupported extension \"%s\"", name)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1551, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "unsupported extension \"%s\"" , name); |
1552 | free(name); |
1553 | send: |
1554 | send_status(e, success); |
1555 | } |
1556 | /* |
1557 | * dispatch incoming message. |
1558 | * returns 1 on success, 0 for incomplete messages or -1 on error. |
1559 | */ |
1560 | static int |
1561 | process_message(u_int socknum) |
1562 | { |
1563 | u_int msg_len; |
1564 | u_char type; |
1565 | const u_char *cp; |
1566 | int r; |
1567 | SocketEntry *e; |
1568 | |
1569 | if (socknum >= sockets_alloc) |
1570 | fatal_f("sock %u >= allocated %u", socknum, sockets_alloc)sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1570, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sock %u >= allocated %u" , socknum, sockets_alloc); |
1571 | e = &sockets[socknum]; |
1572 | |
1573 | if (sshbuf_len(e->input) < 5) |
1574 | return 0; /* Incomplete message header. */ |
1575 | cp = sshbuf_ptr(e->input); |
1576 | msg_len = PEEK_U32(cp)(((u_int32_t)(((const u_char *)(cp))[0]) << 24) | ((u_int32_t )(((const u_char *)(cp))[1]) << 16) | ((u_int32_t)(((const u_char *)(cp))[2]) << 8) | (u_int32_t)(((const u_char * )(cp))[3])); |
1577 | if (msg_len > AGENT_MAX_LEN(256*1024)) { |
1578 | debug_f("socket %u (fd=%d) message too long %u > %u",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1579, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "socket %u (fd=%d) message too long %u > %u" , socknum, e->fd, msg_len, (256*1024)) |
1579 | socknum, e->fd, msg_len, AGENT_MAX_LEN)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1579, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "socket %u (fd=%d) message too long %u > %u" , socknum, e->fd, msg_len, (256*1024)); |
1580 | return -1; |
1581 | } |
1582 | if (sshbuf_len(e->input) < msg_len + 4) |
1583 | return 0; /* Incomplete message body. */ |
1584 | |
1585 | /* move the current input to e->request */ |
1586 | sshbuf_reset(e->request); |
1587 | if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || |
1588 | (r = sshbuf_get_u8(e->request, &type)) != 0) { |
1589 | if (r == SSH_ERR_MESSAGE_INCOMPLETE-3 || |
1590 | r == SSH_ERR_STRING_TOO_LARGE-6) { |
1591 | error_fr(r, "parse")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1591, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), "parse"); |
1592 | return -1; |
1593 | } |
1594 | fatal_fr(r, "parse")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1594, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "parse"); |
1595 | } |
1596 | |
1597 | debug_f("socket %u (fd=%d) type %d", socknum, e->fd, type)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1597, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "socket %u (fd=%d) type %d" , socknum, e->fd, type); |
1598 | |
1599 | /* check whether agent is locked */ |
1600 | if (locked && type != SSH_AGENTC_UNLOCK23) { |
1601 | sshbuf_reset(e->request); |
1602 | switch (type) { |
1603 | case SSH2_AGENTC_REQUEST_IDENTITIES11: |
1604 | /* send empty lists */ |
1605 | no_identities(e); |
1606 | break; |
1607 | default: |
1608 | /* send a fail message for all other request types */ |
1609 | send_status(e, 0); |
1610 | } |
1611 | return 1; |
1612 | } |
1613 | |
1614 | switch (type) { |
1615 | case SSH_AGENTC_LOCK22: |
1616 | case SSH_AGENTC_UNLOCK23: |
1617 | process_lock_agent(e, type == SSH_AGENTC_LOCK22); |
1618 | break; |
1619 | case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES9: |
1620 | process_remove_all_identities(e); /* safe for !WITH_SSH1 */ |
1621 | break; |
1622 | /* ssh2 */ |
1623 | case SSH2_AGENTC_SIGN_REQUEST13: |
1624 | process_sign_request2(e); |
1625 | break; |
1626 | case SSH2_AGENTC_REQUEST_IDENTITIES11: |
1627 | process_request_identities(e); |
1628 | break; |
1629 | case SSH2_AGENTC_ADD_IDENTITY17: |
1630 | case SSH2_AGENTC_ADD_ID_CONSTRAINED25: |
1631 | process_add_identity(e); |
1632 | break; |
1633 | case SSH2_AGENTC_REMOVE_IDENTITY18: |
1634 | process_remove_identity(e); |
1635 | break; |
1636 | case SSH2_AGENTC_REMOVE_ALL_IDENTITIES19: |
1637 | process_remove_all_identities(e); |
1638 | break; |
1639 | #ifdef ENABLE_PKCS111 |
1640 | case SSH_AGENTC_ADD_SMARTCARD_KEY20: |
1641 | case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED26: |
1642 | process_add_smartcard_key(e); |
1643 | break; |
1644 | case SSH_AGENTC_REMOVE_SMARTCARD_KEY21: |
1645 | process_remove_smartcard_key(e); |
1646 | break; |
1647 | #endif /* ENABLE_PKCS11 */ |
1648 | case SSH_AGENTC_EXTENSION27: |
1649 | process_extension(e); |
1650 | break; |
1651 | default: |
1652 | /* Unknown message. Respond with failure. */ |
1653 | error("Unknown message %d", type)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1653, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "Unknown message %d" , type); |
1654 | sshbuf_reset(e->request); |
1655 | send_status(e, 0); |
1656 | break; |
1657 | } |
1658 | return 1; |
1659 | } |
1660 | |
1661 | static void |
1662 | new_socket(sock_type type, int fd) |
1663 | { |
1664 | u_int i, old_alloc, new_alloc; |
1665 | |
1666 | debug_f("type = %s", type == AUTH_CONNECTION ? "CONNECTION" :sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1667, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "type = %s", type == AUTH_CONNECTION ? "CONNECTION" : (type == AUTH_SOCKET ? "SOCKET" : "UNKNOWN")) |
1667 | (type == AUTH_SOCKET ? "SOCKET" : "UNKNOWN"))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1667, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "type = %s", type == AUTH_CONNECTION ? "CONNECTION" : (type == AUTH_SOCKET ? "SOCKET" : "UNKNOWN")); |
1668 | set_nonblock(fd); |
1669 | |
1670 | if (fd > max_fd) |
1671 | max_fd = fd; |
1672 | |
1673 | for (i = 0; i < sockets_alloc; i++) |
1674 | if (sockets[i].type == AUTH_UNUSED) { |
1675 | sockets[i].fd = fd; |
1676 | if ((sockets[i].input = sshbuf_new()) == NULL((void*)0) || |
1677 | (sockets[i].output = sshbuf_new()) == NULL((void*)0) || |
1678 | (sockets[i].request = sshbuf_new()) == NULL((void*)0)) |
1679 | fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1679, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_new failed" ); |
1680 | sockets[i].type = type; |
1681 | return; |
1682 | } |
1683 | old_alloc = sockets_alloc; |
1684 | new_alloc = sockets_alloc + 10; |
1685 | sockets = xrecallocarray(sockets, old_alloc, new_alloc, |
1686 | sizeof(sockets[0])); |
1687 | for (i = old_alloc; i < new_alloc; i++) |
1688 | sockets[i].type = AUTH_UNUSED; |
1689 | sockets_alloc = new_alloc; |
1690 | sockets[old_alloc].fd = fd; |
1691 | if ((sockets[old_alloc].input = sshbuf_new()) == NULL((void*)0) || |
1692 | (sockets[old_alloc].output = sshbuf_new()) == NULL((void*)0) || |
1693 | (sockets[old_alloc].request = sshbuf_new()) == NULL((void*)0)) |
1694 | fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1694, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "sshbuf_new failed" ); |
1695 | sockets[old_alloc].type = type; |
1696 | } |
1697 | |
1698 | static int |
1699 | handle_socket_read(u_int socknum) |
1700 | { |
1701 | struct sockaddr_un sunaddr; |
1702 | socklen_t slen; |
1703 | uid_t euid; |
1704 | gid_t egid; |
1705 | int fd; |
1706 | |
1707 | slen = sizeof(sunaddr); |
1708 | fd = accept(sockets[socknum].fd, (struct sockaddr *)&sunaddr, &slen); |
1709 | if (fd == -1) { |
1710 | error("accept from AUTH_SOCKET: %s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1710, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "accept from AUTH_SOCKET: %s" , strerror((*__errno()))); |
1711 | return -1; |
1712 | } |
1713 | if (getpeereid(fd, &euid, &egid) == -1) { |
1714 | error("getpeereid %d failed: %s", fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1714, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "getpeereid %d failed: %s" , fd, strerror((*__errno()))); |
1715 | close(fd); |
1716 | return -1; |
1717 | } |
1718 | if ((euid != 0) && (getuid() != euid)) { |
1719 | error("uid mismatch: peer euid %u != uid %u",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1720, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "uid mismatch: peer euid %u != uid %u" , (u_int) euid, (u_int) getuid()) |
1720 | (u_int) euid, (u_int) getuid())sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1720, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "uid mismatch: peer euid %u != uid %u" , (u_int) euid, (u_int) getuid()); |
1721 | close(fd); |
1722 | return -1; |
1723 | } |
1724 | new_socket(AUTH_CONNECTION, fd); |
1725 | return 0; |
1726 | } |
1727 | |
1728 | static int |
1729 | handle_conn_read(u_int socknum) |
1730 | { |
1731 | char buf[AGENT_RBUF_LEN(4096)]; |
1732 | ssize_t len; |
1733 | int r; |
1734 | |
1735 | if ((len = read(sockets[socknum].fd, buf, sizeof(buf))) <= 0) { |
1736 | if (len == -1) { |
1737 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == EINTR4) |
1738 | return 0; |
1739 | error_f("read error on socket %u (fd %d): %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1740, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "read error on socket %u (fd %d): %s" , socknum, sockets[socknum].fd, strerror((*__errno()))) |
1740 | socknum, sockets[socknum].fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1740, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "read error on socket %u (fd %d): %s" , socknum, sockets[socknum].fd, strerror((*__errno()))); |
1741 | } |
1742 | return -1; |
1743 | } |
1744 | if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0) |
1745 | fatal_fr(r, "compose")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1745, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "compose"); |
1746 | explicit_bzero(buf, sizeof(buf)); |
1747 | for (;;) { |
1748 | if ((r = process_message(socknum)) == -1) |
1749 | return -1; |
1750 | else if (r == 0) |
1751 | break; |
1752 | } |
1753 | return 0; |
1754 | } |
1755 | |
1756 | static int |
1757 | handle_conn_write(u_int socknum) |
1758 | { |
1759 | ssize_t len; |
1760 | int r; |
1761 | |
1762 | if (sshbuf_len(sockets[socknum].output) == 0) |
1763 | return 0; /* shouldn't happen */ |
1764 | if ((len = write(sockets[socknum].fd, |
1765 | sshbuf_ptr(sockets[socknum].output), |
1766 | sshbuf_len(sockets[socknum].output))) <= 0) { |
1767 | if (len == -1) { |
1768 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == EINTR4) |
1769 | return 0; |
1770 | error_f("read error on socket %u (fd %d): %s",sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1771, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "read error on socket %u (fd %d): %s" , socknum, sockets[socknum].fd, strerror((*__errno()))) |
1771 | socknum, sockets[socknum].fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1771, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "read error on socket %u (fd %d): %s" , socknum, sockets[socknum].fd, strerror((*__errno()))); |
1772 | } |
1773 | return -1; |
1774 | } |
1775 | if ((r = sshbuf_consume(sockets[socknum].output, len)) != 0) |
1776 | fatal_fr(r, "consume")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1776, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "consume"); |
1777 | return 0; |
1778 | } |
1779 | |
1780 | static void |
1781 | after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds) |
1782 | { |
1783 | size_t i; |
1784 | u_int socknum, activefds = npfd; |
1785 | |
1786 | for (i = 0; i < npfd; i++) { |
1787 | if (pfd[i].revents == 0) |
1788 | continue; |
1789 | /* Find sockets entry */ |
1790 | for (socknum = 0; socknum < sockets_alloc; socknum++) { |
1791 | if (sockets[socknum].type != AUTH_SOCKET && |
1792 | sockets[socknum].type != AUTH_CONNECTION) |
1793 | continue; |
1794 | if (pfd[i].fd == sockets[socknum].fd) |
1795 | break; |
1796 | } |
1797 | if (socknum >= sockets_alloc) { |
1798 | error_f("no socket for fd %d", pfd[i].fd)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1798, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "no socket for fd %d" , pfd[i].fd); |
1799 | continue; |
1800 | } |
1801 | /* Process events */ |
1802 | switch (sockets[socknum].type) { |
1803 | case AUTH_SOCKET: |
1804 | if ((pfd[i].revents & (POLLIN0x0001|POLLERR0x0008)) == 0) |
1805 | break; |
1806 | if (npfd > maxfds) { |
1807 | debug3("out of fds (active %u >= limit %u); "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1808, 0, SYSLOG_LEVEL_DEBUG3, ((void*)0), "out of fds (active %u >= limit %u); " "skipping accept", activefds, maxfds) |
1808 | "skipping accept", activefds, maxfds)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1808, 0, SYSLOG_LEVEL_DEBUG3, ((void*)0), "out of fds (active %u >= limit %u); " "skipping accept", activefds, maxfds); |
1809 | break; |
1810 | } |
1811 | if (handle_socket_read(socknum) == 0) |
1812 | activefds++; |
1813 | break; |
1814 | case AUTH_CONNECTION: |
1815 | if ((pfd[i].revents & (POLLIN0x0001|POLLHUP0x0010|POLLERR0x0008)) != 0 && |
1816 | handle_conn_read(socknum) != 0) |
1817 | goto close_sock; |
1818 | if ((pfd[i].revents & (POLLOUT0x0004|POLLHUP0x0010)) != 0 && |
1819 | handle_conn_write(socknum) != 0) { |
1820 | close_sock: |
1821 | if (activefds == 0) |
1822 | fatal("activefds == 0 at close_sock")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1822, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "activefds == 0 at close_sock" ); |
1823 | close_socket(&sockets[socknum]); |
1824 | activefds--; |
1825 | break; |
1826 | } |
1827 | break; |
1828 | default: |
1829 | break; |
1830 | } |
1831 | } |
1832 | } |
1833 | |
1834 | static int |
1835 | prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds) |
1836 | { |
1837 | struct pollfd *pfd = *pfdp; |
1838 | size_t i, j, npfd = 0; |
1839 | time_t deadline; |
1840 | int r; |
1841 | |
1842 | /* Count active sockets */ |
1843 | for (i = 0; i < sockets_alloc; i++) { |
1844 | switch (sockets[i].type) { |
1845 | case AUTH_SOCKET: |
1846 | case AUTH_CONNECTION: |
1847 | npfd++; |
1848 | break; |
1849 | case AUTH_UNUSED: |
1850 | break; |
1851 | default: |
1852 | fatal("Unknown socket type %d", sockets[i].type)sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1852, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "Unknown socket type %d" , sockets[i].type); |
1853 | break; |
1854 | } |
1855 | } |
1856 | if (npfd != *npfdp && |
1857 | (pfd = recallocarray(pfd, *npfdp, npfd, sizeof(*pfd))) == NULL((void*)0)) |
1858 | fatal_f("recallocarray failed")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1858, 1, SYSLOG_LEVEL_FATAL, ((void*)0), "recallocarray failed" ); |
1859 | *pfdp = pfd; |
1860 | *npfdp = npfd; |
1861 | |
1862 | for (i = j = 0; i < sockets_alloc; i++) { |
1863 | switch (sockets[i].type) { |
1864 | case AUTH_SOCKET: |
1865 | if (npfd > maxfds) { |
1866 | debug3("out of fds (active %zu >= limit %u); "sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1867, 0, SYSLOG_LEVEL_DEBUG3, ((void*)0), "out of fds (active %zu >= limit %u); " "skipping arming listener", npfd, maxfds) |
1867 | "skipping arming listener", npfd, maxfds)sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1867, 0, SYSLOG_LEVEL_DEBUG3, ((void*)0), "out of fds (active %zu >= limit %u); " "skipping arming listener", npfd, maxfds); |
1868 | break; |
1869 | } |
1870 | pfd[j].fd = sockets[i].fd; |
1871 | pfd[j].revents = 0; |
1872 | pfd[j].events = POLLIN0x0001; |
1873 | j++; |
1874 | break; |
1875 | case AUTH_CONNECTION: |
1876 | pfd[j].fd = sockets[i].fd; |
1877 | pfd[j].revents = 0; |
1878 | /* |
1879 | * Only prepare to read if we can handle a full-size |
1880 | * input read buffer and enqueue a max size reply.. |
1881 | */ |
1882 | if ((r = sshbuf_check_reserve(sockets[i].input, |
1883 | AGENT_RBUF_LEN(4096))) == 0 && |
1884 | (r = sshbuf_check_reserve(sockets[i].output, |
1885 | AGENT_MAX_LEN(256*1024))) == 0) |
1886 | pfd[j].events = POLLIN0x0001; |
1887 | else if (r != SSH_ERR_NO_BUFFER_SPACE-9) |
1888 | fatal_fr(r, "reserve")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1888, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "reserve"); |
1889 | if (sshbuf_len(sockets[i].output) > 0) |
1890 | pfd[j].events |= POLLOUT0x0004; |
1891 | j++; |
1892 | break; |
1893 | default: |
1894 | break; |
1895 | } |
1896 | } |
1897 | deadline = reaper(); |
1898 | if (parent_alive_interval != 0) |
1899 | deadline = (deadline == 0) ? parent_alive_interval : |
1900 | MINIMUM(deadline, parent_alive_interval)(((deadline) < (parent_alive_interval)) ? (deadline) : (parent_alive_interval )); |
1901 | if (deadline == 0) { |
1902 | *timeoutp = -1; /* INFTIM */ |
1903 | } else { |
1904 | if (deadline > INT_MAX2147483647 / 1000) |
1905 | *timeoutp = INT_MAX2147483647 / 1000; |
1906 | else |
1907 | *timeoutp = deadline * 1000; |
1908 | } |
1909 | return (1); |
1910 | } |
1911 | |
1912 | static void |
1913 | cleanup_socket(void) |
1914 | { |
1915 | if (cleanup_pid != 0 && getpid() != cleanup_pid) |
1916 | return; |
1917 | debug_f("cleanup")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1917, 1, SYSLOG_LEVEL_DEBUG1, ((void*)0), "cleanup"); |
1918 | if (socket_name[0]) |
1919 | unlink(socket_name); |
1920 | if (socket_dir[0]) |
1921 | rmdir(socket_dir); |
1922 | } |
1923 | |
1924 | void |
1925 | cleanup_exit(int i) |
1926 | { |
1927 | cleanup_socket(); |
1928 | _exit(i); |
1929 | } |
1930 | |
1931 | /*ARGSUSED*/ |
1932 | static void |
1933 | cleanup_handler(int sig) |
1934 | { |
1935 | cleanup_socket(); |
1936 | #ifdef ENABLE_PKCS111 |
1937 | pkcs11_terminate(); |
1938 | #endif |
1939 | _exit(2); |
1940 | } |
1941 | |
1942 | static void |
1943 | check_parent_exists(void) |
1944 | { |
1945 | /* |
1946 | * If our parent has exited then getppid() will return (pid_t)1, |
1947 | * so testing for that should be safe. |
1948 | */ |
1949 | if (parent_pid != -1 && getppid() != parent_pid) { |
1950 | /* printf("Parent has died - Authentication agent exiting.\n"); */ |
1951 | cleanup_socket(); |
1952 | _exit(2); |
1953 | } |
1954 | } |
1955 | |
1956 | static void |
1957 | usage(void) |
1958 | { |
1959 | fprintf(stderr(&__sF[2]), |
1960 | "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" |
1961 | " [-P allowed_providers] [-t life]\n" |
1962 | " ssh-agent [-a bind_address] [-E fingerprint_hash] [-P allowed_providers]\n" |
1963 | " [-t life] command [arg ...]\n" |
1964 | " ssh-agent [-c | -s] -k\n"); |
1965 | exit(1); |
1966 | } |
1967 | |
1968 | int |
1969 | main(int ac, char **av) |
1970 | { |
1971 | int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; |
1972 | int sock, ch, result, saved_errno; |
1973 | char *shell, *format, *pidstr, *agentsocket = NULL((void*)0); |
1974 | struct rlimit rlim; |
1975 | extern int optind; |
1976 | extern char *optarg; |
1977 | pid_t pid; |
1978 | char pidstrbuf[1 + 3 * sizeof pid]; |
1979 | size_t len; |
1980 | mode_t prev_mask; |
1981 | int timeout = -1; /* INFTIM */ |
1982 | struct pollfd *pfd = NULL((void*)0); |
1983 | size_t npfd = 0; |
1984 | u_int maxfds; |
1985 | |
1986 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
1987 | sanitise_stdfd(); |
1988 | |
1989 | /* drop */ |
1990 | setegid(getgid()); |
1991 | setgid(getgid()); |
The return value from the call to 'setgid' is not checked. If an error occurs in 'setgid', the following code may execute with unexpected privileges | |
1992 | |
1993 | if (getrlimit(RLIMIT_NOFILE8, &rlim) == -1) |
1994 | fatal("%s: getrlimit: %s", __progname, strerror(errno))sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 1994, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "%s: getrlimit: %s" , __progname, strerror((*__errno()))); |
1995 | |
1996 | #ifdef WITH_OPENSSL1 |
1997 | OpenSSL_add_all_algorithms()OPENSSL_add_all_algorithms_noconf(); |
1998 | #endif |
1999 | |
2000 | while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) { |
2001 | switch (ch) { |
2002 | case 'E': |
2003 | fingerprint_hash = ssh_digest_alg_by_name(optarg); |
2004 | if (fingerprint_hash == -1) |
2005 | fatal("Invalid hash algorithm \"%s\"", optarg)sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2005, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "Invalid hash algorithm \"%s\"" , optarg); |
2006 | break; |
2007 | case 'c': |
2008 | if (s_flag) |
2009 | usage(); |
2010 | c_flag++; |
2011 | break; |
2012 | case 'k': |
2013 | k_flag++; |
2014 | break; |
2015 | case 'O': |
2016 | if (strcmp(optarg, "no-restrict-websafe") == 0) |
2017 | restrict_websafe = 0; |
2018 | else |
2019 | fatal("Unknown -O option")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2019, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "Unknown -O option" ); |
2020 | break; |
2021 | case 'P': |
2022 | if (allowed_providers != NULL((void*)0)) |
2023 | fatal("-P option already specified")sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2023, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "-P option already specified" ); |
2024 | allowed_providers = xstrdup(optarg); |
2025 | break; |
2026 | case 's': |
2027 | if (c_flag) |
2028 | usage(); |
2029 | s_flag++; |
2030 | break; |
2031 | case 'd': |
2032 | if (d_flag || D_flag) |
2033 | usage(); |
2034 | d_flag++; |
2035 | break; |
2036 | case 'D': |
2037 | if (d_flag || D_flag) |
2038 | usage(); |
2039 | D_flag++; |
2040 | break; |
2041 | case 'a': |
2042 | agentsocket = optarg; |
2043 | break; |
2044 | case 't': |
2045 | if ((lifetime = convtime(optarg)) == -1) { |
2046 | fprintf(stderr(&__sF[2]), "Invalid lifetime\n"); |
2047 | usage(); |
2048 | } |
2049 | break; |
2050 | default: |
2051 | usage(); |
2052 | } |
2053 | } |
2054 | ac -= optind; |
2055 | av += optind; |
2056 | |
2057 | if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) |
2058 | usage(); |
2059 | |
2060 | if (allowed_providers == NULL((void*)0)) |
2061 | allowed_providers = xstrdup(DEFAULT_ALLOWED_PROVIDERS"/usr/lib*/*,/usr/local/lib*/*"); |
2062 | |
2063 | if (ac == 0 && !c_flag && !s_flag) { |
2064 | shell = getenv("SHELL"); |
2065 | if (shell != NULL((void*)0) && (len = strlen(shell)) > 2 && |
2066 | strncmp(shell + len - 3, "csh", 3) == 0) |
2067 | c_flag = 1; |
2068 | } |
2069 | if (k_flag) { |
2070 | const char *errstr = NULL((void*)0); |
2071 | |
2072 | pidstr = getenv(SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID"); |
2073 | if (pidstr == NULL((void*)0)) { |
2074 | fprintf(stderr(&__sF[2]), "%s not set, cannot kill agent\n", |
2075 | SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID"); |
2076 | exit(1); |
2077 | } |
2078 | pid = (int)strtonum(pidstr, 2, INT_MAX2147483647, &errstr); |
2079 | if (errstr) { |
2080 | fprintf(stderr(&__sF[2]), |
2081 | "%s=\"%s\", which is not a good PID: %s\n", |
2082 | SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID", pidstr, errstr); |
2083 | exit(1); |
2084 | } |
2085 | if (kill(pid, SIGTERM15) == -1) { |
2086 | perror("kill"); |
2087 | exit(1); |
2088 | } |
2089 | format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; |
2090 | printf(format, SSH_AUTHSOCKET_ENV_NAME"SSH_AUTH_SOCK"); |
2091 | printf(format, SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID"); |
2092 | printf("echo Agent pid %ld killed;\n", (long)pid); |
2093 | exit(0); |
2094 | } |
2095 | |
2096 | /* |
2097 | * Minimum file descriptors: |
2098 | * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) + |
2099 | * a few spare for libc / stack protectors / sanitisers, etc. |
2100 | */ |
2101 | #define SSH_AGENT_MIN_FDS(3+1+1+1+4) (3+1+1+1+4) |
2102 | if (rlim.rlim_cur < SSH_AGENT_MIN_FDS(3+1+1+1+4)) |
2103 | fatal("%s: file descriptor rlimit %lld too low (minimum %u)",sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2104, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "%s: file descriptor rlimit %lld too low (minimum %u)" , __progname, (long long)rlim.rlim_cur, (3+1+1+1+4)) |
2104 | __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS)sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2104, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "%s: file descriptor rlimit %lld too low (minimum %u)" , __progname, (long long)rlim.rlim_cur, (3+1+1+1+4)); |
2105 | maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS(3+1+1+1+4); |
2106 | |
2107 | parent_pid = getpid(); |
2108 | |
2109 | if (agentsocket == NULL((void*)0)) { |
2110 | /* Create private directory for agent socket */ |
2111 | mktemp_proto(socket_dir, sizeof(socket_dir)); |
2112 | if (mkdtemp(socket_dir) == NULL((void*)0)) { |
2113 | perror("mkdtemp: private socket dir"); |
2114 | exit(1); |
2115 | } |
2116 | snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, |
2117 | (long)parent_pid); |
2118 | } else { |
2119 | /* Try to use specified agent socket */ |
2120 | socket_dir[0] = '\0'; |
2121 | strlcpy(socket_name, agentsocket, sizeof socket_name); |
2122 | } |
2123 | |
2124 | /* |
2125 | * Create socket early so it will exist before command gets run from |
2126 | * the parent. |
2127 | */ |
2128 | prev_mask = umask(0177); |
2129 | sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG128, 0); |
2130 | if (sock < 0) { |
2131 | /* XXX - unix_listener() calls error() not perror() */ |
2132 | *socket_name = '\0'; /* Don't unlink any existing file */ |
2133 | cleanup_exit(1); |
2134 | } |
2135 | umask(prev_mask); |
2136 | |
2137 | /* |
2138 | * Fork, and have the parent execute the command, if any, or present |
2139 | * the socket data. The child continues as the authentication agent. |
2140 | */ |
2141 | if (D_flag || d_flag) { |
2142 | log_init(__progname, |
2143 | d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, |
2144 | SYSLOG_FACILITY_AUTH, 1); |
2145 | format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; |
2146 | printf(format, SSH_AUTHSOCKET_ENV_NAME"SSH_AUTH_SOCK", socket_name, |
2147 | SSH_AUTHSOCKET_ENV_NAME"SSH_AUTH_SOCK"); |
2148 | printf("echo Agent pid %ld;\n", (long)parent_pid); |
2149 | fflush(stdout(&__sF[1])); |
2150 | goto skip; |
2151 | } |
2152 | pid = fork(); |
2153 | if (pid == -1) { |
2154 | perror("fork"); |
2155 | cleanup_exit(1); |
2156 | } |
2157 | if (pid != 0) { /* Parent - execute the given command. */ |
2158 | close(sock); |
2159 | snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); |
2160 | if (ac == 0) { |
2161 | format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; |
2162 | printf(format, SSH_AUTHSOCKET_ENV_NAME"SSH_AUTH_SOCK", socket_name, |
2163 | SSH_AUTHSOCKET_ENV_NAME"SSH_AUTH_SOCK"); |
2164 | printf(format, SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID", pidstrbuf, |
2165 | SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID"); |
2166 | printf("echo Agent pid %ld;\n", (long)pid); |
2167 | exit(0); |
2168 | } |
2169 | if (setenv(SSH_AUTHSOCKET_ENV_NAME"SSH_AUTH_SOCK", socket_name, 1) == -1 || |
2170 | setenv(SSH_AGENTPID_ENV_NAME"SSH_AGENT_PID", pidstrbuf, 1) == -1) { |
2171 | perror("setenv"); |
2172 | exit(1); |
2173 | } |
2174 | execvp(av[0], av); |
2175 | perror(av[0]); |
2176 | exit(1); |
2177 | } |
2178 | /* child */ |
2179 | log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); |
2180 | |
2181 | if (setsid() == -1) { |
2182 | error("setsid: %s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2182, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "setsid: %s", strerror ((*__errno()))); |
2183 | cleanup_exit(1); |
2184 | } |
2185 | |
2186 | (void)chdir("/"); |
2187 | if (stdfd_devnull(1, 1, 1) == -1) |
2188 | error_f("stdfd_devnull failed")sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2188, 1, SYSLOG_LEVEL_ERROR, ((void*)0), "stdfd_devnull failed" ); |
2189 | |
2190 | /* deny core dumps, since memory contains unencrypted private keys */ |
2191 | rlim.rlim_cur = rlim.rlim_max = 0; |
2192 | if (setrlimit(RLIMIT_CORE4, &rlim) == -1) { |
2193 | error("setrlimit RLIMIT_CORE: %s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2193, 0, SYSLOG_LEVEL_ERROR, ((void*)0), "setrlimit RLIMIT_CORE: %s" , strerror((*__errno()))); |
2194 | cleanup_exit(1); |
2195 | } |
2196 | |
2197 | skip: |
2198 | |
2199 | cleanup_pid = getpid(); |
2200 | |
2201 | #ifdef ENABLE_PKCS111 |
2202 | pkcs11_init(0); |
2203 | #endif |
2204 | new_socket(AUTH_SOCKET, sock); |
2205 | if (ac > 0) |
2206 | parent_alive_interval = 10; |
2207 | idtab_init(); |
2208 | ssh_signal(SIGPIPE13, SIG_IGN(void (*)(int))1); |
2209 | ssh_signal(SIGINT2, (d_flag | D_flag) ? cleanup_handler : SIG_IGN(void (*)(int))1); |
2210 | ssh_signal(SIGHUP1, cleanup_handler); |
2211 | ssh_signal(SIGTERM15, cleanup_handler); |
2212 | |
2213 | if (pledge("stdio rpath cpath unix id proc exec", NULL((void*)0)) == -1) |
2214 | fatal("%s: pledge: %s", __progname, strerror(errno))sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2214, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "%s: pledge: %s", __progname , strerror((*__errno()))); |
2215 | |
2216 | while (1) { |
2217 | prepare_poll(&pfd, &npfd, &timeout, maxfds); |
2218 | result = poll(pfd, npfd, timeout); |
2219 | saved_errno = errno(*__errno()); |
2220 | if (parent_alive_interval != 0) |
2221 | check_parent_exists(); |
2222 | (void) reaper(); /* remove expired keys */ |
2223 | if (result == -1) { |
2224 | if (saved_errno == EINTR4) |
2225 | continue; |
2226 | fatal("poll: %s", strerror(saved_errno))sshfatal("/usr/src/usr.bin/ssh/ssh-agent/../ssh-agent.c", __func__ , 2226, 0, SYSLOG_LEVEL_FATAL, ((void*)0), "poll: %s", strerror (saved_errno)); |
2227 | } else if (result > 0) |
2228 | after_poll(pfd, npfd, maxfds); |
2229 | } |
2230 | /* NOTREACHED */ |
2231 | } |