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