Merge branch 'vendor/OPENRESOLV'
[dragonfly.git] / crypto / openssh / authfd.c
1 /* $OpenBSD: authfd.c,v 1.105 2017/07/01 13:50:45 djm 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  * Functions for connecting the local authentication agent.
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  * SSH2 implementation,
15  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37
38 #include "includes.h"
39
40 #include <sys/types.h>
41 #include <sys/un.h>
42 #include <sys/socket.h>
43
44 #include <fcntl.h>
45 #include <stdlib.h>
46 #include <signal.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <errno.h>
51
52 #include "xmalloc.h"
53 #include "ssh.h"
54 #include "sshbuf.h"
55 #include "sshkey.h"
56 #include "authfd.h"
57 #include "cipher.h"
58 #include "compat.h"
59 #include "log.h"
60 #include "atomicio.h"
61 #include "misc.h"
62 #include "ssherr.h"
63
64 #define MAX_AGENT_IDENTITIES    2048            /* Max keys in agent reply */
65 #define MAX_AGENT_REPLY_LEN     (256 * 1024)    /* Max bytes in agent reply */
66
67 /* macro to check for "agent failure" message */
68 #define agent_failed(x) \
69     ((x == SSH_AGENT_FAILURE) || \
70     (x == SSH_COM_AGENT2_FAILURE) || \
71     (x == SSH2_AGENT_FAILURE))
72
73 /* Convert success/failure response from agent to a err.h status */
74 static int
75 decode_reply(u_char type)
76 {
77         if (agent_failed(type))
78                 return SSH_ERR_AGENT_FAILURE;
79         else if (type == SSH_AGENT_SUCCESS)
80                 return 0;
81         else
82                 return SSH_ERR_INVALID_FORMAT;
83 }
84
85 /* Returns the number of the authentication fd, or -1 if there is none. */
86 int
87 ssh_get_authentication_socket(int *fdp)
88 {
89         const char *authsocket;
90         int sock, oerrno;
91         struct sockaddr_un sunaddr;
92
93         if (fdp != NULL)
94                 *fdp = -1;
95
96         authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
97         if (!authsocket)
98                 return SSH_ERR_AGENT_NOT_PRESENT;
99
100         memset(&sunaddr, 0, sizeof(sunaddr));
101         sunaddr.sun_family = AF_UNIX;
102         strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
103
104         if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
105                 return SSH_ERR_SYSTEM_ERROR;
106
107         /* close on exec */
108         if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 ||
109             connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
110                 oerrno = errno;
111                 close(sock);
112                 errno = oerrno;
113                 return SSH_ERR_SYSTEM_ERROR;
114         }
115         if (fdp != NULL)
116                 *fdp = sock;
117         else
118                 close(sock);
119         return 0;
120 }
121
122 /* Communicate with agent: send request and read reply */
123 static int
124 ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
125 {
126         int r;
127         size_t l, len;
128         char buf[1024];
129
130         /* Get the length of the message, and format it in the buffer. */
131         len = sshbuf_len(request);
132         put_u32(buf, len);
133
134         /* Send the length and then the packet to the agent. */
135         if (atomicio(vwrite, sock, buf, 4) != 4 ||
136             atomicio(vwrite, sock, (u_char *)sshbuf_ptr(request),
137             sshbuf_len(request)) != sshbuf_len(request))
138                 return SSH_ERR_AGENT_COMMUNICATION;
139         /*
140          * Wait for response from the agent.  First read the length of the
141          * response packet.
142          */
143         if (atomicio(read, sock, buf, 4) != 4)
144             return SSH_ERR_AGENT_COMMUNICATION;
145
146         /* Extract the length, and check it for sanity. */
147         len = get_u32(buf);
148         if (len > MAX_AGENT_REPLY_LEN)
149                 return SSH_ERR_INVALID_FORMAT;
150
151         /* Read the rest of the response in to the buffer. */
152         sshbuf_reset(reply);
153         while (len > 0) {
154                 l = len;
155                 if (l > sizeof(buf))
156                         l = sizeof(buf);
157                 if (atomicio(read, sock, buf, l) != l)
158                         return SSH_ERR_AGENT_COMMUNICATION;
159                 if ((r = sshbuf_put(reply, buf, l)) != 0)
160                         return r;
161                 len -= l;
162         }
163         return 0;
164 }
165
166 /*
167  * Closes the agent socket if it should be closed (depends on how it was
168  * obtained).  The argument must have been returned by
169  * ssh_get_authentication_socket().
170  */
171 void
172 ssh_close_authentication_socket(int sock)
173 {
174         if (getenv(SSH_AUTHSOCKET_ENV_NAME))
175                 close(sock);
176 }
177
178 /* Lock/unlock agent */
179 int
180 ssh_lock_agent(int sock, int lock, const char *password)
181 {
182         int r;
183         u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK;
184         struct sshbuf *msg;
185
186         if ((msg = sshbuf_new()) == NULL)
187                 return SSH_ERR_ALLOC_FAIL;
188         if ((r = sshbuf_put_u8(msg, type)) != 0 ||
189             (r = sshbuf_put_cstring(msg, password)) != 0)
190                 goto out;
191         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
192                 goto out;
193         if ((r = sshbuf_get_u8(msg, &type)) != 0)
194                 goto out;
195         r = decode_reply(type);
196  out:
197         sshbuf_free(msg);
198         return r;
199 }
200
201
202 static int
203 deserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp)
204 {
205         int r;
206         char *comment = NULL;
207         const u_char *blob;
208         size_t blen;
209
210         if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 ||
211             (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0)
212                 goto out;
213         if ((r = sshkey_from_blob(blob, blen, keyp)) != 0)
214                 goto out;
215         if (commentp != NULL) {
216                 *commentp = comment;
217                 comment = NULL;
218         }
219         r = 0;
220  out:
221         free(comment);
222         return r;
223 }
224
225 /*
226  * Fetch list of identities held by the agent.
227  */
228 int
229 ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp)
230 {
231         u_char type;
232         u_int32_t num, i;
233         struct sshbuf *msg;
234         struct ssh_identitylist *idl = NULL;
235         int r;
236
237         /*
238          * Send a message to the agent requesting for a list of the
239          * identities it can represent.
240          */
241         if ((msg = sshbuf_new()) == NULL)
242                 return SSH_ERR_ALLOC_FAIL;
243         if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REQUEST_IDENTITIES)) != 0)
244                 goto out;
245
246         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
247                 goto out;
248
249         /* Get message type, and verify that we got a proper answer. */
250         if ((r = sshbuf_get_u8(msg, &type)) != 0)
251                 goto out;
252         if (agent_failed(type)) {
253                 r = SSH_ERR_AGENT_FAILURE;
254                 goto out;
255         } else if (type != SSH2_AGENT_IDENTITIES_ANSWER) {
256                 r = SSH_ERR_INVALID_FORMAT;
257                 goto out;
258         }
259
260         /* Get the number of entries in the response and check it for sanity. */
261         if ((r = sshbuf_get_u32(msg, &num)) != 0)
262                 goto out;
263         if (num > MAX_AGENT_IDENTITIES) {
264                 r = SSH_ERR_INVALID_FORMAT;
265                 goto out;
266         }
267         if (num == 0) {
268                 r = SSH_ERR_AGENT_NO_IDENTITIES;
269                 goto out;
270         }
271
272         /* Deserialise the response into a list of keys/comments */
273         if ((idl = calloc(1, sizeof(*idl))) == NULL ||
274             (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL ||
275             (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) {
276                 r = SSH_ERR_ALLOC_FAIL;
277                 goto out;
278         }
279         for (i = 0; i < num;) {
280                 if ((r = deserialise_identity2(msg, &(idl->keys[i]),
281                     &(idl->comments[i]))) != 0) {
282                         if (r == SSH_ERR_KEY_TYPE_UNKNOWN) {
283                                 /* Gracefully skip unknown key types */
284                                 num--;
285                                 continue;
286                         } else
287                                 goto out;
288                 }
289                 i++;
290         }
291         idl->nkeys = num;
292         *idlp = idl;
293         idl = NULL;
294         r = 0;
295  out:
296         sshbuf_free(msg);
297         if (idl != NULL)
298                 ssh_free_identitylist(idl);
299         return r;
300 }
301
302 void
303 ssh_free_identitylist(struct ssh_identitylist *idl)
304 {
305         size_t i;
306
307         if (idl == NULL)
308                 return;
309         for (i = 0; i < idl->nkeys; i++) {
310                 if (idl->keys != NULL)
311                         sshkey_free(idl->keys[i]);
312                 if (idl->comments != NULL)
313                         free(idl->comments[i]);
314         }
315         free(idl);
316 }
317
318 /*
319  * Sends a challenge (typically from a server via ssh(1)) to the agent,
320  * and waits for a response from the agent.
321  * Returns true (non-zero) if the agent gave the correct answer, zero
322  * otherwise.
323  */
324
325
326 /* encode signature algoritm in flag bits, so we can keep the msg format */
327 static u_int
328 agent_encode_alg(const struct sshkey *key, const char *alg)
329 {
330         if (alg != NULL && key->type == KEY_RSA) {
331                 if (strcmp(alg, "rsa-sha2-256") == 0)
332                         return SSH_AGENT_RSA_SHA2_256;
333                 else if (strcmp(alg, "rsa-sha2-512") == 0)
334                         return SSH_AGENT_RSA_SHA2_512;
335         }
336         return 0;
337 }
338
339 /* ask agent to sign data, returns err.h code on error, 0 on success */
340 int
341 ssh_agent_sign(int sock, const struct sshkey *key,
342     u_char **sigp, size_t *lenp,
343     const u_char *data, size_t datalen, const char *alg, u_int compat)
344 {
345         struct sshbuf *msg;
346         u_char *blob = NULL, type;
347         size_t blen = 0, len = 0;
348         u_int flags = 0;
349         int r = SSH_ERR_INTERNAL_ERROR;
350
351         *sigp = NULL;
352         *lenp = 0;
353
354         if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
355                 return SSH_ERR_INVALID_ARGUMENT;
356         if (compat & SSH_BUG_SIGBLOB)
357                 flags |= SSH_AGENT_OLD_SIGNATURE;
358         if ((msg = sshbuf_new()) == NULL)
359                 return SSH_ERR_ALLOC_FAIL;
360         if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
361                 goto out;
362         flags |= agent_encode_alg(key, alg);
363         if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
364             (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
365             (r = sshbuf_put_string(msg, data, datalen)) != 0 ||
366             (r = sshbuf_put_u32(msg, flags)) != 0)
367                 goto out;
368         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
369                 goto out;
370         if ((r = sshbuf_get_u8(msg, &type)) != 0)
371                 goto out;
372         if (agent_failed(type)) {
373                 r = SSH_ERR_AGENT_FAILURE;
374                 goto out;
375         } else if (type != SSH2_AGENT_SIGN_RESPONSE) {
376                 r = SSH_ERR_INVALID_FORMAT;
377                 goto out;
378         }
379         if ((r = sshbuf_get_string(msg, sigp, &len)) != 0)
380                 goto out;
381         *lenp = len;
382         r = 0;
383  out:
384         if (blob != NULL) {
385                 explicit_bzero(blob, blen);
386                 free(blob);
387         }
388         sshbuf_free(msg);
389         return r;
390 }
391
392 /* Encode key for a message to the agent. */
393
394
395 static int
396 ssh_encode_identity_ssh2(struct sshbuf *b, struct sshkey *key,
397     const char *comment)
398 {
399         int r;
400
401         if ((r = sshkey_private_serialize(key, b)) != 0 ||
402             (r = sshbuf_put_cstring(b, comment)) != 0)
403                 return r;
404         return 0;
405 }
406
407 static int
408 encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
409 {
410         int r;
411
412         if (life != 0) {
413                 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 ||
414                     (r = sshbuf_put_u32(m, life)) != 0)
415                         goto out;
416         }
417         if (confirm != 0) {
418                 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0)
419                         goto out;
420         }
421         r = 0;
422  out:
423         return r;
424 }
425
426 /*
427  * Adds an identity to the authentication server.
428  * This call is intended only for use by ssh-add(1) and like applications.
429  */
430 int
431 ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment,
432     u_int life, u_int confirm)
433 {
434         struct sshbuf *msg;
435         int r, constrained = (life || confirm);
436         u_char type;
437
438         if ((msg = sshbuf_new()) == NULL)
439                 return SSH_ERR_ALLOC_FAIL;
440
441         switch (key->type) {
442 #ifdef WITH_OPENSSL
443         case KEY_RSA:
444         case KEY_RSA_CERT:
445         case KEY_DSA:
446         case KEY_DSA_CERT:
447         case KEY_ECDSA:
448         case KEY_ECDSA_CERT:
449 #endif
450         case KEY_ED25519:
451         case KEY_ED25519_CERT:
452                 type = constrained ?
453                     SSH2_AGENTC_ADD_ID_CONSTRAINED :
454                     SSH2_AGENTC_ADD_IDENTITY;
455                 if ((r = sshbuf_put_u8(msg, type)) != 0 ||
456                     (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0)
457                         goto out;
458                 break;
459         default:
460                 r = SSH_ERR_INVALID_ARGUMENT;
461                 goto out;
462         }
463         if (constrained &&
464             (r = encode_constraints(msg, life, confirm)) != 0)
465                 goto out;
466         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
467                 goto out;
468         if ((r = sshbuf_get_u8(msg, &type)) != 0)
469                 goto out;
470         r = decode_reply(type);
471  out:
472         sshbuf_free(msg);
473         return r;
474 }
475
476 /*
477  * Removes an identity from the authentication server.
478  * This call is intended only for use by ssh-add(1) and like applications.
479  */
480 int
481 ssh_remove_identity(int sock, struct sshkey *key)
482 {
483         struct sshbuf *msg;
484         int r;
485         u_char type, *blob = NULL;
486         size_t blen;
487
488         if ((msg = sshbuf_new()) == NULL)
489                 return SSH_ERR_ALLOC_FAIL;
490
491         if (key->type != KEY_UNSPEC) {
492                 if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
493                         goto out;
494                 if ((r = sshbuf_put_u8(msg,
495                     SSH2_AGENTC_REMOVE_IDENTITY)) != 0 ||
496                     (r = sshbuf_put_string(msg, blob, blen)) != 0)
497                         goto out;
498         } else {
499                 r = SSH_ERR_INVALID_ARGUMENT;
500                 goto out;
501         }
502         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
503                 goto out;
504         if ((r = sshbuf_get_u8(msg, &type)) != 0)
505                 goto out;
506         r = decode_reply(type);
507  out:
508         if (blob != NULL) {
509                 explicit_bzero(blob, blen);
510                 free(blob);
511         }
512         sshbuf_free(msg);
513         return r;
514 }
515
516 /*
517  * Add/remove an token-based identity from the authentication server.
518  * This call is intended only for use by ssh-add(1) and like applications.
519  */
520 int
521 ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
522     u_int life, u_int confirm)
523 {
524         struct sshbuf *msg;
525         int r, constrained = (life || confirm);
526         u_char type;
527
528         if (add) {
529                 type = constrained ?
530                     SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED :
531                     SSH_AGENTC_ADD_SMARTCARD_KEY;
532         } else
533                 type = SSH_AGENTC_REMOVE_SMARTCARD_KEY;
534
535         if ((msg = sshbuf_new()) == NULL)
536                 return SSH_ERR_ALLOC_FAIL;
537         if ((r = sshbuf_put_u8(msg, type)) != 0 ||
538             (r = sshbuf_put_cstring(msg, reader_id)) != 0 ||
539             (r = sshbuf_put_cstring(msg, pin)) != 0)
540                 goto out;
541         if (constrained &&
542             (r = encode_constraints(msg, life, confirm)) != 0)
543                 goto out;
544         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
545                 goto out;
546         if ((r = sshbuf_get_u8(msg, &type)) != 0)
547                 goto out;
548         r = decode_reply(type);
549  out:
550         sshbuf_free(msg);
551         return r;
552 }
553
554 /*
555  * Removes all identities from the agent.
556  * This call is intended only for use by ssh-add(1) and like applications.
557  *
558  * This supports the SSH protocol 1 message to because, when clearing all
559  * keys from an agent, we generally want to clear both protocol v1 and v2
560  * keys.
561  */
562 int
563 ssh_remove_all_identities(int sock, int version)
564 {
565         struct sshbuf *msg;
566         u_char type = (version == 1) ?
567             SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES :
568             SSH2_AGENTC_REMOVE_ALL_IDENTITIES;
569         int r;
570
571         if ((msg = sshbuf_new()) == NULL)
572                 return SSH_ERR_ALLOC_FAIL;
573         if ((r = sshbuf_put_u8(msg, type)) != 0)
574                 goto out;
575         if ((r = ssh_request_reply(sock, msg, msg)) != 0)
576                 goto out;
577         if ((r = sshbuf_get_u8(msg, &type)) != 0)
578                 goto out;
579         r = decode_reply(type);
580  out:
581         sshbuf_free(msg);
582         return r;
583 }