1 /* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
4 * Redistribution and use in source and binary forms,
5 * with or without modification, are permitted provided
6 * that the following conditions are met:
8 * Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the
10 * following disclaimer.
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.
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.
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
38 #include "libssh2_priv.h"
39 #include "libssh2_publickey.h"
41 #define LIBSSH2_PUBLICKEY_VERSION 2
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
48 typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST
53 } LIBSSH2_PUBLICKEY_CODE_LIST
;
55 static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_response_codes
[] = {
56 {LIBSSH2_PUBLICKEY_RESPONSE_STATUS
, "status", sizeof("status") - 1}
58 {LIBSSH2_PUBLICKEY_RESPONSE_VERSION
, "version", sizeof("version") - 1}
60 {LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY
, "publickey", sizeof("publickey") - 1}
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
76 #define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8
78 static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_status_codes
[] = {
79 {LIBSSH2_PUBLICKEY_SUCCESS
, "success", sizeof("success") - 1}
81 {LIBSSH2_PUBLICKEY_ACCESS_DENIED
, "access denied",
82 sizeof("access denied") - 1}
84 {LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED
, "storage exceeded",
85 sizeof("storage exceeded") - 1}
87 {LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED
, "version not supported",
88 sizeof("version not supported") - 1}
90 {LIBSSH2_PUBLICKEY_KEY_NOT_FOUND
, "key not found",
91 sizeof("key not found") - 1}
93 {LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED
, "key not supported",
94 sizeof("key not supported") - 1}
96 {LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT
, "key already present",
97 sizeof("key already present") - 1}
99 {LIBSSH2_PUBLICKEY_GENERAL_FAILURE
, "general failure",
100 sizeof("general failure") - 1}
102 {LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED
, "request not supported",
103 sizeof("request not supported") - 1}
108 /* {{{ libssh2_publickey_status_error
109 * Format an error message from a status code
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 "\""
115 libssh2_publickey_status_error(const LIBSSH2_PUBLICKEY
* pkey
,
116 LIBSSH2_SESSION
* session
, int status
,
117 const unsigned char *message
, int message_len
)
119 const char *status_text
;
124 /* GENERAL_FAILURE got remapped between version 1 and 2 */
125 if (status
== 6 && pkey
&& pkey
->version
== 1) {
129 if (status
< 0 || status
> LIBSSH2_PUBLICKEY_STATUS_CODE_MAX
) {
130 status_text
= "unknown";
131 status_text_len
= sizeof("unknown") - 1;
133 status_text
= libssh2_publickey_status_codes
[status
].name
;
134 status_text_len
= libssh2_publickey_status_codes
[status
].name_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);
143 libssh2_error(session
, LIBSSH2_ERROR_ALLOC
,
144 "Unable to allocate memory for status message", 0);
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
);
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);
166 /* {{{ libssh2_publickey_packet_receive
167 * Read a packet from the subsystem
170 libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY
* pkey
,
171 unsigned char **data
, unsigned long *data_len
)
173 LIBSSH2_CHANNEL
*channel
= pkey
->channel
;
174 LIBSSH2_SESSION
*session
= channel
->session
;
175 unsigned char buffer
[4];
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);
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);
197 pkey
->receive_state
= libssh2_NB_state_sent
;
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",
209 LIBSSH2_FREE(session
, pkey
->receive_packet
);
210 pkey
->receive_packet
= NULL
;
211 pkey
->receive_state
= libssh2_NB_state_idle
;
215 *data
= pkey
->receive_packet
;
216 *data_len
= pkey
->receive_packet_len
;
219 pkey
->receive_state
= libssh2_NB_state_idle
;
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
231 libssh2_publickey_response_id(unsigned char **pdata
, int data_len
)
233 unsigned long response_len
;
234 unsigned char *data
= *pdata
;
235 const LIBSSH2_PUBLICKEY_CODE_LIST
*codes
=
236 libssh2_publickey_response_codes
;
239 /* Malformed response */
242 response_len
= libssh2_ntohu32(data
);
245 if (data_len
< (int)response_len
) {
246 /* Malformed response */
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
;
264 /* {{{ libssh2_publickey_response_success
265 * Generic helper routine to wait for success response and nothing else
268 libssh2_publickey_response_success(LIBSSH2_PUBLICKEY
* pkey
)
270 LIBSSH2_SESSION
*session
= pkey
->channel
->session
;
271 unsigned char *data
, *s
;
272 unsigned long data_len
;
277 rc
= libssh2_publickey_packet_receive(pkey
, &data
, &data_len
);
278 if (rc
== PACKET_EAGAIN
) {
279 return PACKET_EAGAIN
;
281 libssh2_error(session
, LIBSSH2_ERROR_SOCKET_TIMEOUT
,
282 "Timeout waiting for response from publickey subsystem",
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
);
296 case LIBSSH2_PUBLICKEY_RESPONSE_STATUS
:
297 /* Error, or processing complete */
299 unsigned long status
, descr_len
, lang_len
;
300 unsigned char *descr
, *lang
;
302 status
= libssh2_ntohu32(s
);
304 descr_len
= libssh2_ntohu32(s
);
308 lang_len
= libssh2_ntohu32(s
);
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
);
320 if (status
== LIBSSH2_PUBLICKEY_SUCCESS
) {
321 LIBSSH2_FREE(session
, data
);
325 libssh2_publickey_status_error(pkey
, session
, status
, descr
,
327 LIBSSH2_FREE(session
, data
);
331 /* Unknown/Unexpected */
332 libssh2_error(session
, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL
,
333 "Unexpected publickey subsystem response, ignoring",
335 LIBSSH2_FREE(session
, data
);
339 /* never reached, but include `return` to silence compiler warnings */
350 /* {{{ libssh2_publickey_init
351 * Startup the publickey subsystem
353 LIBSSH2_API LIBSSH2_PUBLICKEY
*
354 libssh2_publickey_init(LIBSSH2_SESSION
* session
)
356 /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */
357 unsigned char buffer
[19];
362 if (session
->pkeyInit_state
== libssh2_NB_state_idle
) {
363 session
->pkeyInit_data
= NULL
;
364 session
->pkeyInit_pkey
= NULL
;
365 session
->pkeyInit_channel
= NULL
;
367 _libssh2_debug(session
, LIBSSH2_DBG_PUBLICKEY
,
368 "Initializing publickey subsystem");
370 session
->pkeyInit_state
= libssh2_NB_state_allocated
;
373 if (session
->pkeyInit_state
== libssh2_NB_state_allocated
) {
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
,
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);
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);
395 } while (!session
->pkeyInit_channel
);
397 session
->pkeyInit_state
= libssh2_NB_state_sent
;
400 if (session
->pkeyInit_state
== libssh2_NB_state_sent
) {
401 rc
= libssh2_channel_process_startup(session
->pkeyInit_channel
,
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);
410 libssh2_error(session
, LIBSSH2_ERROR_CHANNEL_FAILURE
,
411 "Unable to request publickey subsystem", 0);
415 session
->pkeyInit_state
= libssh2_NB_state_sent1
;
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);
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);
434 memset(session
->pkeyInit_pkey
, 0, sizeof(LIBSSH2_PUBLICKEY
));
435 session
->pkeyInit_pkey
->channel
= session
->pkeyInit_channel
;
436 session
->pkeyInit_pkey
->version
= 0;
439 libssh2_htonu32(s
, 4 + (sizeof("version") - 1) + 4);
441 libssh2_htonu32(s
, sizeof("version") - 1);
443 memcpy(s
, "version", sizeof("version") - 1);
444 s
+= sizeof("version") - 1;
445 libssh2_htonu32(s
, LIBSSH2_PUBLICKEY_VERSION
);
448 _libssh2_debug(session
, LIBSSH2_DBG_PUBLICKEY
,
449 "Sending publickey version packet advertising version %d support",
450 (int) LIBSSH2_PUBLICKEY_VERSION
);
452 session
->pkeyInit_state
= libssh2_NB_state_sent2
;
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);
462 } else if ((s
- buffer
) != rc
) {
463 libssh2_error(session
, LIBSSH2_ERROR_SOCKET_SEND
,
464 "Unable to send publickey version packet", 0);
468 session
->pkeyInit_state
= libssh2_NB_state_sent3
;
471 if (session
->pkeyInit_state
== libssh2_NB_state_sent3
) {
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",
482 libssh2_error(session
, LIBSSH2_ERROR_SOCKET_TIMEOUT
,
483 "Timeout waiting for response from publickey subsystem",
488 s
= session
->pkeyInit_data
;
490 libssh2_publickey_response_id(&s
,
491 session
->pkeyInit_data_len
)) <
493 libssh2_error(session
, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL
,
494 "Invalid publickey subsystem response code", 0);
499 case LIBSSH2_PUBLICKEY_RESPONSE_STATUS
:
502 unsigned long status
, descr_len
, lang_len
;
503 unsigned char *descr
, *lang
;
505 status
= libssh2_ntohu32(s
);
507 descr_len
= libssh2_ntohu32(s
);
511 lang_len
= libssh2_ntohu32(s
);
517 session
->pkeyInit_data
+ session
->pkeyInit_data_len
) {
518 libssh2_error(session
,
519 LIBSSH2_ERROR_PUBLICKEY_PROTOCOL
,
520 "Malformed publickey subsystem packet",
525 libssh2_publickey_status_error(NULL
, session
, status
,
530 case LIBSSH2_PUBLICKEY_RESPONSE_VERSION
:
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
;
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
;
550 /* Unknown/Unexpected */
551 libssh2_error(session
, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL
,
552 "Unexpected publickey subsystem response, ignoring",
554 LIBSSH2_FREE(session
, session
->pkeyInit_data
);
555 session
->pkeyInit_data
= NULL
;
560 /* Never reached except by direct goto */
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);
571 if (session
->pkeyInit_pkey
) {
572 LIBSSH2_FREE(session
, session
->pkeyInit_pkey
);
573 session
->pkeyInit_pkey
= NULL
;
575 if (session
->pkeyInit_data
) {
576 LIBSSH2_FREE(session
, session
->pkeyInit_data
);
577 session
->pkeyInit_data
= NULL
;
579 session
->pkeyInit_state
= libssh2_NB_state_idle
;
585 /* {{{ libssh2_publickey_add_ex
586 * Add a new public key entry
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
[])
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;
603 if (pkey
->add_state
== libssh2_NB_state_idle
) {
604 pkey
->add_packet
= NULL
;
606 _libssh2_debug(session
, LIBSSH2_DBG_PUBLICKEY
, "Adding %s publickey",
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
;
620 packet_len
+= 4 + comment_len
;
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) */
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",
637 pkey
->add_s
= pkey
->add_packet
;
638 libssh2_htonu32(pkey
->add_s
, packet_len
- 4);
640 libssh2_htonu32(pkey
->add_s
, sizeof("add") - 1);
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
);
648 memcpy(pkey
->add_s
, comment
, comment_len
);
649 pkey
->add_s
+= comment_len
;
652 libssh2_htonu32(pkey
->add_s
, name_len
);
654 memcpy(pkey
->add_s
, name
, name_len
);
655 pkey
->add_s
+= name_len
;
656 libssh2_htonu32(pkey
->add_s
, blob_len
);
658 memcpy(pkey
->add_s
, blob
, blob_len
);
659 pkey
->add_s
+= blob_len
;
663 libssh2_htonu32(pkey
->add_s
, name_len
);
665 memcpy(pkey
->add_s
, name
, name_len
);
666 pkey
->add_s
+= name_len
;
667 libssh2_htonu32(pkey
->add_s
, blob_len
);
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
);
674 for(i
= 0; i
< num_attrs
; i
++) {
675 libssh2_htonu32(pkey
->add_s
, attrs
[i
].name_len
);
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
);
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;
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
);
691 pkey
->add_state
= libssh2_NB_state_created
;
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
;
706 LIBSSH2_FREE(session
, pkey
->add_packet
);
707 pkey
->add_packet
= NULL
;
709 pkey
->add_state
= libssh2_NB_state_sent
;
712 rc
= libssh2_publickey_response_success(pkey
);
713 if (rc
== PACKET_EAGAIN
) {
714 return PACKET_EAGAIN
;
717 pkey
->add_state
= libssh2_NB_state_idle
;
724 /* {{{ libssh2_publickey_remove_ex
725 * Remove an existing publickey so that authentication can no longer be performed using it
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
)
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
;
738 if (pkey
->remove_state
== libssh2_NB_state_idle
) {
739 pkey
->remove_packet
= NULL
;
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",
749 pkey
->remove_s
= pkey
->remove_packet
;
750 libssh2_htonu32(pkey
->remove_s
, packet_len
- 4);
752 libssh2_htonu32(pkey
->remove_s
, sizeof("remove") - 1);
754 memcpy(pkey
->remove_s
, "remove", sizeof("remove") - 1);
755 pkey
->remove_s
+= sizeof("remove") - 1;
756 libssh2_htonu32(pkey
->remove_s
, name_len
);
758 memcpy(pkey
->remove_s
, name
, name_len
);
759 pkey
->remove_s
+= name_len
;
760 libssh2_htonu32(pkey
->remove_s
, blob_len
);
762 memcpy(pkey
->remove_s
, blob
, blob_len
);
763 pkey
->remove_s
+= blob_len
;
765 _libssh2_debug(session
, LIBSSH2_DBG_PUBLICKEY
,
766 "Sending publickey \"remove\" packet: type=%s blob_len=%ld",
769 pkey
->remove_state
= libssh2_NB_state_created
;
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
;
785 LIBSSH2_FREE(session
, pkey
->remove_packet
);
786 pkey
->remove_packet
= NULL
;
788 pkey
->remove_state
= libssh2_NB_state_sent
;
791 rc
= libssh2_publickey_response_success(pkey
);
792 if (rc
== PACKET_EAGAIN
) {
793 return PACKET_EAGAIN
;
796 pkey
->remove_state
= libssh2_NB_state_idle
;
803 /* {{{ libssh2_publickey_list_fetch
804 * Fetch a list of supported public key from a server
807 libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY
* pkey
, unsigned long *num_keys
,
808 libssh2_publickey_list
** pkey_list
)
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) */
818 if (pkey
->listFetch_state
== libssh2_NB_state_idle
) {
819 pkey
->listFetch_data
= NULL
;
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;
829 _libssh2_debug(session
, LIBSSH2_DBG_PUBLICKEY
,
830 "Sending publickey \"list\" packet");
832 pkey
->listFetch_state
= libssh2_NB_state_created
;
835 if (pkey
->listFetch_state
== libssh2_NB_state_created
) {
836 rc
= libssh2_channel_write_ex(channel
, 0,
837 (char *) pkey
->listFetch_buffer
,
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
;
849 pkey
->listFetch_state
= libssh2_NB_state_sent
;
853 rc
= libssh2_publickey_packet_receive(pkey
, &pkey
->listFetch_data
,
854 &pkey
->listFetch_data_len
);
855 if (rc
== PACKET_EAGAIN
) {
856 return PACKET_EAGAIN
;
858 libssh2_error(session
, LIBSSH2_ERROR_SOCKET_TIMEOUT
,
859 "Timeout waiting for response from publickey subsystem",
864 pkey
->listFetch_s
= pkey
->listFetch_data
;
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);
874 case LIBSSH2_PUBLICKEY_RESPONSE_STATUS
:
875 /* Error, or processing complete */
877 unsigned long status
, descr_len
, lang_len
;
878 unsigned char *descr
, *lang
;
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
;
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);
898 if (status
== LIBSSH2_PUBLICKEY_SUCCESS
) {
899 LIBSSH2_FREE(session
, pkey
->listFetch_data
);
900 pkey
->listFetch_data
= NULL
;
903 pkey
->listFetch_state
= libssh2_NB_state_idle
;
907 libssh2_publickey_status_error(pkey
, session
, status
, descr
,
911 case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY
:
913 if (keys
>= max_keys
) {
914 libssh2_publickey_list
*newlist
;
915 /* Grow the key list if necessary */
918 LIBSSH2_REALLOC(session
, list
,
920 1) * sizeof(libssh2_publickey_list
));
922 libssh2_error(session
, LIBSSH2_ERROR_ALLOC
,
923 "Unable to allocate memory for publickey list",
929 if (pkey
->version
== 1) {
930 unsigned long comment_len
;
932 comment_len
= libssh2_ntohu32(pkey
->listFetch_s
);
933 pkey
->listFetch_s
+= 4;
935 list
[keys
].num_attrs
= 1;
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",
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;
951 pkey
->listFetch_s
+= comment_len
;
953 list
[keys
].num_attrs
= 0;
954 list
[keys
].attrs
= NULL
;
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
;
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
) {
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",
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 */
1001 list
[keys
].attrs
= NULL
;
1004 list
[keys
].packet
= pkey
->listFetch_data
; /* To be FREEd in libssh2_publickey_list_free() */
1007 list
[keys
].packet
= NULL
; /* Terminate the list */
1008 pkey
->listFetch_data
= NULL
;
1011 /* Unknown/Unexpected */
1012 libssh2_error(session
, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL
,
1013 "Unexpected publickey subsystem response, ignoring",
1015 LIBSSH2_FREE(session
, pkey
->listFetch_data
);
1016 pkey
->listFetch_data
= NULL
;
1020 /* Only reached via explicit goto */
1022 if (pkey
->listFetch_data
) {
1023 LIBSSH2_FREE(session
, pkey
->listFetch_data
);
1024 pkey
->listFetch_data
= NULL
;
1027 libssh2_publickey_list_free(pkey
, list
);
1029 pkey
->listFetch_state
= libssh2_NB_state_idle
;
1035 /* {{{ libssh2_publickey_list_free
1036 * Free a previously fetched list of public keys
1039 libssh2_publickey_list_free(LIBSSH2_PUBLICKEY
* pkey
,
1040 libssh2_publickey_list
* pkey_list
)
1042 LIBSSH2_SESSION
*session
= pkey
->channel
->session
;
1043 libssh2_publickey_list
*p
= pkey_list
;
1047 LIBSSH2_FREE(session
, p
->attrs
);
1049 LIBSSH2_FREE(session
, p
->packet
);
1053 LIBSSH2_FREE(session
, pkey_list
);
1058 /* {{{ libssh2_publickey_shutdown
1059 * Shutdown the publickey subsystem
1062 libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY
* pkey
)
1064 LIBSSH2_SESSION
*session
= pkey
->channel
->session
;
1067 * Make sure all memory used in the state variables are free
1069 if (pkey
->receive_packet
) {
1070 LIBSSH2_FREE(session
, pkey
->receive_packet
);
1071 pkey
->receive_packet
= NULL
;
1073 if (pkey
->add_packet
) {
1074 LIBSSH2_FREE(session
, pkey
->add_packet
);
1075 pkey
->add_packet
= NULL
;
1077 if (pkey
->remove_packet
) {
1078 LIBSSH2_FREE(session
, pkey
->remove_packet
);
1079 pkey
->remove_packet
= NULL
;
1081 if (pkey
->listFetch_data
) {
1082 LIBSSH2_FREE(session
, pkey
->listFetch_data
);
1083 pkey
->listFetch_data
= NULL
;
1086 if (libssh2_channel_free(pkey
->channel
) == PACKET_EAGAIN
) {
1087 return PACKET_EAGAIN
;
1090 LIBSSH2_FREE(session
, pkey
);