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