We now build libssh2 in Xcode and it's a much better UB/10.4 citizen
[printdrop.git] / Vendor / libssh2 / Source / publickey.c
1 /* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms,
5 * with or without modification, are permitted provided
6 * that the following conditions are met:
7 *
8 * Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the
10 * following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 *
17 * Neither the name of the copyright holder nor the names
18 * of any other contributors may be used to endorse or
19 * promote products derived from this software without
20 * specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
34 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
35 * OF SUCH DAMAGE.
36 */
37
38 #include "libssh2_priv.h"
39 #include "libssh2_publickey.h"
40
41 #define LIBSSH2_PUBLICKEY_VERSION 2
42
43 /* Numericised response codes -- Not IETF standard, just a local representation */
44 #define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0
45 #define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1
46 #define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2
47
48 typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST
49 {
50 int code;
51 const char *name;
52 int name_len;
53 } LIBSSH2_PUBLICKEY_CODE_LIST;
54
55 static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_response_codes[] = {
56 {LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1}
57 ,
58 {LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1}
59 ,
60 {LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey", sizeof("publickey") - 1}
61 ,
62 {0, NULL, 0}
63 };
64
65 /* PUBLICKEY status codes -- IETF defined */
66 #define LIBSSH2_PUBLICKEY_SUCCESS 0
67 #define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1
68 #define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2
69 #define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3
70 #define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4
71 #define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5
72 #define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6
73 #define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7
74 #define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8
75
76 #define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8
77
78 static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_status_codes[] = {
79 {LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1}
80 ,
81 {LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied",
82 sizeof("access denied") - 1}
83 ,
84 {LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded",
85 sizeof("storage exceeded") - 1}
86 ,
87 {LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported",
88 sizeof("version not supported") - 1}
89 ,
90 {LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found",
91 sizeof("key not found") - 1}
92 ,
93 {LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported",
94 sizeof("key not supported") - 1}
95 ,
96 {LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present",
97 sizeof("key already present") - 1}
98 ,
99 {LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure",
100 sizeof("general failure") - 1}
101 ,
102 {LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported",
103 sizeof("request not supported") - 1}
104 ,
105 {0, NULL, 0}
106 };
107
108 /* {{{ libssh2_publickey_status_error
109 * Format an error message from a status code
110 */
111 #define LIBSSH2_PUBLICKEY_STATUS_TEXT_START "Publickey Subsystem Error: \""
112 #define LIBSSH2_PUBLICKEY_STATUS_TEXT_MID "\" Server Resports: \""
113 #define LIBSSH2_PUBLICKEY_STATUS_TEXT_END "\""
114 static void
115 libssh2_publickey_status_error(const LIBSSH2_PUBLICKEY * pkey,
116 LIBSSH2_SESSION * session, int status,
117 const unsigned char *message, int message_len)
118 {
119 const char *status_text;
120 int status_text_len;
121 char *m, *s;
122 int m_len;
123
124 /* GENERAL_FAILURE got remapped between version 1 and 2 */
125 if (status == 6 && pkey && pkey->version == 1) {
126 status = 7;
127 }
128
129 if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) {
130 status_text = "unknown";
131 status_text_len = sizeof("unknown") - 1;
132 } else {
133 status_text = libssh2_publickey_status_codes[status].name;
134 status_text_len = libssh2_publickey_status_codes[status].name_len;
135 }
136
137 m_len =
138 (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1) + status_text_len +
139 (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1) + message_len +
140 (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1);
141 m = LIBSSH2_ALLOC(session, m_len + 1);
142 if (!m) {
143 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
144 "Unable to allocate memory for status message", 0);
145 return;
146 }
147 s = m;
148 memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_START,
149 sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1);
150 s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1;
151 memcpy(s, status_text, status_text_len);
152 s += status_text_len;
153 memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_MID,
154 sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1);
155 s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1;
156 memcpy(s, message, message_len);
157 s += message_len;
158 memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_END,
159 sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1);
160 s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END);
161 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, m, 1);
162 }
163
164 /* }}} */
165
166 /* {{{ libssh2_publickey_packet_receive
167 * Read a packet from the subsystem
168 */
169 static int
170 libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY * pkey,
171 unsigned char **data, unsigned long *data_len)
172 {
173 LIBSSH2_CHANNEL *channel = pkey->channel;
174 LIBSSH2_SESSION *session = channel->session;
175 unsigned char buffer[4];
176 int rc;
177
178 if (pkey->receive_state == libssh2_NB_state_idle) {
179 rc = libssh2_channel_read_ex(channel, 0, (char *) buffer, 4);
180 if (rc == PACKET_EAGAIN) {
181 return PACKET_EAGAIN;
182 } else if (rc != 4) {
183 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
184 "Invalid response from publickey subsystem", 0);
185 return -1;
186 }
187
188 pkey->receive_packet_len = libssh2_ntohu32(buffer);
189 pkey->receive_packet =
190 LIBSSH2_ALLOC(session, pkey->receive_packet_len);
191 if (!pkey->receive_packet) {
192 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
193 "Unable to allocate publickey response buffer", 0);
194 return -1;
195 }
196
197 pkey->receive_state = libssh2_NB_state_sent;
198 }
199
200 if (pkey->receive_state == libssh2_NB_state_sent) {
201 rc = libssh2_channel_read_ex(channel, 0, (char *) pkey->receive_packet,
202 pkey->receive_packet_len);
203 if (rc == PACKET_EAGAIN) {
204 return PACKET_EAGAIN;
205 } else if (rc != (int)pkey->receive_packet_len) {
206 libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
207 "Timeout waiting for publickey subsystem response packet",
208 0);
209 LIBSSH2_FREE(session, pkey->receive_packet);
210 pkey->receive_packet = NULL;
211 pkey->receive_state = libssh2_NB_state_idle;
212 return -1;
213 }
214
215 *data = pkey->receive_packet;
216 *data_len = pkey->receive_packet_len;
217 }
218
219 pkey->receive_state = libssh2_NB_state_idle;
220
221 return 0;
222 }
223
224 /* }}} */
225
226 /* {{{ libssh2_publickey_response_id
227 * Translate a string response name to a numeric code
228 * Data will be incremented by 4 + response_len on success only
229 */
230 static int
231 libssh2_publickey_response_id(unsigned char **pdata, int data_len)
232 {
233 unsigned long response_len;
234 unsigned char *data = *pdata;
235 const LIBSSH2_PUBLICKEY_CODE_LIST *codes =
236 libssh2_publickey_response_codes;
237
238 if (data_len < 4) {
239 /* Malformed response */
240 return -1;
241 }
242 response_len = libssh2_ntohu32(data);
243 data += 4;
244 data_len -= 4;
245 if (data_len < (int)response_len) {
246 /* Malformed response */
247 return -1;
248 }
249
250 while (codes->name) {
251 if ((unsigned long)codes->name_len == response_len &&
252 strncmp(codes->name, (char *) data, response_len) == 0) {
253 *pdata = data + response_len;
254 return codes->code;
255 }
256 codes++;
257 }
258
259 return -1;
260 }
261
262 /* }}} */
263
264 /* {{{ libssh2_publickey_response_success
265 * Generic helper routine to wait for success response and nothing else
266 */
267 static int
268 libssh2_publickey_response_success(LIBSSH2_PUBLICKEY * pkey)
269 {
270 LIBSSH2_SESSION *session = pkey->channel->session;
271 unsigned char *data, *s;
272 unsigned long data_len;
273 int response;
274 int rc;
275
276 while (1) {
277 rc = libssh2_publickey_packet_receive(pkey, &data, &data_len);
278 if (rc == PACKET_EAGAIN) {
279 return PACKET_EAGAIN;
280 } else if (rc) {
281 libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
282 "Timeout waiting for response from publickey subsystem",
283 0);
284 return -1;
285 }
286
287 s = data;
288 if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) {
289 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
290 "Invalid publickey subsystem response code", 0);
291 LIBSSH2_FREE(session, data);
292 return -1;
293 }
294
295 switch (response) {
296 case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
297 /* Error, or processing complete */
298 {
299 unsigned long status, descr_len, lang_len;
300 unsigned char *descr, *lang;
301
302 status = libssh2_ntohu32(s);
303 s += 4;
304 descr_len = libssh2_ntohu32(s);
305 s += 4;
306 descr = s;
307 s += descr_len;
308 lang_len = libssh2_ntohu32(s);
309 s += 4;
310 lang = s;
311 s += lang_len;
312
313 if (s > data + data_len) {
314 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
315 "Malformed publickey subsystem packet", 0);
316 LIBSSH2_FREE(session, data);
317 return -1;
318 }
319
320 if (status == LIBSSH2_PUBLICKEY_SUCCESS) {
321 LIBSSH2_FREE(session, data);
322 return 0;
323 }
324
325 libssh2_publickey_status_error(pkey, session, status, descr,
326 descr_len);
327 LIBSSH2_FREE(session, data);
328 return -1;
329 }
330 default:
331 /* Unknown/Unexpected */
332 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
333 "Unexpected publickey subsystem response, ignoring",
334 0);
335 LIBSSH2_FREE(session, data);
336 data = NULL;
337 }
338 }
339 /* never reached, but include `return` to silence compiler warnings */
340 return -1;
341 }
342
343 /* }}} */
344
345
346 /* *****************
347 * Publickey API *
348 ***************** */
349
350 /* {{{ libssh2_publickey_init
351 * Startup the publickey subsystem
352 */
353 LIBSSH2_API LIBSSH2_PUBLICKEY *
354 libssh2_publickey_init(LIBSSH2_SESSION * session)
355 {
356 /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */
357 unsigned char buffer[19];
358 unsigned char *s;
359 int response;
360 int rc;
361
362 if (session->pkeyInit_state == libssh2_NB_state_idle) {
363 session->pkeyInit_data = NULL;
364 session->pkeyInit_pkey = NULL;
365 session->pkeyInit_channel = NULL;
366
367 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
368 "Initializing publickey subsystem");
369
370 session->pkeyInit_state = libssh2_NB_state_allocated;
371 }
372
373 if (session->pkeyInit_state == libssh2_NB_state_allocated) {
374 do {
375 session->pkeyInit_channel =
376 libssh2_channel_open_ex(session, "session",
377 sizeof("session") - 1,
378 LIBSSH2_CHANNEL_WINDOW_DEFAULT,
379 LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
380 0);
381 if (!session->pkeyInit_channel
382 && (libssh2_session_last_errno(session) ==
383 LIBSSH2_ERROR_EAGAIN)) {
384 /* The error state is already set, so leave it */
385 libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
386 "Would block to startup channel", 0);
387 return NULL;
388 } else if (!session->pkeyInit_channel
389 && (libssh2_session_last_errno(session) !=
390 LIBSSH2_ERROR_EAGAIN)) {
391 libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
392 "Unable to startup channel", 0);
393 goto err_exit;
394 }
395 } while (!session->pkeyInit_channel);
396
397 session->pkeyInit_state = libssh2_NB_state_sent;
398 }
399
400 if (session->pkeyInit_state == libssh2_NB_state_sent) {
401 rc = libssh2_channel_process_startup(session->pkeyInit_channel,
402 "subsystem",
403 sizeof("subsystem") - 1,
404 "publickey", strlen("publickey"));
405 if (rc == PACKET_EAGAIN) {
406 libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
407 "Would block starting publickkey subsystem", 0);
408 return NULL;
409 } else if (rc) {
410 libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
411 "Unable to request publickey subsystem", 0);
412 goto err_exit;
413 }
414
415 session->pkeyInit_state = libssh2_NB_state_sent1;
416 }
417
418 if (session->pkeyInit_state == libssh2_NB_state_sent1) {
419 rc = libssh2_channel_handle_extended_data2(session->pkeyInit_channel,
420 LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
421 if (rc == PACKET_EAGAIN) {
422 libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
423 "Would block starting publickkey subsystem", 0);
424 return NULL;
425 }
426
427 session->pkeyInit_pkey =
428 LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY));
429 if (!session->pkeyInit_pkey) {
430 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
431 "Unable to allocate a new publickey structure", 0);
432 goto err_exit;
433 }
434 memset(session->pkeyInit_pkey, 0, sizeof(LIBSSH2_PUBLICKEY));
435 session->pkeyInit_pkey->channel = session->pkeyInit_channel;
436 session->pkeyInit_pkey->version = 0;
437
438 s = buffer;
439 libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4);
440 s += 4;
441 libssh2_htonu32(s, sizeof("version") - 1);
442 s += 4;
443 memcpy(s, "version", sizeof("version") - 1);
444 s += sizeof("version") - 1;
445 libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION);
446 s += 4;
447
448 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
449 "Sending publickey version packet advertising version %d support",
450 (int) LIBSSH2_PUBLICKEY_VERSION);
451
452 session->pkeyInit_state = libssh2_NB_state_sent2;
453 }
454
455 if (session->pkeyInit_state == libssh2_NB_state_sent2) {
456 rc = libssh2_channel_write_ex(session->pkeyInit_channel, 0,
457 (char *) buffer, (s - buffer));
458 if (rc == PACKET_EAGAIN) {
459 libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
460 "Would block sending publickkey version packet", 0);
461 return NULL;
462 } else if ((s - buffer) != rc) {
463 libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
464 "Unable to send publickey version packet", 0);
465 goto err_exit;
466 }
467
468 session->pkeyInit_state = libssh2_NB_state_sent3;
469 }
470
471 if (session->pkeyInit_state == libssh2_NB_state_sent3) {
472 while (1) {
473 rc = libssh2_publickey_packet_receive(session->pkeyInit_pkey,
474 &session->pkeyInit_data,
475 &session->pkeyInit_data_len);
476 if (rc == PACKET_EAGAIN) {
477 libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
478 "Would block waiting for response from publickey subsystem",
479 0);
480 return NULL;
481 } else if (rc) {
482 libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
483 "Timeout waiting for response from publickey subsystem",
484 0);
485 goto err_exit;
486 }
487
488 s = session->pkeyInit_data;
489 if ((response =
490 libssh2_publickey_response_id(&s,
491 session->pkeyInit_data_len)) <
492 0) {
493 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
494 "Invalid publickey subsystem response code", 0);
495 goto err_exit;
496 }
497
498 switch (response) {
499 case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
500 /* Error */
501 {
502 unsigned long status, descr_len, lang_len;
503 unsigned char *descr, *lang;
504
505 status = libssh2_ntohu32(s);
506 s += 4;
507 descr_len = libssh2_ntohu32(s);
508 s += 4;
509 descr = s;
510 s += descr_len;
511 lang_len = libssh2_ntohu32(s);
512 s += 4;
513 lang = s;
514 s += lang_len;
515
516 if (s >
517 session->pkeyInit_data + session->pkeyInit_data_len) {
518 libssh2_error(session,
519 LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
520 "Malformed publickey subsystem packet",
521 0);
522 goto err_exit;
523 }
524
525 libssh2_publickey_status_error(NULL, session, status,
526 descr, descr_len);
527 goto err_exit;
528 }
529
530 case LIBSSH2_PUBLICKEY_RESPONSE_VERSION:
531 /* What we want */
532 session->pkeyInit_pkey->version = libssh2_ntohu32(s);
533 if (session->pkeyInit_pkey->version >
534 LIBSSH2_PUBLICKEY_VERSION) {
535 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
536 "Truncating remote publickey version from %lu",
537 session->pkeyInit_pkey->version);
538 session->pkeyInit_pkey->version =
539 LIBSSH2_PUBLICKEY_VERSION;
540 }
541 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
542 "Enabling publickey subsystem version %lu",
543 session->pkeyInit_pkey->version);
544 LIBSSH2_FREE(session, session->pkeyInit_data);
545 session->pkeyInit_data = NULL;
546 session->pkeyInit_state = libssh2_NB_state_idle;
547 return session->pkeyInit_pkey;
548
549 default:
550 /* Unknown/Unexpected */
551 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
552 "Unexpected publickey subsystem response, ignoring",
553 0);
554 LIBSSH2_FREE(session, session->pkeyInit_data);
555 session->pkeyInit_data = NULL;
556 }
557 }
558 }
559
560 /* Never reached except by direct goto */
561 err_exit:
562 session->pkeyInit_state = libssh2_NB_state_sent4;
563 if (session->pkeyInit_channel) {
564 rc = libssh2_channel_close(session->pkeyInit_channel);
565 if (rc == PACKET_EAGAIN) {
566 libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
567 "Would block closing channel", 0);
568 return NULL;
569 }
570 }
571 if (session->pkeyInit_pkey) {
572 LIBSSH2_FREE(session, session->pkeyInit_pkey);
573 session->pkeyInit_pkey = NULL;
574 }
575 if (session->pkeyInit_data) {
576 LIBSSH2_FREE(session, session->pkeyInit_data);
577 session->pkeyInit_data = NULL;
578 }
579 session->pkeyInit_state = libssh2_NB_state_idle;
580 return NULL;
581 }
582
583 /* }}} */
584
585 /* {{{ libssh2_publickey_add_ex
586 * Add a new public key entry
587 */
588 LIBSSH2_API int
589 libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY * pkey, const unsigned char *name,
590 unsigned long name_len, const unsigned char *blob,
591 unsigned long blob_len, char overwrite,
592 unsigned long num_attrs,
593 const libssh2_publickey_attribute attrs[])
594 {
595 LIBSSH2_CHANNEL *channel = pkey->channel;
596 LIBSSH2_SESSION *session = channel->session;
597 /* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name} blob_len(4) + {blob} */
598 unsigned long i, packet_len = 19 + name_len + blob_len;
599 unsigned char *comment = NULL;
600 unsigned long comment_len = 0;
601 int rc;
602
603 if (pkey->add_state == libssh2_NB_state_idle) {
604 pkey->add_packet = NULL;
605
606 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s publickey",
607 name);
608
609 if (pkey->version == 1) {
610 for(i = 0; i < num_attrs; i++) {
611 /* Search for a comment attribute */
612 if (attrs[i].name_len == (sizeof("comment") - 1) &&
613 strncmp(attrs[i].name, "comment",
614 sizeof("comment") - 1) == 0) {
615 comment = (unsigned char *) attrs[i].value;
616 comment_len = attrs[i].value_len;
617 break;
618 }
619 }
620 packet_len += 4 + comment_len;
621 } else {
622 packet_len += 5; /* overwrite(1) + attribute_count(4) */
623 for(i = 0; i < num_attrs; i++) {
624 packet_len += 9 + attrs[i].name_len + attrs[i].value_len;
625 /* name_len(4) + value_len(4) + mandatory(1) */
626 }
627 }
628
629 pkey->add_packet = LIBSSH2_ALLOC(session, packet_len);
630 if (!pkey->add_packet) {
631 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
632 "Unable to allocate memory for publickey \"add\" packet",
633 0);
634 return -1;
635 }
636
637 pkey->add_s = pkey->add_packet;
638 libssh2_htonu32(pkey->add_s, packet_len - 4);
639 pkey->add_s += 4;
640 libssh2_htonu32(pkey->add_s, sizeof("add") - 1);
641 pkey->add_s += 4;
642 memcpy(pkey->add_s, "add", sizeof("add") - 1);
643 pkey->add_s += sizeof("add") - 1;
644 if (pkey->version == 1) {
645 libssh2_htonu32(pkey->add_s, comment_len);
646 pkey->add_s += 4;
647 if (comment) {
648 memcpy(pkey->add_s, comment, comment_len);
649 pkey->add_s += comment_len;
650 }
651
652 libssh2_htonu32(pkey->add_s, name_len);
653 pkey->add_s += 4;
654 memcpy(pkey->add_s, name, name_len);
655 pkey->add_s += name_len;
656 libssh2_htonu32(pkey->add_s, blob_len);
657 pkey->add_s += 4;
658 memcpy(pkey->add_s, blob, blob_len);
659 pkey->add_s += blob_len;
660 } else {
661 /* Version == 2 */
662
663 libssh2_htonu32(pkey->add_s, name_len);
664 pkey->add_s += 4;
665 memcpy(pkey->add_s, name, name_len);
666 pkey->add_s += name_len;
667 libssh2_htonu32(pkey->add_s, blob_len);
668 pkey->add_s += 4;
669 memcpy(pkey->add_s, blob, blob_len);
670 pkey->add_s += blob_len;
671 *(pkey->add_s++) = overwrite ? 0x01 : 0;
672 libssh2_htonu32(pkey->add_s, num_attrs);
673 pkey->add_s += 4;
674 for(i = 0; i < num_attrs; i++) {
675 libssh2_htonu32(pkey->add_s, attrs[i].name_len);
676 pkey->add_s += 4;
677 memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len);
678 pkey->add_s += attrs[i].name_len;
679 libssh2_htonu32(pkey->add_s, attrs[i].value_len);
680 pkey->add_s += 4;
681 memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len);
682 pkey->add_s += attrs[i].value_len;
683 *(pkey->add_s++) = attrs[i].mandatory ? 0x01 : 0;
684 }
685 }
686
687 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
688 "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld",
689 name, blob_len, num_attrs);
690
691 pkey->add_state = libssh2_NB_state_created;
692 }
693
694 if (pkey->add_state == libssh2_NB_state_created) {
695 rc = libssh2_channel_write_ex(channel, 0, (char *) pkey->add_packet,
696 (pkey->add_s - pkey->add_packet));
697 if (rc == PACKET_EAGAIN) {
698 return PACKET_EAGAIN;
699 } else if ((pkey->add_s - pkey->add_packet) != rc) {
700 libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
701 "Unable to send publickey add packet", 0);
702 LIBSSH2_FREE(session, pkey->add_packet);
703 pkey->add_packet = NULL;
704 return -1;
705 }
706 LIBSSH2_FREE(session, pkey->add_packet);
707 pkey->add_packet = NULL;
708
709 pkey->add_state = libssh2_NB_state_sent;
710 }
711
712 rc = libssh2_publickey_response_success(pkey);
713 if (rc == PACKET_EAGAIN) {
714 return PACKET_EAGAIN;
715 }
716
717 pkey->add_state = libssh2_NB_state_idle;
718
719 return rc;
720 }
721
722 /* }}} */
723
724 /* {{{ libssh2_publickey_remove_ex
725 * Remove an existing publickey so that authentication can no longer be performed using it
726 */
727 LIBSSH2_API int
728 libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY * pkey,
729 const unsigned char *name, unsigned long name_len,
730 const unsigned char *blob, unsigned long blob_len)
731 {
732 LIBSSH2_CHANNEL *channel = pkey->channel;
733 LIBSSH2_SESSION *session = channel->session;
734 /* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name} + blob_len(4) + {blob} */
735 unsigned long packet_len = 22 + name_len + blob_len;
736 int rc;
737
738 if (pkey->remove_state == libssh2_NB_state_idle) {
739 pkey->remove_packet = NULL;
740
741 pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len);
742 if (!pkey->remove_packet) {
743 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
744 "Unable to allocate memory for publickey \"remove\" packet",
745 0);
746 return -1;
747 }
748
749 pkey->remove_s = pkey->remove_packet;
750 libssh2_htonu32(pkey->remove_s, packet_len - 4);
751 pkey->remove_s += 4;
752 libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1);
753 pkey->remove_s += 4;
754 memcpy(pkey->remove_s, "remove", sizeof("remove") - 1);
755 pkey->remove_s += sizeof("remove") - 1;
756 libssh2_htonu32(pkey->remove_s, name_len);
757 pkey->remove_s += 4;
758 memcpy(pkey->remove_s, name, name_len);
759 pkey->remove_s += name_len;
760 libssh2_htonu32(pkey->remove_s, blob_len);
761 pkey->remove_s += 4;
762 memcpy(pkey->remove_s, blob, blob_len);
763 pkey->remove_s += blob_len;
764
765 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
766 "Sending publickey \"remove\" packet: type=%s blob_len=%ld",
767 name, blob_len);
768
769 pkey->remove_state = libssh2_NB_state_created;
770 }
771
772 if (pkey->remove_state == libssh2_NB_state_created) {
773 rc = libssh2_channel_write_ex(channel, 0, (char *) pkey->remove_packet,
774 (pkey->remove_s - pkey->remove_packet));
775 if (rc == PACKET_EAGAIN) {
776 return PACKET_EAGAIN;
777 } else if ((pkey->remove_s - pkey->remove_packet) != rc) {
778 libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
779 "Unable to send publickey remove packet", 0);
780 LIBSSH2_FREE(session, pkey->remove_packet);
781 pkey->remove_packet = NULL;
782 pkey->remove_state = libssh2_NB_state_idle;
783 return -1;
784 }
785 LIBSSH2_FREE(session, pkey->remove_packet);
786 pkey->remove_packet = NULL;
787
788 pkey->remove_state = libssh2_NB_state_sent;
789 }
790
791 rc = libssh2_publickey_response_success(pkey);
792 if (rc == PACKET_EAGAIN) {
793 return PACKET_EAGAIN;
794 }
795
796 pkey->remove_state = libssh2_NB_state_idle;
797
798 return rc;
799 }
800
801 /* }}} */
802
803 /* {{{ libssh2_publickey_list_fetch
804 * Fetch a list of supported public key from a server
805 */
806 LIBSSH2_API int
807 libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY * pkey, unsigned long *num_keys,
808 libssh2_publickey_list ** pkey_list)
809 {
810 LIBSSH2_CHANNEL *channel = pkey->channel;
811 LIBSSH2_SESSION *session = channel->session;
812 libssh2_publickey_list *list = NULL;
813 unsigned long buffer_len = 12, keys = 0, max_keys = 0, i;
814 /* 12 = packet_len(4) + list_len(4) + "list"(4) */
815 int response;
816 int rc;
817
818 if (pkey->listFetch_state == libssh2_NB_state_idle) {
819 pkey->listFetch_data = NULL;
820
821 pkey->listFetch_s = pkey->listFetch_buffer;
822 libssh2_htonu32(pkey->listFetch_s, buffer_len - 4);
823 pkey->listFetch_s += 4;
824 libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1);
825 pkey->listFetch_s += 4;
826 memcpy(pkey->listFetch_s, "list", sizeof("list") - 1);
827 pkey->listFetch_s += sizeof("list") - 1;
828
829 _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY,
830 "Sending publickey \"list\" packet");
831
832 pkey->listFetch_state = libssh2_NB_state_created;
833 }
834
835 if (pkey->listFetch_state == libssh2_NB_state_created) {
836 rc = libssh2_channel_write_ex(channel, 0,
837 (char *) pkey->listFetch_buffer,
838 (pkey->listFetch_s -
839 pkey->listFetch_buffer));
840 if (rc == PACKET_EAGAIN) {
841 return PACKET_EAGAIN;
842 } else if ((pkey->listFetch_s - pkey->listFetch_buffer) != rc) {
843 libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
844 "Unable to send publickey list packet", 0);
845 pkey->listFetch_state = libssh2_NB_state_idle;
846 return -1;
847 }
848
849 pkey->listFetch_state = libssh2_NB_state_sent;
850 }
851
852 while (1) {
853 rc = libssh2_publickey_packet_receive(pkey, &pkey->listFetch_data,
854 &pkey->listFetch_data_len);
855 if (rc == PACKET_EAGAIN) {
856 return PACKET_EAGAIN;
857 } else if (rc) {
858 libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
859 "Timeout waiting for response from publickey subsystem",
860 0);
861 goto err_exit;
862 }
863
864 pkey->listFetch_s = pkey->listFetch_data;
865 if ((response =
866 libssh2_publickey_response_id(&pkey->listFetch_s,
867 pkey->listFetch_data_len)) < 0) {
868 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
869 "Invalid publickey subsystem response code", 0);
870 goto err_exit;
871 }
872
873 switch (response) {
874 case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
875 /* Error, or processing complete */
876 {
877 unsigned long status, descr_len, lang_len;
878 unsigned char *descr, *lang;
879
880 status = libssh2_ntohu32(pkey->listFetch_s);
881 pkey->listFetch_s += 4;
882 descr_len = libssh2_ntohu32(pkey->listFetch_s);
883 pkey->listFetch_s += 4;
884 descr = pkey->listFetch_s;
885 pkey->listFetch_s += descr_len;
886 lang_len = libssh2_ntohu32(pkey->listFetch_s);
887 pkey->listFetch_s += 4;
888 lang = pkey->listFetch_s;
889 pkey->listFetch_s += lang_len;
890
891 if (pkey->listFetch_s >
892 pkey->listFetch_data + pkey->listFetch_data_len) {
893 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
894 "Malformed publickey subsystem packet", 0);
895 goto err_exit;
896 }
897
898 if (status == LIBSSH2_PUBLICKEY_SUCCESS) {
899 LIBSSH2_FREE(session, pkey->listFetch_data);
900 pkey->listFetch_data = NULL;
901 *pkey_list = list;
902 *num_keys = keys;
903 pkey->listFetch_state = libssh2_NB_state_idle;
904 return 0;
905 }
906
907 libssh2_publickey_status_error(pkey, session, status, descr,
908 descr_len);
909 goto err_exit;
910 }
911 case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY:
912 /* What we want */
913 if (keys >= max_keys) {
914 libssh2_publickey_list *newlist;
915 /* Grow the key list if necessary */
916 max_keys += 8;
917 newlist =
918 LIBSSH2_REALLOC(session, list,
919 (max_keys +
920 1) * sizeof(libssh2_publickey_list));
921 if (!newlist) {
922 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
923 "Unable to allocate memory for publickey list",
924 0);
925 goto err_exit;
926 }
927 list = newlist;
928 }
929 if (pkey->version == 1) {
930 unsigned long comment_len;
931
932 comment_len = libssh2_ntohu32(pkey->listFetch_s);
933 pkey->listFetch_s += 4;
934 if (comment_len) {
935 list[keys].num_attrs = 1;
936 list[keys].attrs =
937 LIBSSH2_ALLOC(session,
938 sizeof(libssh2_publickey_attribute));
939 if (!list[keys].attrs) {
940 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
941 "Unable to allocate memory for publickey attributes",
942 0);
943 goto err_exit;
944 }
945 list[keys].attrs[0].name = "comment";
946 list[keys].attrs[0].name_len = sizeof("comment") - 1;
947 list[keys].attrs[0].value = (char *) pkey->listFetch_s;
948 list[keys].attrs[0].value_len = comment_len;
949 list[keys].attrs[0].mandatory = 0;
950
951 pkey->listFetch_s += comment_len;
952 } else {
953 list[keys].num_attrs = 0;
954 list[keys].attrs = NULL;
955 }
956 list[keys].name_len = libssh2_ntohu32(pkey->listFetch_s);
957 pkey->listFetch_s += 4;
958 list[keys].name = pkey->listFetch_s;
959 pkey->listFetch_s += list[keys].name_len;
960 list[keys].blob_len = libssh2_ntohu32(pkey->listFetch_s);
961 pkey->listFetch_s += 4;
962 list[keys].blob = pkey->listFetch_s;
963 pkey->listFetch_s += list[keys].blob_len;
964 } else {
965 /* Version == 2 */
966 list[keys].name_len = libssh2_ntohu32(pkey->listFetch_s);
967 pkey->listFetch_s += 4;
968 list[keys].name = pkey->listFetch_s;
969 pkey->listFetch_s += list[keys].name_len;
970 list[keys].blob_len = libssh2_ntohu32(pkey->listFetch_s);
971 pkey->listFetch_s += 4;
972 list[keys].blob = pkey->listFetch_s;
973 pkey->listFetch_s += list[keys].blob_len;
974 list[keys].num_attrs = libssh2_ntohu32(pkey->listFetch_s);
975 pkey->listFetch_s += 4;
976 if (list[keys].num_attrs) {
977 list[keys].attrs =
978 LIBSSH2_ALLOC(session,
979 list[keys].num_attrs *
980 sizeof(libssh2_publickey_attribute));
981 if (!list[keys].attrs) {
982 libssh2_error(session, LIBSSH2_ERROR_ALLOC,
983 "Unable to allocate memory for publickey attributes",
984 0);
985 goto err_exit;
986 }
987 for(i = 0; i < list[keys].num_attrs; i++) {
988 list[keys].attrs[i].name_len =
989 libssh2_ntohu32(pkey->listFetch_s);
990 pkey->listFetch_s += 4;
991 list[keys].attrs[i].name = (char *) pkey->listFetch_s;
992 pkey->listFetch_s += list[keys].attrs[i].name_len;
993 list[keys].attrs[i].value_len =
994 libssh2_ntohu32(pkey->listFetch_s);
995 pkey->listFetch_s += 4;
996 list[keys].attrs[i].value = (char *) pkey->listFetch_s;
997 pkey->listFetch_s += list[keys].attrs[i].value_len;
998 list[keys].attrs[i].mandatory = 0; /* actually an ignored value */
999 }
1000 } else {
1001 list[keys].attrs = NULL;
1002 }
1003 }
1004 list[keys].packet = pkey->listFetch_data; /* To be FREEd in libssh2_publickey_list_free() */
1005 keys++;
1006
1007 list[keys].packet = NULL; /* Terminate the list */
1008 pkey->listFetch_data = NULL;
1009 break;
1010 default:
1011 /* Unknown/Unexpected */
1012 libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
1013 "Unexpected publickey subsystem response, ignoring",
1014 0);
1015 LIBSSH2_FREE(session, pkey->listFetch_data);
1016 pkey->listFetch_data = NULL;
1017 }
1018 }
1019
1020 /* Only reached via explicit goto */
1021 err_exit:
1022 if (pkey->listFetch_data) {
1023 LIBSSH2_FREE(session, pkey->listFetch_data);
1024 pkey->listFetch_data = NULL;
1025 }
1026 if (list) {
1027 libssh2_publickey_list_free(pkey, list);
1028 }
1029 pkey->listFetch_state = libssh2_NB_state_idle;
1030 return -1;
1031 }
1032
1033 /* }}} */
1034
1035 /* {{{ libssh2_publickey_list_free
1036 * Free a previously fetched list of public keys
1037 */
1038 LIBSSH2_API void
1039 libssh2_publickey_list_free(LIBSSH2_PUBLICKEY * pkey,
1040 libssh2_publickey_list * pkey_list)
1041 {
1042 LIBSSH2_SESSION *session = pkey->channel->session;
1043 libssh2_publickey_list *p = pkey_list;
1044
1045 while (p->packet) {
1046 if (p->attrs) {
1047 LIBSSH2_FREE(session, p->attrs);
1048 }
1049 LIBSSH2_FREE(session, p->packet);
1050 p++;
1051 }
1052
1053 LIBSSH2_FREE(session, pkey_list);
1054 }
1055
1056 /* }}} */
1057
1058 /* {{{ libssh2_publickey_shutdown
1059 * Shutdown the publickey subsystem
1060 */
1061 LIBSSH2_API int
1062 libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY * pkey)
1063 {
1064 LIBSSH2_SESSION *session = pkey->channel->session;
1065
1066 /*
1067 * Make sure all memory used in the state variables are free
1068 */
1069 if (pkey->receive_packet) {
1070 LIBSSH2_FREE(session, pkey->receive_packet);
1071 pkey->receive_packet = NULL;
1072 }
1073 if (pkey->add_packet) {
1074 LIBSSH2_FREE(session, pkey->add_packet);
1075 pkey->add_packet = NULL;
1076 }
1077 if (pkey->remove_packet) {
1078 LIBSSH2_FREE(session, pkey->remove_packet);
1079 pkey->remove_packet = NULL;
1080 }
1081 if (pkey->listFetch_data) {
1082 LIBSSH2_FREE(session, pkey->listFetch_data);
1083 pkey->listFetch_data = NULL;
1084 }
1085
1086 if (libssh2_channel_free(pkey->channel) == PACKET_EAGAIN) {
1087 return PACKET_EAGAIN;
1088 }
1089
1090 LIBSSH2_FREE(session, pkey);
1091 return 0;
1092 }
1093
1094 /* }}} */