1 | /* |
---|
2 | |
---|
3 | authfd.c |
---|
4 | |
---|
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> |
---|
6 | |
---|
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
---|
8 | All rights reserved |
---|
9 | |
---|
10 | Created: Wed Mar 29 01:30:28 1995 ylo |
---|
11 | |
---|
12 | Functions for connecting the local authentication agent. |
---|
13 | |
---|
14 | */ |
---|
15 | |
---|
16 | /* |
---|
17 | * $Id: authfd.c,v 1.1.1.4 1999-03-08 17:43:16 danw Exp $ |
---|
18 | * $Log: not supported by cvs2svn $ |
---|
19 | * Revision 1.16 1998/05/23 20:20:49 kivinen |
---|
20 | * Changed () -> (void). |
---|
21 | * |
---|
22 | * Revision 1.15 1998/03/27 16:56:30 kivinen |
---|
23 | * Allow authentication socket to be symlink, if not suid. Fixed |
---|
24 | * authsocketdir freeing. |
---|
25 | * |
---|
26 | * Revision 1.14 1998/01/02 06:15:49 kivinen |
---|
27 | * Fixed agent socket opening routine. |
---|
28 | * |
---|
29 | * Revision 1.13 1997/04/17 04:00:46 kivinen |
---|
30 | * Removed extra namelen variable. |
---|
31 | * |
---|
32 | * Revision 1.12 1997/03/26 07:00:57 kivinen |
---|
33 | * Changed uid 0 to UID_ROOT. |
---|
34 | * Fixed memory leak. |
---|
35 | * Removed ssh_close_authentication function. |
---|
36 | * |
---|
37 | * Revision 1.11 1996/11/19 22:44:03 kivinen |
---|
38 | * Changed socket directory checks so that if the |
---|
39 | * original_real_uid is root do not check the file owner. |
---|
40 | * |
---|
41 | * Revision 1.10 1996/10/29 22:34:52 kivinen |
---|
42 | * log -> log_msg. Removed userfile.h. |
---|
43 | * |
---|
44 | * Revision 1.9 1996/10/24 14:05:44 ttsalo |
---|
45 | * Cleaning up old fd-auth trash |
---|
46 | * |
---|
47 | * Revision 1.8 1996/10/21 16:15:40 ttsalo |
---|
48 | * Fixed auth socket name handling |
---|
49 | * |
---|
50 | * Revision 1.7 1996/10/20 16:26:17 ttsalo |
---|
51 | * Modified the routines to use agent socket directly |
---|
52 | * |
---|
53 | * Revision 1.6 1996/10/03 18:46:13 ylo |
---|
54 | * Fixed a bug that caused "Received signal 14" errors. |
---|
55 | * |
---|
56 | * Revision 1.5 1996/09/27 13:56:35 ttsalo |
---|
57 | * Fixed a memory deallocation bug |
---|
58 | * |
---|
59 | * Revision 1.4 1996/09/11 17:54:22 kivinen |
---|
60 | * Added check for bind errors in |
---|
61 | * ssh_get_authentication_connection_fd. |
---|
62 | * Fixed bug in old alarm / timeout restoration. |
---|
63 | * Changed limit of messages from 256 kB to 30 kB. |
---|
64 | * |
---|
65 | * Revision 1.3 1996/09/08 17:21:04 ttsalo |
---|
66 | * A lot of changes in agent-socket handling |
---|
67 | * |
---|
68 | * Revision 1.2 1996/09/04 12:41:50 ttsalo |
---|
69 | * Minor fixes |
---|
70 | * |
---|
71 | * Revision 1.1.1.1 1996/02/18 21:38:11 ylo |
---|
72 | * Imported ssh-1.2.13. |
---|
73 | * |
---|
74 | * Revision 1.7 1995/09/21 17:08:11 ylo |
---|
75 | * Support AF_UNIX_SIZE. |
---|
76 | * |
---|
77 | * Revision 1.6 1995/09/09 21:26:38 ylo |
---|
78 | * /m/shadows/u2/users/ylo/ssh/README |
---|
79 | * |
---|
80 | * Revision 1.5 1995/08/29 22:18:58 ylo |
---|
81 | * Added remove_all_identities. |
---|
82 | * |
---|
83 | * Revision 1.4 1995/08/21 23:21:04 ylo |
---|
84 | * Deleted ssh_authenticate(). |
---|
85 | * Pass session key and response_type in agent request. |
---|
86 | * |
---|
87 | * Revision 1.3 1995/07/13 01:14:40 ylo |
---|
88 | * Removed the "Last modified" header. |
---|
89 | * |
---|
90 | * Revision 1.2 1995/07/13 01:11:31 ylo |
---|
91 | * Added cvs log. |
---|
92 | * |
---|
93 | * $Endlog$ |
---|
94 | */ |
---|
95 | |
---|
96 | #include "includes.h" |
---|
97 | #include "ssh.h" |
---|
98 | #include "rsa.h" |
---|
99 | #include "authfd.h" |
---|
100 | #include "buffer.h" |
---|
101 | #include "bufaux.h" |
---|
102 | #include "xmalloc.h" |
---|
103 | #include "getput.h" |
---|
104 | |
---|
105 | /* Returns the authentication fd, or -1 if there is none. |
---|
106 | Socket directory privileges are checked to prevent anyone |
---|
107 | from connecting to other users' sockets with suid root ssh. */ |
---|
108 | |
---|
109 | int ssh_get_authentication_fd(void) |
---|
110 | { |
---|
111 | char *authsocket, *authsocketdir, *newauthsockdir, *last_dir; |
---|
112 | char *origauthsocket; |
---|
113 | int sock; |
---|
114 | struct sockaddr_un sunaddr; |
---|
115 | struct stat socket_st, dir_st, dot_st, dotdot_st, parent_st, link_st; |
---|
116 | struct passwd *pw; |
---|
117 | int i; |
---|
118 | |
---|
119 | newauthsockdir = NULL; |
---|
120 | |
---|
121 | origauthsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); |
---|
122 | if (!origauthsocket) |
---|
123 | return -1; |
---|
124 | else |
---|
125 | authsocketdir = xstrdup(origauthsocket); |
---|
126 | |
---|
127 | /* Point to the end of the name */ |
---|
128 | authsocket = authsocketdir + strlen(authsocketdir); |
---|
129 | |
---|
130 | /* Cut the authsocketdir and make authsocket point to |
---|
131 | the bare socket name */ |
---|
132 | while (authsocket != authsocketdir && *authsocket != '/') |
---|
133 | authsocket--; |
---|
134 | |
---|
135 | *authsocket = '\0'; |
---|
136 | authsocket++; |
---|
137 | |
---|
138 | /* Find parent directory */ |
---|
139 | last_dir = strrchr(authsocketdir, '/'); |
---|
140 | if (last_dir == NULL || last_dir == authsocketdir) |
---|
141 | { |
---|
142 | error("Invalid %s `%.100s', it should contain at least one /.", |
---|
143 | SSH_AUTHSOCKET_ENV_NAME, authsocketdir); |
---|
144 | xfree(authsocketdir); |
---|
145 | return -1; |
---|
146 | } |
---|
147 | |
---|
148 | /* Stat parent directory */ |
---|
149 | *last_dir = '\0'; |
---|
150 | if (stat(authsocketdir, &parent_st) != 0) |
---|
151 | { |
---|
152 | error("Parent directory stat failed `%.100s'", authsocketdir); |
---|
153 | xfree(authsocketdir); |
---|
154 | return -1; |
---|
155 | } |
---|
156 | *last_dir = '/'; |
---|
157 | |
---|
158 | pw = getpwuid(original_real_uid); |
---|
159 | |
---|
160 | /* Change to the socket directory so it's privileges can be |
---|
161 | reliably checked */ |
---|
162 | |
---|
163 | /* Stat it */ |
---|
164 | if (lstat(authsocketdir, &dir_st) != 0) |
---|
165 | { |
---|
166 | error("Cannot stat authentication socket directory %.100s", |
---|
167 | authsocketdir); |
---|
168 | xfree(authsocketdir); |
---|
169 | return -1; |
---|
170 | } |
---|
171 | |
---|
172 | chdir(authsocketdir); |
---|
173 | |
---|
174 | if (stat(".", &dot_st) != 0) |
---|
175 | { |
---|
176 | perror("stat . failed"); |
---|
177 | xfree(authsocketdir); |
---|
178 | return -1; |
---|
179 | } |
---|
180 | |
---|
181 | /* Check that stat of real directory name and . matches. */ |
---|
182 | if (dot_st.st_dev != dir_st.st_dev || dot_st.st_ino != dir_st.st_ino) |
---|
183 | { |
---|
184 | error("Wrong directory after chdir"); |
---|
185 | return -1; |
---|
186 | } |
---|
187 | |
---|
188 | if (original_real_uid != UID_ROOT && dot_st.st_uid != pw->pw_uid) |
---|
189 | { |
---|
190 | error("Invalid owner of authentication socket directory %.100s", |
---|
191 | authsocketdir); |
---|
192 | xfree(authsocketdir); |
---|
193 | return -1; |
---|
194 | } |
---|
195 | |
---|
196 | if ((dot_st.st_mode & 077) != 0) |
---|
197 | { |
---|
198 | error("Invalid modes for authentication socket directory %.100s", |
---|
199 | authsocketdir); |
---|
200 | xfree(authsocketdir); |
---|
201 | return -1; |
---|
202 | } |
---|
203 | |
---|
204 | if (lstat(authsocket, &socket_st) != 0) |
---|
205 | { |
---|
206 | error("Cannot find authentication socket %.100s/%.100s", |
---|
207 | authsocketdir, authsocket); |
---|
208 | xfree(authsocketdir); |
---|
209 | return -1; |
---|
210 | } |
---|
211 | if (S_ISLNK(socket_st.st_mode)) |
---|
212 | { |
---|
213 | if (original_real_uid != geteuid()) |
---|
214 | { |
---|
215 | error("Authentication socket `%.100s' is symlink", origauthsocket); |
---|
216 | xfree(authsocketdir); |
---|
217 | return -1; |
---|
218 | } |
---|
219 | } |
---|
220 | |
---|
221 | /* Check if we are suid process */ |
---|
222 | if (original_real_uid != geteuid()) |
---|
223 | { |
---|
224 | /* Something wierd code here again. We need to make sure the socket is |
---|
225 | not symlink to somebody elses socket. We cannot use stat/lstat because |
---|
226 | user might change the inode after we have stat/lstat'ed it. We cannot |
---|
227 | use fstat, because it doesn't work for sockets, so we need some magic |
---|
228 | spell here. |
---|
229 | |
---|
230 | Create temporary directory at same position where the real agent |
---|
231 | directory is, allow only owner to modify it (==root). Change current |
---|
232 | working directory to there and make sure we ended where we wanted |
---|
233 | (stat "." and real path and check that they match, and check that |
---|
234 | parent is what it is supposed to be (stat of .. and real parent |
---|
235 | matches)). Then check that the parent directory ("..") is sticky so |
---|
236 | nobody can mess with this directory. Now we are at safe place where |
---|
237 | nobody else have any permissions. Now make hard link from the real |
---|
238 | authentication socket to this directory. Hard link to symlink will |
---|
239 | point to destination of that symlink, so if the agent socket was |
---|
240 | symlink to somebody elses socket then the stat of our hardlink and |
---|
241 | agent socket given by user differs and we give an error. Otherwise we |
---|
242 | know that the hard link points to real socket that (at least used to |
---|
243 | be) at the directory that was owned by user, so we can safely open the |
---|
244 | hardlink socket (not the original it might be changed after we have |
---|
245 | checked the permissions). */ |
---|
246 | |
---|
247 | newauthsockdir = xmalloc(strlen(authsocketdir) + 20); |
---|
248 | sprintf(newauthsockdir, "%s-%d", authsocketdir, getpid()); |
---|
249 | |
---|
250 | /* Create directory */ |
---|
251 | if (mkdir(newauthsockdir, S_IRWXU) != 0) |
---|
252 | { |
---|
253 | error("Cannot make temporary authentication socket directory %.100s", |
---|
254 | newauthsockdir); |
---|
255 | xfree(authsocketdir); |
---|
256 | xfree(newauthsockdir); |
---|
257 | return -1; |
---|
258 | } |
---|
259 | |
---|
260 | /* Stat it */ |
---|
261 | if (lstat(newauthsockdir, &dir_st) != 0) |
---|
262 | { |
---|
263 | error("Cannot stat newly created temporary authentication socket directory %.100s", |
---|
264 | newauthsockdir); |
---|
265 | xfree(authsocketdir); |
---|
266 | xfree(newauthsockdir); |
---|
267 | return -1; |
---|
268 | } |
---|
269 | |
---|
270 | /* Move to there */ |
---|
271 | chdir(newauthsockdir); |
---|
272 | |
---|
273 | /* Stat . */ |
---|
274 | if (stat(".", &dot_st) != 0) |
---|
275 | { |
---|
276 | error("Cannot stat . in newly created temporary authentication socket directory %.100s", |
---|
277 | newauthsockdir); |
---|
278 | xfree(authsocketdir); |
---|
279 | xfree(newauthsockdir); |
---|
280 | return -1; |
---|
281 | } |
---|
282 | |
---|
283 | /* Check that stat of real directory name and . matches. */ |
---|
284 | if (dot_st.st_dev != dir_st.st_dev || dot_st.st_ino != dir_st.st_ino) |
---|
285 | { |
---|
286 | error("Wrong directory after chdir"); |
---|
287 | return -1; |
---|
288 | } |
---|
289 | |
---|
290 | /* Stat .. (it should match the parent directory and it must be sticky)*/ |
---|
291 | if (stat("..", &dotdot_st) != 0) |
---|
292 | { |
---|
293 | error("Cannot stat .. in newly created temporary authentication socket directory %.100s", |
---|
294 | newauthsockdir); |
---|
295 | xfree(authsocketdir); |
---|
296 | xfree(newauthsockdir); |
---|
297 | return -1; |
---|
298 | } |
---|
299 | if ((dotdot_st.st_mode & 01000) == 0) |
---|
300 | { |
---|
301 | error("Agent parent directory is not sticky, mode is %o it should be 041777", |
---|
302 | dotdot_st.st_mode); |
---|
303 | xfree(authsocketdir); |
---|
304 | xfree(newauthsockdir); |
---|
305 | return -1; |
---|
306 | } |
---|
307 | if (dotdot_st.st_dev != parent_st.st_dev || |
---|
308 | dotdot_st.st_ino != parent_st.st_ino) |
---|
309 | { |
---|
310 | error("Wrong parent directory after chdir to temp directory"); |
---|
311 | xfree(authsocketdir); |
---|
312 | xfree(newauthsockdir); |
---|
313 | return -1; |
---|
314 | } |
---|
315 | |
---|
316 | /* Now we are at safe place, make hardlink to agent socket */ |
---|
317 | if (link(origauthsocket, authsocket) != 0) |
---|
318 | { |
---|
319 | error("Hard link to auth socket failed"); |
---|
320 | xfree(authsocketdir); |
---|
321 | xfree(newauthsockdir); |
---|
322 | return -1; |
---|
323 | } |
---|
324 | |
---|
325 | /* Check that it match the original socket */ |
---|
326 | if (stat(authsocket, &link_st) != 0) |
---|
327 | { |
---|
328 | error("Stat to hard link of authentication socket failed"); |
---|
329 | xfree(authsocketdir); |
---|
330 | xfree(newauthsockdir); |
---|
331 | return -1; |
---|
332 | } |
---|
333 | if (link_st.st_dev != socket_st.st_dev || |
---|
334 | link_st.st_ino != socket_st.st_ino) |
---|
335 | { |
---|
336 | error("Hard link and orignal socket are not same"); |
---|
337 | xfree(authsocketdir); |
---|
338 | xfree(newauthsockdir); |
---|
339 | return -1; |
---|
340 | } |
---|
341 | /* Note! here we are still at the newly created directory, so the connect |
---|
342 | will use the hard link of socket instead of real socket */ |
---|
343 | } |
---|
344 | |
---|
345 | sunaddr.sun_family = AF_UNIX; |
---|
346 | strncpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); |
---|
347 | |
---|
348 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
---|
349 | if (sock < 0) |
---|
350 | { |
---|
351 | error("Socket failed"); |
---|
352 | if (newauthsockdir != NULL) |
---|
353 | { |
---|
354 | unlink(authsocket); |
---|
355 | chdir("/"); |
---|
356 | rmdir(newauthsockdir); |
---|
357 | xfree(newauthsockdir); |
---|
358 | } |
---|
359 | xfree(authsocketdir); |
---|
360 | return -1; |
---|
361 | } |
---|
362 | |
---|
363 | if (connect(sock, (struct sockaddr *)&sunaddr, |
---|
364 | AF_UNIX_SIZE(sunaddr)) < 0) |
---|
365 | { |
---|
366 | close(sock); |
---|
367 | if (newauthsockdir != NULL) |
---|
368 | { |
---|
369 | unlink(authsocket); |
---|
370 | chdir("/"); |
---|
371 | rmdir(newauthsockdir); |
---|
372 | xfree(newauthsockdir); |
---|
373 | } |
---|
374 | xfree(authsocketdir); |
---|
375 | return -1; |
---|
376 | } |
---|
377 | if (newauthsockdir != NULL) |
---|
378 | { |
---|
379 | unlink(authsocket); |
---|
380 | chdir("/"); |
---|
381 | rmdir(newauthsockdir); |
---|
382 | xfree(newauthsockdir); |
---|
383 | } |
---|
384 | xfree(authsocketdir); |
---|
385 | fcntl(sock, F_SETFL, 0); /* Set the socket to blocking mode */ |
---|
386 | return sock; |
---|
387 | } |
---|
388 | |
---|
389 | /* Opens a socket to the authentication server. Returns the number of |
---|
390 | that socket, or -1 if no connection could be made. */ |
---|
391 | |
---|
392 | int ssh_get_authentication_connection_fd(void) |
---|
393 | { |
---|
394 | int authfd; |
---|
395 | |
---|
396 | /* Get the the socket number from the environment. */ |
---|
397 | authfd = ssh_get_authentication_fd(); |
---|
398 | |
---|
399 | if (authfd >= 0) |
---|
400 | debug("Connection to authentication agent opened."); |
---|
401 | else |
---|
402 | debug("No agent."); |
---|
403 | |
---|
404 | return authfd; |
---|
405 | } |
---|
406 | |
---|
407 | /* Opens and connects a private socket for communication with the |
---|
408 | authentication agent. Returns the file descriptor (which must be |
---|
409 | shut down and closed by the caller when no longer needed). |
---|
410 | Returns NULL if an error occurred and the connection could not be |
---|
411 | opened. */ |
---|
412 | |
---|
413 | AuthenticationConnection * |
---|
414 | ssh_get_authentication_connection(void) |
---|
415 | { |
---|
416 | AuthenticationConnection *auth; |
---|
417 | int sock; |
---|
418 | |
---|
419 | /* Get a connection to the authentication agent. */ |
---|
420 | sock = ssh_get_authentication_connection_fd(); |
---|
421 | |
---|
422 | /* Fail if we couldn't obtain a connection. This happens if we exited |
---|
423 | due to a timeout. */ |
---|
424 | if (sock < 0) |
---|
425 | return NULL; |
---|
426 | |
---|
427 | /* Allocate the connection structure and initialize it. */ |
---|
428 | auth = xmalloc(sizeof(*auth)); |
---|
429 | auth->fd = sock; |
---|
430 | buffer_init(&auth->packet); |
---|
431 | buffer_init(&auth->identities); |
---|
432 | auth->num_identities = 0; |
---|
433 | |
---|
434 | return auth; |
---|
435 | } |
---|
436 | |
---|
437 | /* Closes the connection to the authentication agent and frees any associated |
---|
438 | memory. */ |
---|
439 | |
---|
440 | void ssh_close_authentication_connection(AuthenticationConnection *ac) |
---|
441 | { |
---|
442 | /* Close the connection. */ |
---|
443 | shutdown(ac->fd, 2); |
---|
444 | close(ac->fd); |
---|
445 | |
---|
446 | /* Free the buffers. */ |
---|
447 | buffer_free(&ac->packet); |
---|
448 | buffer_free(&ac->identities); |
---|
449 | |
---|
450 | /* Free the connection data structure. */ |
---|
451 | xfree(ac); |
---|
452 | } |
---|
453 | |
---|
454 | /* Returns the first authentication identity held by the agent. |
---|
455 | Returns true if an identity is available, 0 otherwise. |
---|
456 | The caller must initialize the integers before the call, and free the |
---|
457 | comment after a successful call (before calling ssh_get_next_identity). */ |
---|
458 | |
---|
459 | int ssh_get_first_identity(AuthenticationConnection *auth, |
---|
460 | int *bitsp, MP_INT *e, MP_INT *n, char **comment) |
---|
461 | { |
---|
462 | unsigned char msg[8192]; |
---|
463 | int len, l; |
---|
464 | |
---|
465 | /* Send a message to the agent requesting for a list of the identities |
---|
466 | it can represent. */ |
---|
467 | msg[0] = 0; |
---|
468 | msg[1] = 0; |
---|
469 | msg[2] = 0; |
---|
470 | msg[3] = 1; |
---|
471 | msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; |
---|
472 | if (write(auth->fd, msg, 5) != 5) |
---|
473 | { |
---|
474 | error("write auth->fd: %.100s", strerror(errno)); |
---|
475 | return 0; |
---|
476 | } |
---|
477 | |
---|
478 | /* Read the length of the response. XXX implement timeouts here. */ |
---|
479 | len = 4; |
---|
480 | while (len > 0) |
---|
481 | { |
---|
482 | l = read(auth->fd, msg + 4 - len, len); |
---|
483 | if (l <= 0) |
---|
484 | { |
---|
485 | error("read auth->fd: %.100s", strerror(errno)); |
---|
486 | return 0; |
---|
487 | } |
---|
488 | len -= l; |
---|
489 | } |
---|
490 | |
---|
491 | /* Extract the length, and check it for sanity. (We cannot trust |
---|
492 | authentication agents). */ |
---|
493 | len = GET_32BIT(msg); |
---|
494 | if (len < 1 || len > 30*1024) |
---|
495 | fatal("Authentication reply message too long: %d\n", len); |
---|
496 | |
---|
497 | /* Read the packet itself. */ |
---|
498 | buffer_clear(&auth->identities); |
---|
499 | while (len > 0) |
---|
500 | { |
---|
501 | l = len; |
---|
502 | if (l > sizeof(msg)) |
---|
503 | l = sizeof(msg); |
---|
504 | l = read(auth->fd, msg, l); |
---|
505 | if (l <= 0) |
---|
506 | fatal("Incomplete authentication reply."); |
---|
507 | buffer_append(&auth->identities, (char *)msg, l); |
---|
508 | len -= l; |
---|
509 | } |
---|
510 | |
---|
511 | /* Get message type, and verify that we got a proper answer. */ |
---|
512 | buffer_get(&auth->identities, (char *)msg, 1); |
---|
513 | if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) |
---|
514 | fatal("Bad authentication reply message type: %d", msg[0]); |
---|
515 | |
---|
516 | /* Get the number of entries in the response and check it for sanity. */ |
---|
517 | auth->num_identities = buffer_get_int(&auth->identities); |
---|
518 | if (auth->num_identities > 1024) |
---|
519 | fatal("Too many identities in authentication reply: %d\n", |
---|
520 | auth->num_identities); |
---|
521 | |
---|
522 | /* Return the first entry (if any). */ |
---|
523 | return ssh_get_next_identity(auth, bitsp, e, n, comment); |
---|
524 | } |
---|
525 | |
---|
526 | /* Returns the next authentication identity for the agent. Other functions |
---|
527 | can be called between this and ssh_get_first_identity or two calls of this |
---|
528 | function. This returns 0 if there are no more identities. The caller |
---|
529 | must free comment after a successful return. */ |
---|
530 | |
---|
531 | int ssh_get_next_identity(AuthenticationConnection *auth, |
---|
532 | int *bitsp, MP_INT *e, MP_INT *n, char **comment) |
---|
533 | { |
---|
534 | /* Return failure if no more entries. */ |
---|
535 | if (auth->num_identities <= 0) |
---|
536 | return 0; |
---|
537 | |
---|
538 | /* Get the next entry from the packet. These will abort with a fatal |
---|
539 | error if the packet is too short or contains corrupt data. */ |
---|
540 | *bitsp = buffer_get_int(&auth->identities); |
---|
541 | buffer_get_mp_int(&auth->identities, e); |
---|
542 | buffer_get_mp_int(&auth->identities, n); |
---|
543 | *comment = buffer_get_string(&auth->identities, NULL); |
---|
544 | |
---|
545 | /* Decrement the number of remaining entries. */ |
---|
546 | auth->num_identities--; |
---|
547 | |
---|
548 | return 1; |
---|
549 | } |
---|
550 | |
---|
551 | /* Generates a random challenge, sends it to the agent, and waits for response |
---|
552 | from the agent. Returns true (non-zero) if the agent gave the correct |
---|
553 | answer, zero otherwise. Response type selects the style of response |
---|
554 | desired, with 0 corresponding to protocol version 1.0 (no longer supported) |
---|
555 | and 1 corresponding to protocol version 1.1. */ |
---|
556 | |
---|
557 | int ssh_decrypt_challenge(AuthenticationConnection *auth, |
---|
558 | int bits, MP_INT *e, MP_INT *n, MP_INT *challenge, |
---|
559 | unsigned char session_id[16], |
---|
560 | unsigned int response_type, |
---|
561 | unsigned char response[16]) |
---|
562 | { |
---|
563 | Buffer buffer; |
---|
564 | unsigned char buf[8192]; |
---|
565 | int len, l, i; |
---|
566 | |
---|
567 | /* Response type 0 is no longer supported. */ |
---|
568 | if (response_type == 0) |
---|
569 | fatal("Compatibility with ssh protocol version 1.0 no longer supported."); |
---|
570 | |
---|
571 | /* Format a message to the agent. */ |
---|
572 | buf[0] = SSH_AGENTC_RSA_CHALLENGE; |
---|
573 | buffer_init(&buffer); |
---|
574 | buffer_append(&buffer, (char *)buf, 1); |
---|
575 | buffer_put_int(&buffer, bits); |
---|
576 | buffer_put_mp_int(&buffer, e); |
---|
577 | buffer_put_mp_int(&buffer, n); |
---|
578 | buffer_put_mp_int(&buffer, challenge); |
---|
579 | buffer_append(&buffer, (char *)session_id, 16); |
---|
580 | buffer_put_int(&buffer, response_type); |
---|
581 | |
---|
582 | /* Get the length of the message, and format it in the buffer. */ |
---|
583 | len = buffer_len(&buffer); |
---|
584 | PUT_32BIT(buf, len); |
---|
585 | |
---|
586 | /* Send the length and then the packet to the agent. */ |
---|
587 | if (write(auth->fd, buf, 4) != 4 || |
---|
588 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != |
---|
589 | buffer_len(&buffer)) |
---|
590 | { |
---|
591 | error("Error writing to authentication socket."); |
---|
592 | error_cleanup: |
---|
593 | buffer_free(&buffer); |
---|
594 | return 0; |
---|
595 | } |
---|
596 | |
---|
597 | /* Wait for response from the agent. First read the length of the |
---|
598 | response packet. */ |
---|
599 | len = 4; |
---|
600 | while (len > 0) |
---|
601 | { |
---|
602 | l = read(auth->fd, buf + 4 - len, len); |
---|
603 | if (l <= 0) |
---|
604 | { |
---|
605 | error("Error reading response length from authentication socket."); |
---|
606 | goto error_cleanup; |
---|
607 | } |
---|
608 | len -= l; |
---|
609 | } |
---|
610 | |
---|
611 | /* Extract the length, and check it for sanity. */ |
---|
612 | len = GET_32BIT(buf); |
---|
613 | if (len > 30*1024) |
---|
614 | fatal("Authentication response too long: %d", len); |
---|
615 | |
---|
616 | /* Read the rest of the response in to the buffer. */ |
---|
617 | buffer_clear(&buffer); |
---|
618 | while (len > 0) |
---|
619 | { |
---|
620 | l = len; |
---|
621 | if (l > sizeof(buf)) |
---|
622 | l = sizeof(buf); |
---|
623 | l = read(auth->fd, buf, l); |
---|
624 | if (l <= 0) |
---|
625 | { |
---|
626 | error("Error reading response from authentication socket."); |
---|
627 | goto error_cleanup; |
---|
628 | } |
---|
629 | buffer_append(&buffer, (char *)buf, l); |
---|
630 | len -= l; |
---|
631 | } |
---|
632 | |
---|
633 | /* Get the type of the packet. */ |
---|
634 | buffer_get(&buffer, (char *)buf, 1); |
---|
635 | |
---|
636 | /* Check for agent failure message. */ |
---|
637 | if (buf[0] == SSH_AGENT_FAILURE) |
---|
638 | { |
---|
639 | log_msg("Agent admitted failure to authenticate using the key."); |
---|
640 | goto error_cleanup; |
---|
641 | } |
---|
642 | |
---|
643 | /* Now it must be an authentication response packet. */ |
---|
644 | if (buf[0] != SSH_AGENT_RSA_RESPONSE) |
---|
645 | fatal("Bad authentication response: %d", buf[0]); |
---|
646 | |
---|
647 | /* Get the response from the packet. This will abort with a fatal error |
---|
648 | if the packet is corrupt. */ |
---|
649 | for (i = 0; i < 16; i++) |
---|
650 | response[i] = buffer_get_char(&buffer); |
---|
651 | |
---|
652 | /* The buffer containing the packet is no longer needed. */ |
---|
653 | buffer_free(&buffer); |
---|
654 | |
---|
655 | /* Correct answer. */ |
---|
656 | return 1; |
---|
657 | } |
---|
658 | |
---|
659 | /* Adds an identity to the authentication server. This call is not meant to |
---|
660 | be used by normal applications. */ |
---|
661 | |
---|
662 | int ssh_add_identity(AuthenticationConnection *auth, |
---|
663 | RSAPrivateKey *key, const char *comment) |
---|
664 | { |
---|
665 | Buffer buffer; |
---|
666 | unsigned char buf[8192]; |
---|
667 | int len, l, type; |
---|
668 | |
---|
669 | /* Format a message to the agent. */ |
---|
670 | buffer_init(&buffer); |
---|
671 | buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); |
---|
672 | buffer_put_int(&buffer, key->bits); |
---|
673 | buffer_put_mp_int(&buffer, &key->n); |
---|
674 | buffer_put_mp_int(&buffer, &key->e); |
---|
675 | buffer_put_mp_int(&buffer, &key->d); |
---|
676 | buffer_put_mp_int(&buffer, &key->u); |
---|
677 | buffer_put_mp_int(&buffer, &key->p); |
---|
678 | buffer_put_mp_int(&buffer, &key->q); |
---|
679 | buffer_put_string(&buffer, comment, strlen(comment)); |
---|
680 | |
---|
681 | /* Get the length of the message, and format it in the buffer. */ |
---|
682 | len = buffer_len(&buffer); |
---|
683 | PUT_32BIT(buf, len); |
---|
684 | |
---|
685 | /* Send the length and then the packet to the agent. */ |
---|
686 | if (write(auth->fd, buf, 4) != 4 || |
---|
687 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != |
---|
688 | buffer_len(&buffer)) |
---|
689 | { |
---|
690 | error("Error writing to authentication socket."); |
---|
691 | error_cleanup: |
---|
692 | buffer_free(&buffer); |
---|
693 | return 0; |
---|
694 | } |
---|
695 | |
---|
696 | /* Wait for response from the agent. First read the length of the |
---|
697 | response packet. */ |
---|
698 | len = 4; |
---|
699 | while (len > 0) |
---|
700 | { |
---|
701 | l = read(auth->fd, buf + 4 - len, len); |
---|
702 | if (l <= 0) |
---|
703 | { |
---|
704 | error("Error reading response length from authentication socket."); |
---|
705 | goto error_cleanup; |
---|
706 | } |
---|
707 | len -= l; |
---|
708 | } |
---|
709 | |
---|
710 | /* Extract the length, and check it for sanity. */ |
---|
711 | len = GET_32BIT(buf); |
---|
712 | if (len > 30*1024) |
---|
713 | fatal("Add identity response too long: %d", len); |
---|
714 | |
---|
715 | /* Read the rest of the response in tothe buffer. */ |
---|
716 | buffer_clear(&buffer); |
---|
717 | while (len > 0) |
---|
718 | { |
---|
719 | l = len; |
---|
720 | if (l > sizeof(buf)) |
---|
721 | l = sizeof(buf); |
---|
722 | l = read(auth->fd, buf, l); |
---|
723 | if (l <= 0) |
---|
724 | { |
---|
725 | error("Error reading response from authentication socket."); |
---|
726 | goto error_cleanup; |
---|
727 | } |
---|
728 | buffer_append(&buffer, (char *)buf, l); |
---|
729 | len -= l; |
---|
730 | } |
---|
731 | |
---|
732 | /* Get the type of the packet. */ |
---|
733 | type = buffer_get_char(&buffer); |
---|
734 | switch (type) |
---|
735 | { |
---|
736 | case SSH_AGENT_FAILURE: |
---|
737 | buffer_free(&buffer); |
---|
738 | return 0; |
---|
739 | case SSH_AGENT_SUCCESS: |
---|
740 | buffer_free(&buffer); |
---|
741 | return 1; |
---|
742 | default: |
---|
743 | fatal("Bad response to add identity from authentication agent: %d", |
---|
744 | type); |
---|
745 | } |
---|
746 | /*NOTREACHED*/ |
---|
747 | return 0; |
---|
748 | } |
---|
749 | |
---|
750 | /* Removes an identity from the authentication server. This call is not meant |
---|
751 | to be used by normal applications. */ |
---|
752 | |
---|
753 | int ssh_remove_identity(AuthenticationConnection *auth, RSAPublicKey *key) |
---|
754 | { |
---|
755 | Buffer buffer; |
---|
756 | unsigned char buf[8192]; |
---|
757 | int len, l, type; |
---|
758 | |
---|
759 | /* Format a message to the agent. */ |
---|
760 | buffer_init(&buffer); |
---|
761 | buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); |
---|
762 | buffer_put_int(&buffer, key->bits); |
---|
763 | buffer_put_mp_int(&buffer, &key->e); |
---|
764 | buffer_put_mp_int(&buffer, &key->n); |
---|
765 | |
---|
766 | /* Get the length of the message, and format it in the buffer. */ |
---|
767 | len = buffer_len(&buffer); |
---|
768 | PUT_32BIT(buf, len); |
---|
769 | |
---|
770 | /* Send the length and then the packet to the agent. */ |
---|
771 | if (write(auth->fd, buf, 4) != 4 || |
---|
772 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != |
---|
773 | buffer_len(&buffer)) |
---|
774 | { |
---|
775 | error("Error writing to authentication socket."); |
---|
776 | error_cleanup: |
---|
777 | buffer_free(&buffer); |
---|
778 | return 0; |
---|
779 | } |
---|
780 | |
---|
781 | /* Wait for response from the agent. First read the length of the |
---|
782 | response packet. */ |
---|
783 | len = 4; |
---|
784 | while (len > 0) |
---|
785 | { |
---|
786 | l = read(auth->fd, buf + 4 - len, len); |
---|
787 | if (l <= 0) |
---|
788 | { |
---|
789 | error("Error reading response length from authentication socket."); |
---|
790 | goto error_cleanup; |
---|
791 | } |
---|
792 | len -= l; |
---|
793 | } |
---|
794 | |
---|
795 | /* Extract the length, and check it for sanity. */ |
---|
796 | len = GET_32BIT(buf); |
---|
797 | if (len > 30*1024) |
---|
798 | fatal("Remove identity response too long: %d", len); |
---|
799 | |
---|
800 | /* Read the rest of the response in tothe buffer. */ |
---|
801 | buffer_clear(&buffer); |
---|
802 | while (len > 0) |
---|
803 | { |
---|
804 | l = len; |
---|
805 | if (l > sizeof(buf)) |
---|
806 | l = sizeof(buf); |
---|
807 | l = read(auth->fd, buf, l); |
---|
808 | if (l <= 0) |
---|
809 | { |
---|
810 | error("Error reading response from authentication socket."); |
---|
811 | goto error_cleanup; |
---|
812 | } |
---|
813 | buffer_append(&buffer, (char *)buf, l); |
---|
814 | len -= l; |
---|
815 | } |
---|
816 | |
---|
817 | /* Get the type of the packet. */ |
---|
818 | type = buffer_get_char(&buffer); |
---|
819 | switch (type) |
---|
820 | { |
---|
821 | case SSH_AGENT_FAILURE: |
---|
822 | buffer_free(&buffer); |
---|
823 | return 0; |
---|
824 | case SSH_AGENT_SUCCESS: |
---|
825 | buffer_free(&buffer); |
---|
826 | return 1; |
---|
827 | default: |
---|
828 | fatal("Bad response to remove identity from authentication agent: %d", |
---|
829 | type); |
---|
830 | } |
---|
831 | /*NOTREACHED*/ |
---|
832 | return 0; |
---|
833 | } |
---|
834 | |
---|
835 | /* Removes all identities from the agent. This call is not meant |
---|
836 | to be used by normal applications. */ |
---|
837 | |
---|
838 | int ssh_remove_all_identities(AuthenticationConnection *auth) |
---|
839 | { |
---|
840 | Buffer buffer; |
---|
841 | unsigned char buf[8192]; |
---|
842 | int len, l, type; |
---|
843 | |
---|
844 | /* Get the length of the message, and format it in the buffer. */ |
---|
845 | PUT_32BIT(buf, 1); |
---|
846 | buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; |
---|
847 | |
---|
848 | /* Send the length and then the packet to the agent. */ |
---|
849 | if (write(auth->fd, buf, 5) != 5) |
---|
850 | { |
---|
851 | error("Error writing to authentication socket."); |
---|
852 | return 0; |
---|
853 | } |
---|
854 | |
---|
855 | /* Wait for response from the agent. First read the length of the |
---|
856 | response packet. */ |
---|
857 | len = 4; |
---|
858 | while (len > 0) |
---|
859 | { |
---|
860 | l = read(auth->fd, buf + 4 - len, len); |
---|
861 | if (l <= 0) |
---|
862 | { |
---|
863 | error("Error reading response length from authentication socket."); |
---|
864 | return 0; |
---|
865 | } |
---|
866 | len -= l; |
---|
867 | } |
---|
868 | |
---|
869 | /* Extract the length, and check it for sanity. */ |
---|
870 | len = GET_32BIT(buf); |
---|
871 | if (len > 30*1024) |
---|
872 | fatal("Remove identity response too long: %d", len); |
---|
873 | |
---|
874 | /* Read the rest of the response into the buffer. */ |
---|
875 | buffer_init(&buffer); |
---|
876 | while (len > 0) |
---|
877 | { |
---|
878 | l = len; |
---|
879 | if (l > sizeof(buf)) |
---|
880 | l = sizeof(buf); |
---|
881 | l = read(auth->fd, buf, l); |
---|
882 | if (l <= 0) |
---|
883 | { |
---|
884 | error("Error reading response from authentication socket."); |
---|
885 | buffer_free(&buffer); |
---|
886 | return 0; |
---|
887 | } |
---|
888 | buffer_append(&buffer, (char *)buf, l); |
---|
889 | len -= l; |
---|
890 | } |
---|
891 | |
---|
892 | /* Get the type of the packet. */ |
---|
893 | type = buffer_get_char(&buffer); |
---|
894 | switch (type) |
---|
895 | { |
---|
896 | case SSH_AGENT_FAILURE: |
---|
897 | buffer_free(&buffer); |
---|
898 | return 0; |
---|
899 | case SSH_AGENT_SUCCESS: |
---|
900 | buffer_free(&buffer); |
---|
901 | return 1; |
---|
902 | default: |
---|
903 | fatal("Bad response to remove identity from authentication agent: %d", |
---|
904 | type); |
---|
905 | } |
---|
906 | /*NOTREACHED*/ |
---|
907 | return 0; |
---|
908 | } |
---|