1 /*
2  * misc.c - useful client functions
3  *
4  * This file is part of the SSH Library
5  *
6  * Copyright (c) 2003-2009 by Aris Adamantiadis
7  * Copyright (c) 2008-2009 by Andreas Schneider <asn@cryptomilk.org>
8  *
9  * The SSH Library is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or (at your
12  * option) any later version.
13  *
14  * The SSH Library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with the SSH Library; see the file COPYING.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22  * MA 02111-1307, USA.
23  */
24 
25 #include "config.h"
26 
27 #ifndef _WIN32
28 /* This is needed for a standard getpwuid_r on opensolaris */
29 #define _POSIX_PTHREAD_SEMANTICS
30 #include <pwd.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 
36 #endif /* _WIN32 */
37 
38 #include <errno.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <ctype.h>
46 #include <time.h>
47 #ifdef HAVE_SYS_TIME_H
48 #include <sys/time.h>
49 #endif /* HAVE_SYS_TIME_H */
50 
51 
52 #ifdef _WIN32
53 
54 #ifndef _WIN32_IE
55 # define _WIN32_IE 0x0501 // SHGetSpecialFolderPath
56 #endif
57 
58 #include <winsock2.h> // Must be the first to include
59 #include <ws2tcpip.h>
60 #include <shlobj.h>
61 #include <direct.h>
62 
63 #ifdef HAVE_IO_H
64 #include <io.h>
65 #endif /* HAVE_IO_H */
66 
67 #endif /* _WIN32 */
68 
69 #include "libssh/priv.h"
70 #include "libssh/misc.h"
71 #include "libssh/session.h"
72 
73 #ifdef HAVE_LIBGCRYPT
74 #define GCRYPT_STRING "/gnutls"
75 #else
76 #define GCRYPT_STRING ""
77 #endif
78 
79 #ifdef HAVE_LIBCRYPTO
80 #define CRYPTO_STRING "/openssl"
81 #else
82 #define CRYPTO_STRING ""
83 #endif
84 
85 #ifdef HAVE_LIBMBEDCRYPTO
86 #define MBED_STRING "/mbedtls"
87 #else
88 #define MBED_STRING ""
89 #endif
90 
91 #ifdef WITH_ZLIB
92 #define ZLIB_STRING "/zlib"
93 #else
94 #define ZLIB_STRING ""
95 #endif
96 
97 /**
98  * @defgroup libssh_misc The SSH helper functions.
99  * @ingroup libssh
100  *
101  * Different helper functions used in the SSH Library.
102  *
103  * @{
104  */
105 
106 #ifdef _WIN32
ssh_get_user_home_dir(void)107 char *ssh_get_user_home_dir(void) {
108   char tmp[MAX_PATH] = {0};
109   char *szPath = NULL;
110 
111   if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
112     szPath = malloc(strlen(tmp) + 1);
113     if (szPath == NULL) {
114       return NULL;
115     }
116 
117     strcpy(szPath, tmp);
118     return szPath;
119   }
120 
121   return NULL;
122 }
123 
124 /* we have read access on file */
ssh_file_readaccess_ok(const char * file)125 int ssh_file_readaccess_ok(const char *file) {
126   if (_access(file, 4) < 0) {
127     return 0;
128   }
129 
130   return 1;
131 }
132 
133 /**
134  * @brief Check if the given path is an existing directory and that is
135  * accessible for writing.
136  *
137  * @param[in] path Path to the directory to be checked
138  *
139  * @return Return 1 if the directory exists and is accessible; 0 otherwise
140  * */
ssh_dir_writeable(const char * path)141 int ssh_dir_writeable(const char *path)
142 {
143     struct _stat buffer;
144     int rc;
145 
146     rc = _stat(path, &buffer);
147     if (rc < 0) {
148         return 0;
149     }
150 
151     if ((buffer.st_mode & _S_IFDIR) && (buffer.st_mode & _S_IWRITE)) {
152         return 1;
153     }
154 
155     return 0;
156 }
157 
158 #define SSH_USEC_IN_SEC         1000000LL
159 #define SSH_SECONDS_SINCE_1601  11644473600LL
160 
gettimeofday(struct timeval * __p,void * __t)161 int gettimeofday(struct timeval *__p, void *__t) {
162   union {
163     unsigned long long ns100; /* time since 1 Jan 1601 in 100ns units */
164     FILETIME ft;
165   } now;
166 
167   GetSystemTimeAsFileTime (&now.ft);
168   __p->tv_usec = (long) ((now.ns100 / 10LL) % SSH_USEC_IN_SEC);
169   __p->tv_sec  = (long)(((now.ns100 / 10LL ) / SSH_USEC_IN_SEC) - SSH_SECONDS_SINCE_1601);
170 
171   return (0);
172 }
173 
ssh_get_local_username(void)174 char *ssh_get_local_username(void) {
175     DWORD size = 0;
176     char *user;
177 
178     /* get the size */
179     GetUserName(NULL, &size);
180 
181     user = (char *) malloc(size);
182     if (user == NULL) {
183         return NULL;
184     }
185 
186     if (GetUserName(user, &size)) {
187         return user;
188     }
189 
190     return NULL;
191 }
192 
ssh_is_ipaddr_v4(const char * str)193 int ssh_is_ipaddr_v4(const char *str) {
194     struct sockaddr_storage ss;
195     int sslen = sizeof(ss);
196     int rc = SOCKET_ERROR;
197 
198     /* WSAStringToAddressA thinks that 0.0.0 is a valid IP */
199     if (strlen(str) < 7) {
200         return 0;
201     }
202 
203     rc = WSAStringToAddressA((LPSTR) str,
204                              AF_INET,
205                              NULL,
206                              (struct sockaddr*)&ss,
207                              &sslen);
208     if (rc == 0) {
209         return 1;
210     }
211 
212     return 0;
213 }
214 
ssh_is_ipaddr(const char * str)215 int ssh_is_ipaddr(const char *str) {
216     int rc = SOCKET_ERROR;
217 
218     if (strchr(str, ':')) {
219         struct sockaddr_storage ss;
220         int sslen = sizeof(ss);
221 
222         /* TODO link-local (IP:v6:addr%ifname). */
223         rc = WSAStringToAddressA((LPSTR) str,
224                                  AF_INET6,
225                                  NULL,
226                                  (struct sockaddr*)&ss,
227                                  &sslen);
228         if (rc == 0) {
229             return 1;
230         }
231     }
232 
233     return ssh_is_ipaddr_v4(str);
234 }
235 #else /* _WIN32 */
236 
237 #ifndef NSS_BUFLEN_PASSWD
238 #define NSS_BUFLEN_PASSWD 4096
239 #endif /* NSS_BUFLEN_PASSWD */
240 
ssh_get_user_home_dir(void)241 char *ssh_get_user_home_dir(void)
242 {
243     char *szPath = NULL;
244     struct passwd pwd;
245     struct passwd *pwdbuf = NULL;
246     char buf[NSS_BUFLEN_PASSWD] = {0};
247     int rc;
248 
249     rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
250     if (rc != 0 || pwdbuf == NULL ) {
251         szPath = getenv("HOME");
252         if (szPath == NULL) {
253             return NULL;
254         }
255         snprintf(buf, sizeof(buf), "%s", szPath);
256 
257         return strdup(buf);
258     }
259 
260     szPath = strdup(pwd.pw_dir);
261 
262     return szPath;
263 }
264 
265 /* we have read access on file */
ssh_file_readaccess_ok(const char * file)266 int ssh_file_readaccess_ok(const char *file)
267 {
268     if (access(file, R_OK) < 0) {
269         return 0;
270     }
271 
272     return 1;
273 }
274 
275 /**
276  * @brief Check if the given path is an existing directory and that is
277  * accessible for writing.
278  *
279  * @param[in] path Path to the directory to be checked
280  *
281  * @return Return 1 if the directory exists and is accessible; 0 otherwise
282  * */
ssh_dir_writeable(const char * path)283 int ssh_dir_writeable(const char *path)
284 {
285     struct stat buffer;
286     int rc;
287 
288     rc = stat(path, &buffer);
289     if (rc < 0) {
290         return 0;
291     }
292 
293     if (S_ISDIR(buffer.st_mode) && (buffer.st_mode & S_IWRITE)) {
294         return 1;
295     }
296 
297     return 0;
298 }
299 
ssh_get_local_username(void)300 char *ssh_get_local_username(void)
301 {
302     struct passwd pwd;
303     struct passwd *pwdbuf = NULL;
304     char buf[NSS_BUFLEN_PASSWD];
305     char *name;
306     int rc;
307 
308     rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
309     if (rc != 0 || pwdbuf == NULL) {
310         return NULL;
311     }
312 
313     name = strdup(pwd.pw_name);
314 
315     if (name == NULL) {
316         return NULL;
317     }
318 
319     return name;
320 }
321 
ssh_is_ipaddr_v4(const char * str)322 int ssh_is_ipaddr_v4(const char *str) {
323     int rc = -1;
324     struct in_addr dest;
325 
326     rc = inet_pton(AF_INET, str, &dest);
327     if (rc > 0) {
328         return 1;
329     }
330 
331     return 0;
332 }
333 
ssh_is_ipaddr(const char * str)334 int ssh_is_ipaddr(const char *str) {
335     int rc = -1;
336 
337     if (strchr(str, ':')) {
338         struct in6_addr dest6;
339 
340         /* TODO link-local (IP:v6:addr%ifname). */
341         rc = inet_pton(AF_INET6, str, &dest6);
342         if (rc > 0) {
343             return 1;
344         }
345     }
346 
347     return ssh_is_ipaddr_v4(str);
348 }
349 
350 #endif /* _WIN32 */
351 
ssh_lowercase(const char * str)352 char *ssh_lowercase(const char* str) {
353   char *new, *p;
354 
355   if (str == NULL) {
356     return NULL;
357   }
358 
359   new = strdup(str);
360   if (new == NULL) {
361     return NULL;
362   }
363 
364   for (p = new; *p; p++) {
365     *p = tolower(*p);
366   }
367 
368   return new;
369 }
370 
ssh_hostport(const char * host,int port)371 char *ssh_hostport(const char *host, int port)
372 {
373     char *dest = NULL;
374     size_t len;
375 
376     if (host == NULL) {
377         return NULL;
378     }
379 
380     /* 3 for []:, 5 for 65536 and 1 for nul */
381     len = strlen(host) + 3 + 5 + 1;
382     dest = malloc(len);
383     if (dest == NULL) {
384         return NULL;
385     }
386     snprintf(dest, len, "[%s]:%d", host, port);
387 
388     return dest;
389 }
390 
391 /**
392  * @brief Convert a buffer into a colon separated hex string.
393  * The caller has to free the memory.
394  *
395  * @param  what         What should be converted to a hex string.
396  *
397  * @param  len          Length of the buffer to convert.
398  *
399  * @return              The hex string or NULL on error.
400  *
401  * @see ssh_string_free_char()
402  */
ssh_get_hexa(const unsigned char * what,size_t len)403 char *ssh_get_hexa(const unsigned char *what, size_t len) {
404     const char h[] = "0123456789abcdef";
405     char *hexa;
406     size_t i;
407     size_t hlen = len * 3;
408 
409     if (len > (UINT_MAX - 1) / 3) {
410         return NULL;
411     }
412 
413     hexa = malloc(hlen + 1);
414     if (hexa == NULL) {
415         return NULL;
416     }
417 
418     for (i = 0; i < len; i++) {
419         hexa[i * 3] = h[(what[i] >> 4) & 0xF];
420         hexa[i * 3 + 1] = h[what[i] & 0xF];
421         hexa[i * 3 + 2] = ':';
422     }
423     hexa[hlen - 1] = '\0';
424 
425     return hexa;
426 }
427 
428 /**
429  * @deprecated          Please use ssh_print_hash() instead
430  */
ssh_print_hexa(const char * descr,const unsigned char * what,size_t len)431 void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
432     char *hexa = ssh_get_hexa(what, len);
433 
434     if (hexa == NULL) {
435       return;
436     }
437     fprintf(stderr, "%s: %s\n", descr, hexa);
438 
439     free(hexa);
440 }
441 
442 /**
443  * @brief Log the content of a buffer in hexadecimal format, similar to the
444  * output of 'hexdump -C' command.
445  *
446  * The first logged line is the given description followed by the length.
447  * Then the content of the buffer is logged 16 bytes per line in the following
448  * format:
449  *
450  * (offset) (first 8 bytes) (last 8 bytes) (the 16 bytes as ASCII char values)
451  *
452  * The output for a 16 bytes array containing values from 0x00 to 0x0f would be:
453  *
454  * "Example (16 bytes):"
455  * "  00000000  00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f  ................"
456  *
457  * The value for each byte as corresponding ASCII character is printed at the
458  * end if the value is printable. Otherwise it is replace with '.'.
459  *
460  * @param[in] descr A description for the content to be logged
461  * @param[in] what  The buffer to be logged
462  * @param[in] len   The length of the buffer given in what
463  *
464  * @note If a too long description is provided (which would result in a first
465  * line longer than 80 bytes), the function will fail.
466  */
ssh_log_hexdump(const char * descr,const unsigned char * what,size_t len)467 void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len)
468 {
469     size_t i;
470     char ascii[17];
471     const unsigned char *pc = NULL;
472     size_t count = 0;
473     ssize_t printed = 0;
474 
475     /* The required buffer size is calculated from:
476      *
477      *  2 bytes for spaces at the beginning
478      *  8 bytes for the offset
479      *  2 bytes for spaces
480      * 24 bytes to print the first 8 bytes + spaces
481      *  1 byte for an extra space
482      * 24 bytes to print next 8 bytes + spaces
483      *  2 bytes for extra spaces
484      * 16 bytes for the content as ASCII characters at the end
485      *  1 byte for the ending '\0'
486      *
487      * Resulting in 80 bytes.
488      *
489      * Except for the first line (description + size), all lines have fixed
490      * length. If a too long description is used, the function will fail.
491      * */
492     char buffer[80];
493 
494     /* Print description */
495     if (descr != NULL) {
496         printed = snprintf(buffer, sizeof(buffer), "%s ", descr);
497         if (printed < 0) {
498             goto error;
499         }
500         count += printed;
501     } else {
502         printed = snprintf(buffer, sizeof(buffer), "(NULL description) ");
503         if (printed < 0) {
504             goto error;
505         }
506         count += printed;
507     }
508 
509     if (len == 0) {
510         printed = snprintf(buffer + count, sizeof(buffer) - count,
511                            "(zero length):");
512         if (printed < 0) {
513             goto error;
514         }
515         SSH_LOG(SSH_LOG_DEBUG, "%s", buffer);
516         return;
517     } else {
518         printed = snprintf(buffer + count, sizeof(buffer) - count,
519                            "(%zu bytes):", len);
520         if (printed < 0) {
521             goto error;
522         }
523         count += printed;
524     }
525 
526     if (what == NULL) {
527         printed = snprintf(buffer + count, sizeof(buffer) - count,
528                            "(NULL)");
529         if (printed < 0) {
530             goto error;
531         }
532         SSH_LOG(SSH_LOG_DEBUG, "%s", buffer);
533         return;
534     }
535 
536     SSH_LOG(SSH_LOG_DEBUG, "%s", buffer);
537 
538     /* Reset state */
539     count = 0;
540     pc = what;
541 
542     for (i = 0; i < len; i++) {
543         /* Add one space after printing 8 bytes */
544         if ((i % 8) == 0) {
545             if (i != 0) {
546                 printed = snprintf(buffer + count, sizeof(buffer) - count, " ");
547                 if (printed < 0) {
548                     goto error;
549                 }
550                 count += printed;
551             }
552         }
553 
554         /* Log previous line and reset state for new line */
555         if ((i % 16) == 0) {
556             if (i != 0) {
557                 printed = snprintf(buffer + count, sizeof(buffer) - count,
558                                    "  %s", ascii);
559                 if (printed < 0) {
560                     goto error;
561                 }
562                 SSH_LOG(SSH_LOG_DEBUG, "%s", buffer);
563                 count = 0;
564             }
565 
566             /* Start a new line with the offset */
567             printed = snprintf(buffer, sizeof(buffer),
568                                "  %08zx ", i);
569             if (printed < 0) {
570                 goto error;
571             }
572             count += printed;
573         }
574 
575         /* Print the current byte hexadecimal representation */
576         printed = snprintf(buffer + count, sizeof(buffer) - count,
577                            " %02x", pc[i]);
578         if (printed < 0) {
579             goto error;
580         }
581         count += printed;
582 
583         /* If printable, store the ASCII character */
584         if (isprint(pc[i])) {
585             ascii[i % 16] = pc[i];
586         } else {
587             ascii[i % 16] = '.';
588         }
589         ascii[(i % 16) + 1] = '\0';
590     }
591 
592     /* Add padding if not exactly 16 characters */
593     while ((i % 16) != 0) {
594         /* Add one space after printing 8 bytes */
595         if ((i % 8) == 0) {
596             if (i != 0) {
597                 printed = snprintf(buffer + count, sizeof(buffer) - count, " ");
598                 if (printed < 0) {
599                     goto error;
600                 }
601                 count += printed;
602             }
603         }
604 
605         printed = snprintf(buffer + count, sizeof(buffer) - count, "   ");
606         if (printed < 0) {
607             goto error;
608         }
609         count += printed;
610         i++;
611     }
612 
613     /* Print the last printable part */
614     printed = snprintf(buffer + count, sizeof(buffer) - count,
615                        "   %s", ascii);
616     if (printed < 0) {
617         goto error;
618     }
619 
620     SSH_LOG(SSH_LOG_DEBUG, "%s", buffer);
621 
622     return;
623 
624 error:
625     SSH_LOG(SSH_LOG_WARN, "Could not print to buffer");
626     return;
627 }
628 
629 /**
630  * @brief Check if libssh is the required version or get the version
631  * string.
632  *
633  * @param[in]  req_version The version required.
634  *
635  * @return              If the version of libssh is newer than the version
636  *                      required it will return a version string.
637  *                      NULL if the version is older.
638  *
639  * Example:
640  *
641  * @code
642  *  if (ssh_version(SSH_VERSION_INT(0,2,1)) == NULL) {
643  *    fprintf(stderr, "libssh version is too old!\n");
644  *    exit(1);
645  *  }
646  *
647  *  if (debug) {
648  *    printf("libssh %s\n", ssh_version(0));
649  *  }
650  * @endcode
651  */
ssh_version(int req_version)652 const char *ssh_version(int req_version) {
653   if (req_version <= LIBSSH_VERSION_INT) {
654     return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING MBED_STRING
655       ZLIB_STRING;
656   }
657 
658   return NULL;
659 }
660 
ssh_list_new(void)661 struct ssh_list *ssh_list_new(void) {
662   struct ssh_list *ret=malloc(sizeof(struct ssh_list));
663   if(!ret)
664     return NULL;
665   ret->root=ret->end=NULL;
666   return ret;
667 }
668 
ssh_list_free(struct ssh_list * list)669 void ssh_list_free(struct ssh_list *list){
670   struct ssh_iterator *ptr,*next;
671   if(!list)
672     return;
673   ptr=list->root;
674   while(ptr){
675     next=ptr->next;
676     SAFE_FREE(ptr);
677     ptr=next;
678   }
679   SAFE_FREE(list);
680 }
681 
ssh_list_get_iterator(const struct ssh_list * list)682 struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list){
683   if(!list)
684     return NULL;
685   return list->root;
686 }
687 
ssh_list_find(const struct ssh_list * list,void * value)688 struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){
689   struct ssh_iterator *it;
690   for(it = ssh_list_get_iterator(list); it != NULL ;it=it->next)
691     if(it->data==value)
692       return it;
693   return NULL;
694 }
695 
696 /**
697  * @brief Get the number of elements in the list
698  *
699  * @param[in]  list     The list to count.
700  *
701  * @return The number of elements in the list.
702  */
ssh_list_count(const struct ssh_list * list)703 size_t ssh_list_count(const struct ssh_list *list)
704 {
705   struct ssh_iterator *it = NULL;
706   int count = 0;
707 
708   for (it = ssh_list_get_iterator(list); it != NULL ; it = it->next) {
709       count++;
710   }
711 
712   return count;
713 }
714 
ssh_iterator_new(const void * data)715 static struct ssh_iterator *ssh_iterator_new(const void *data){
716   struct ssh_iterator *iterator=malloc(sizeof(struct ssh_iterator));
717   if(!iterator)
718     return NULL;
719   iterator->next=NULL;
720   iterator->data=data;
721   return iterator;
722 }
723 
ssh_list_append(struct ssh_list * list,const void * data)724 int ssh_list_append(struct ssh_list *list,const void *data){
725   struct ssh_iterator *iterator = NULL;
726 
727   if (list == NULL) {
728       return SSH_ERROR;
729   }
730 
731   iterator = ssh_iterator_new(data);
732   if (iterator == NULL) {
733       return SSH_ERROR;
734   }
735 
736   if(!list->end){
737     /* list is empty */
738     list->root=list->end=iterator;
739   } else {
740     /* put it on end of list */
741     list->end->next=iterator;
742     list->end=iterator;
743   }
744   return SSH_OK;
745 }
746 
ssh_list_prepend(struct ssh_list * list,const void * data)747 int ssh_list_prepend(struct ssh_list *list, const void *data){
748   struct ssh_iterator *it = NULL;
749 
750   if (list == NULL) {
751       return SSH_ERROR;
752   }
753 
754   it = ssh_iterator_new(data);
755   if (it == NULL) {
756     return SSH_ERROR;
757   }
758 
759   if (list->end == NULL) {
760     /* list is empty */
761     list->root = list->end = it;
762   } else {
763     /* set as new root */
764     it->next = list->root;
765     list->root = it;
766   }
767 
768   return SSH_OK;
769 }
770 
ssh_list_remove(struct ssh_list * list,struct ssh_iterator * iterator)771 void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){
772   struct ssh_iterator *ptr,*prev;
773 
774   if (list == NULL) {
775       return;
776   }
777 
778   prev=NULL;
779   ptr=list->root;
780   while(ptr && ptr != iterator){
781     prev=ptr;
782     ptr=ptr->next;
783   }
784   if(!ptr){
785     /* we did not find the element */
786     return;
787   }
788   /* unlink it */
789   if(prev)
790     prev->next=ptr->next;
791   /* if iterator was the head */
792   if(list->root == iterator)
793     list->root=iterator->next;
794   /* if iterator was the tail */
795   if(list->end == iterator)
796     list->end = prev;
797   SAFE_FREE(iterator);
798 }
799 
800 /**
801  * @internal
802  *
803  * @brief Removes the top element of the list and returns the data value
804  * attached to it.
805  *
806  * @param[in[  list     The ssh_list to remove the element.
807  *
808  * @returns             A pointer to the element being stored in head, or NULL
809  *                      if the list is empty.
810  */
_ssh_list_pop_head(struct ssh_list * list)811 const void *_ssh_list_pop_head(struct ssh_list *list){
812   struct ssh_iterator *iterator = NULL;
813   const void *data = NULL;
814 
815   if (list == NULL) {
816       return NULL;
817   }
818 
819   iterator = list->root;
820   if (iterator == NULL) {
821       return NULL;
822   }
823   data=iterator->data;
824   list->root=iterator->next;
825   if(list->end==iterator)
826     list->end=NULL;
827   SAFE_FREE(iterator);
828   return data;
829 }
830 
831 /**
832  * @brief Parse directory component.
833  *
834  * dirname breaks a null-terminated pathname string into a directory component.
835  * In the usual case, ssh_dirname() returns the string up to, but not including,
836  * the final '/'. Trailing '/' characters are  not  counted as part of the
837  * pathname. The caller must free the memory.
838  *
839  * @param[in]  path     The path to parse.
840  *
841  * @return              The dirname of path or NULL if we can't allocate memory.
842  *                      If path does not contain a slash, c_dirname() returns
843  *                      the string ".".  If path is the string "/", it returns
844  *                      the string "/". If path is NULL or an empty string,
845  *                      "." is returned.
846  */
ssh_dirname(const char * path)847 char *ssh_dirname (const char *path) {
848   char *new = NULL;
849   size_t len;
850 
851   if (path == NULL || *path == '\0') {
852     return strdup(".");
853   }
854 
855   len = strlen(path);
856 
857   /* Remove trailing slashes */
858   while(len > 0 && path[len - 1] == '/') --len;
859 
860   /* We have only slashes */
861   if (len == 0) {
862     return strdup("/");
863   }
864 
865   /* goto next slash */
866   while(len > 0 && path[len - 1] != '/') --len;
867 
868   if (len == 0) {
869     return strdup(".");
870   } else if (len == 1) {
871     return strdup("/");
872   }
873 
874   /* Remove slashes again */
875   while(len > 0 && path[len - 1] == '/') --len;
876 
877   new = malloc(len + 1);
878   if (new == NULL) {
879     return NULL;
880   }
881 
882   strncpy(new, path, len);
883   new[len] = '\0';
884 
885   return new;
886 }
887 
888 /**
889  * @brief basename - parse filename component.
890  *
891  * basename breaks a null-terminated pathname string into a filename component.
892  * ssh_basename() returns the component following the final '/'.  Trailing '/'
893  * characters are not counted as part of the pathname.
894  *
895  * @param[in]  path     The path to parse.
896  *
897  * @return              The filename of path or NULL if we can't allocate
898  *                      memory. If path is a the string "/", basename returns
899  *                      the string "/". If path is NULL or an empty string,
900  *                      "." is returned.
901  */
ssh_basename(const char * path)902 char *ssh_basename (const char *path) {
903   char *new = NULL;
904   const char *s;
905   size_t len;
906 
907   if (path == NULL || *path == '\0') {
908     return strdup(".");
909   }
910 
911   len = strlen(path);
912   /* Remove trailing slashes */
913   while(len > 0 && path[len - 1] == '/') --len;
914 
915   /* We have only slashes */
916   if (len == 0) {
917     return strdup("/");
918   }
919 
920   while(len > 0 && path[len - 1] != '/') --len;
921 
922   if (len > 0) {
923     s = path + len;
924     len = strlen(s);
925 
926     while(len > 0 && s[len - 1] == '/') --len;
927   } else {
928     return strdup(path);
929   }
930 
931   new = malloc(len + 1);
932   if (new == NULL) {
933     return NULL;
934   }
935 
936   strncpy(new, s, len);
937   new[len] = '\0';
938 
939   return new;
940 }
941 
942 /**
943  * @brief Attempts to create a directory with the given pathname.
944  *
945  * This is the portable version of mkdir, mode is ignored on Windows systems.
946  *
947  * @param[in]  pathname The path name to create the directory.
948  *
949  * @param[in]  mode     The permissions to use.
950  *
951  * @return              0 on success, < 0 on error with errno set.
952  */
ssh_mkdir(const char * pathname,mode_t mode)953 int ssh_mkdir(const char *pathname, mode_t mode)
954 {
955     int r;
956 #ifdef _WIN32
957     r = _mkdir(pathname);
958 #else
959     r = mkdir(pathname, mode);
960 #endif
961 
962     return r;
963 }
964 
965 /**
966  * @brief Attempts to create a directory with the given pathname. The missing
967  * directories in the given pathname are created recursively.
968  *
969  * @param[in]  pathname The path name to create the directory.
970  *
971  * @param[in]  mode     The permissions to use.
972  *
973  * @return              0 on success, < 0 on error with errno set.
974  *
975  * @note mode is ignored on Windows systems.
976  */
ssh_mkdirs(const char * pathname,mode_t mode)977 int ssh_mkdirs(const char *pathname, mode_t mode)
978 {
979     int rc = 0;
980     char *parent = NULL;
981 
982     if (pathname == NULL ||
983         pathname[0] == '\0' ||
984         !strcmp(pathname, "/") ||
985         !strcmp(pathname, "."))
986     {
987         errno = EINVAL;
988         return -1;
989     }
990 
991     errno = 0;
992 
993 #ifdef _WIN32
994     rc = _mkdir(pathname);
995 #else
996     rc = mkdir(pathname, mode);
997 #endif
998 
999     if (rc < 0) {
1000         /* If a directory was missing, try to create the parent */
1001         if (errno == ENOENT) {
1002             parent = ssh_dirname(pathname);
1003             if (parent == NULL) {
1004                 errno = ENOMEM;
1005                 return -1;
1006             }
1007 
1008             rc = ssh_mkdirs(parent, mode);
1009             if (rc < 0) {
1010                 /* We could not create the parent */
1011                 SAFE_FREE(parent);
1012                 return -1;
1013             }
1014 
1015             SAFE_FREE(parent);
1016 
1017             /* Try again */
1018             errno = 0;
1019 #ifdef _WIN32
1020             rc = _mkdir(pathname);
1021 #else
1022             rc = mkdir(pathname, mode);
1023 #endif
1024         }
1025     }
1026 
1027     return rc;
1028 }
1029 
1030 /**
1031  * @brief Expand a directory starting with a tilde '~'
1032  *
1033  * @param[in]  d        The directory to expand.
1034  *
1035  * @return              The expanded directory, NULL on error.
1036  */
ssh_path_expand_tilde(const char * d)1037 char *ssh_path_expand_tilde(const char *d) {
1038     char *h = NULL, *r;
1039     const char *p;
1040     size_t ld;
1041     size_t lh = 0;
1042 
1043     if (d[0] != '~') {
1044         return strdup(d);
1045     }
1046     d++;
1047 
1048     /* handle ~user/path */
1049     p = strchr(d, '/');
1050     if (p != NULL && p > d) {
1051 #ifdef _WIN32
1052         return strdup(d);
1053 #else
1054         struct passwd *pw;
1055         size_t s = p - d;
1056         char u[128];
1057 
1058         if (s >= sizeof(u)) {
1059             return NULL;
1060         }
1061         memcpy(u, d, s);
1062         u[s] = '\0';
1063         pw = getpwnam(u);
1064         if (pw == NULL) {
1065             return NULL;
1066         }
1067         ld = strlen(p);
1068         h = strdup(pw->pw_dir);
1069 #endif
1070     } else {
1071         ld = strlen(d);
1072         p = (char *) d;
1073         h = ssh_get_user_home_dir();
1074     }
1075     if (h == NULL) {
1076         return NULL;
1077     }
1078     lh = strlen(h);
1079 
1080     r = malloc(ld + lh + 1);
1081     if (r == NULL) {
1082         SAFE_FREE(h);
1083         return NULL;
1084     }
1085 
1086     if (lh > 0) {
1087         memcpy(r, h, lh);
1088     }
1089     SAFE_FREE(h);
1090     memcpy(r + lh, p, ld + 1);
1091 
1092     return r;
1093 }
1094 
1095 /** @internal
1096  * @brief expands a string in function of session options
1097  * @param[in] s Format string to expand. Known parameters:
1098  *              %d SSH configuration directory (~/.ssh)
1099  *              %h target host name
1100  *              %u local username
1101  *              %l local hostname
1102  *              %r remote username
1103  *              %p remote port
1104  * @returns Expanded string.
1105  */
ssh_path_expand_escape(ssh_session session,const char * s)1106 char *ssh_path_expand_escape(ssh_session session, const char *s) {
1107     char host[NI_MAXHOST];
1108     char buf[MAX_BUF_SIZE];
1109     char *r, *x = NULL;
1110     const char *p;
1111     size_t i, l;
1112 
1113     r = ssh_path_expand_tilde(s);
1114     if (r == NULL) {
1115         ssh_set_error_oom(session);
1116         return NULL;
1117     }
1118 
1119     if (strlen(r) > MAX_BUF_SIZE) {
1120         ssh_set_error(session, SSH_FATAL, "string to expand too long");
1121         free(r);
1122         return NULL;
1123     }
1124 
1125     p = r;
1126     buf[0] = '\0';
1127 
1128     for (i = 0; *p != '\0'; p++) {
1129         if (*p != '%') {
1130         escape:
1131             buf[i] = *p;
1132             i++;
1133             if (i >= MAX_BUF_SIZE) {
1134                 free(r);
1135                 return NULL;
1136             }
1137             buf[i] = '\0';
1138             continue;
1139         }
1140 
1141         p++;
1142         if (*p == '\0') {
1143             break;
1144         }
1145 
1146         switch (*p) {
1147             case '%':
1148                 goto escape;
1149             case 'd':
1150                 x = strdup(session->opts.sshdir);
1151                 break;
1152             case 'u':
1153                 x = ssh_get_local_username();
1154                 break;
1155             case 'l':
1156                 if (gethostname(host, sizeof(host) == 0)) {
1157                     x = strdup(host);
1158                 }
1159                 break;
1160             case 'h':
1161                 x = strdup(session->opts.host);
1162                 break;
1163             case 'r':
1164                 x = strdup(session->opts.username);
1165                 break;
1166             case 'p':
1167                 if (session->opts.port < 65536) {
1168                     char tmp[6];
1169 
1170                     snprintf(tmp,
1171                              sizeof(tmp),
1172                              "%u",
1173                              session->opts.port > 0 ? session->opts.port : 22);
1174                     x = strdup(tmp);
1175                 }
1176                 break;
1177             default:
1178                 ssh_set_error(session, SSH_FATAL,
1179                         "Wrong escape sequence detected");
1180                 free(r);
1181                 return NULL;
1182         }
1183 
1184         if (x == NULL) {
1185             ssh_set_error_oom(session);
1186             free(r);
1187             return NULL;
1188         }
1189 
1190         i += strlen(x);
1191         if (i >= MAX_BUF_SIZE) {
1192             ssh_set_error(session, SSH_FATAL,
1193                     "String too long");
1194             free(x);
1195             free(r);
1196             return NULL;
1197         }
1198         l = strlen(buf);
1199         strncpy(buf + l, x, sizeof(buf) - l - 1);
1200         buf[i] = '\0';
1201         SAFE_FREE(x);
1202     }
1203 
1204     free(r);
1205     return strdup(buf);
1206 #undef MAX_BUF_SIZE
1207 }
1208 
1209 /**
1210  * @internal
1211  *
1212  * @brief Analyze the SSH banner to extract version information.
1213  *
1214  * @param  session      The session to analyze the banner from.
1215  * @param  server       0 means we are a client, 1 a server.
1216  *
1217  * @return 0 on success, < 0 on error.
1218  *
1219  * @see ssh_get_issue_banner()
1220  */
ssh_analyze_banner(ssh_session session,int server)1221 int ssh_analyze_banner(ssh_session session, int server)
1222 {
1223     const char *banner;
1224     const char *openssh;
1225 
1226     if (server) {
1227         banner = session->clientbanner;
1228     } else {
1229         banner = session->serverbanner;
1230     }
1231 
1232     if (banner == NULL) {
1233         ssh_set_error(session, SSH_FATAL, "Invalid banner");
1234         return -1;
1235     }
1236 
1237     /*
1238      * Typical banners e.g. are:
1239      *
1240      * SSH-1.5-openSSH_5.4
1241      * SSH-1.99-openSSH_3.0
1242      *
1243      * SSH-2.0-something
1244      * 012345678901234567890
1245      */
1246     if (strlen(banner) < 6 ||
1247         strncmp(banner, "SSH-", 4) != 0) {
1248           ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner);
1249           return -1;
1250     }
1251 
1252     SSH_LOG(SSH_LOG_PROTOCOL, "Analyzing banner: %s", banner);
1253 
1254     switch (banner[4]) {
1255         case '2':
1256             break;
1257         case '1':
1258             if (strlen(banner) > 6) {
1259                 if (banner[6] == '9') {
1260                     break;
1261                 }
1262             }
1263             FALL_THROUGH;
1264         default:
1265             ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner);
1266             return -1;
1267     }
1268 
1269     /* Make a best-effort to extract OpenSSH version numbers. */
1270     openssh = strstr(banner, "OpenSSH");
1271     if (openssh != NULL) {
1272         char *tmp = NULL;
1273         unsigned long int major = 0UL;
1274         unsigned long int minor = 0UL;
1275 
1276         /*
1277          * The banner is typical:
1278          * OpenSSH_5.4
1279          * 012345678901234567890
1280          */
1281         if (strlen(openssh) > 9) {
1282             major = strtoul(openssh + 8, &tmp, 10);
1283             if ((tmp == (openssh + 8)) ||
1284                 ((errno == ERANGE) && (major == ULONG_MAX)) ||
1285                 ((errno != 0) && (major == 0)) ||
1286                 ((major < 1) || (major > 100))) {
1287                 /* invalid major */
1288                 goto done;
1289             }
1290 
1291             minor = strtoul(openssh + 10, &tmp, 10);
1292             if ((tmp == (openssh + 10)) ||
1293                 ((errno == ERANGE) && (major == ULONG_MAX)) ||
1294                 ((errno != 0) && (major == 0)) ||
1295                 (minor > 100)) {
1296                 /* invalid minor */
1297                 goto done;
1298             }
1299 
1300             session->openssh = SSH_VERSION_INT(((int) major), ((int) minor), 0);
1301 
1302             SSH_LOG(SSH_LOG_PROTOCOL,
1303                     "We are talking to an OpenSSH client version: %lu.%lu (%x)",
1304                     major, minor, session->openssh);
1305         }
1306     }
1307 
1308 done:
1309     return 0;
1310 }
1311 
1312 /* try the Monotonic clock if possible for perfs reasons */
1313 #ifdef _POSIX_MONOTONIC_CLOCK
1314 #define CLOCK CLOCK_MONOTONIC
1315 #else
1316 #define CLOCK CLOCK_REALTIME
1317 #endif
1318 
1319 /**
1320  * @internal
1321  * @brief initializes a timestamp to the current time
1322  * @param[out] ts pointer to an allocated ssh_timestamp structure
1323  */
ssh_timestamp_init(struct ssh_timestamp * ts)1324 void ssh_timestamp_init(struct ssh_timestamp *ts){
1325 #ifdef HAVE_CLOCK_GETTIME
1326   struct timespec tp;
1327   clock_gettime(CLOCK, &tp);
1328   ts->useconds = tp.tv_nsec / 1000;
1329 #else
1330   struct timeval tp;
1331   gettimeofday(&tp, NULL);
1332   ts->useconds = tp.tv_usec;
1333 #endif
1334   ts->seconds = tp.tv_sec;
1335 }
1336 
1337 #undef CLOCK
1338 
1339 /**
1340  * @internal
1341  * @brief gets the time difference between two timestamps in ms
1342  * @param[in] old older value
1343  * @param[in] new newer value
1344  * @returns difference in milliseconds
1345  */
1346 
ssh_timestamp_difference(struct ssh_timestamp * old,struct ssh_timestamp * new)1347 static int ssh_timestamp_difference(struct ssh_timestamp *old,
1348     struct ssh_timestamp *new){
1349   long seconds, usecs, msecs;
1350   seconds = new->seconds - old->seconds;
1351   usecs = new->useconds - old->useconds;
1352   if (usecs < 0){
1353     seconds--;
1354     usecs += 1000000;
1355   }
1356   msecs = seconds * 1000 + usecs/1000;
1357   return msecs;
1358 }
1359 
1360 /**
1361  * @internal
1362  * @brief turn seconds and microseconds pair (as provided by user-set options)
1363  * into millisecond value
1364  * @param[in] sec number of seconds
1365  * @param[in] usec number of microseconds
1366  * @returns milliseconds, or 10000 if user supplied values are equal to zero
1367  */
ssh_make_milliseconds(long sec,long usec)1368 int ssh_make_milliseconds(long sec, long usec) {
1369 	int res = usec ? (usec / 1000) : 0;
1370 	res += (sec * 1000);
1371 	if (res == 0) {
1372 		res = 10 * 1000; /* use a reasonable default value in case
1373 				* SSH_OPTIONS_TIMEOUT is not set in options. */
1374 	}
1375 	return res;
1376 }
1377 
1378 /**
1379  * @internal
1380  * @brief Checks if a timeout is elapsed, in function of a previous
1381  * timestamp and an assigned timeout
1382  * @param[in] ts pointer to an existing timestamp
1383  * @param[in] timeout timeout in milliseconds. Negative values mean infinite
1384  *                   timeout
1385  * @returns 1 if timeout is elapsed
1386  *          0 otherwise
1387  */
ssh_timeout_elapsed(struct ssh_timestamp * ts,int timeout)1388 int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
1389     struct ssh_timestamp now;
1390 
1391     switch(timeout) {
1392         case -2: /*
1393                   * -2 means user-defined timeout as available in
1394                   * session->timeout, session->timeout_usec.
1395                   */
1396             SSH_LOG(SSH_LOG_WARN, "ssh_timeout_elapsed called with -2. this needs to "
1397                             "be fixed. please set a breakpoint on misc.c:%d and "
1398                             "fix the caller\n", __LINE__);
1399             return 0;
1400         case -1: /* -1 means infinite timeout */
1401             return 0;
1402         case 0: /* 0 means no timeout */
1403             return 1;
1404         default:
1405             break;
1406     }
1407 
1408     ssh_timestamp_init(&now);
1409 
1410     return (ssh_timestamp_difference(ts,&now) >= timeout);
1411 }
1412 
1413 /**
1414  * @brief updates a timeout value so it reflects the remaining time
1415  * @param[in] ts pointer to an existing timestamp
1416  * @param[in] timeout timeout in milliseconds. Negative values mean infinite
1417  *             timeout
1418  * @returns   remaining time in milliseconds, 0 if elapsed, -1 if never.
1419  */
ssh_timeout_update(struct ssh_timestamp * ts,int timeout)1420 int ssh_timeout_update(struct ssh_timestamp *ts, int timeout){
1421   struct ssh_timestamp now;
1422   int ms, ret;
1423   if (timeout <= 0) {
1424       return timeout;
1425   }
1426   ssh_timestamp_init(&now);
1427   ms = ssh_timestamp_difference(ts,&now);
1428   if(ms < 0)
1429     ms = 0;
1430   ret = timeout - ms;
1431   return ret >= 0 ? ret: 0;
1432 }
1433 
1434 
ssh_match_group(const char * group,const char * object)1435 int ssh_match_group(const char *group, const char *object)
1436 {
1437     const char *a;
1438     const char *z;
1439 
1440     z = group;
1441     do {
1442         a = strchr(z, ',');
1443         if (a == NULL) {
1444             if (strcmp(z, object) == 0) {
1445                 return 1;
1446             }
1447             return 0;
1448         } else {
1449             if (strncmp(z, object, a - z) == 0) {
1450                 return 1;
1451             }
1452         }
1453         z = a + 1;
1454     } while(1);
1455 
1456     /* not reached */
1457     return 0;
1458 }
1459 
1460 #if !defined(HAVE_EXPLICIT_BZERO)
explicit_bzero(void * s,size_t n)1461 void explicit_bzero(void *s, size_t n)
1462 {
1463 #if defined(HAVE_MEMSET_S)
1464     memset_s(s, n, '\0', n);
1465 #elif defined(HAVE_SECURE_ZERO_MEMORY)
1466     SecureZeroMemory(s, n);
1467 #else
1468     memset(s, '\0', n);
1469 #if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION)
1470     /* See http://llvm.org/bugs/show_bug.cgi?id=15495 */
1471     __asm__ volatile("" : : "g"(s) : "memory");
1472 #endif /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */
1473 #endif
1474 }
1475 #endif /* !HAVE_EXPLICIT_BZERO */
1476 
1477 #if !defined(HAVE_STRNDUP)
strndup(const char * s,size_t n)1478 char *strndup(const char *s, size_t n)
1479 {
1480     char *x = NULL;
1481 
1482     if (n + 1 < n) {
1483         return NULL;
1484     }
1485 
1486     x = malloc(n + 1);
1487     if (x == NULL) {
1488         return NULL;
1489     }
1490 
1491     memcpy(x, s, n);
1492     x[n] = '\0';
1493 
1494     return x;
1495 }
1496 #endif /* ! HAVE_STRNDUP */
1497 
1498 /* Increment 64b integer in network byte order */
1499 void
uint64_inc(unsigned char * counter)1500 uint64_inc(unsigned char *counter)
1501 {
1502     int i;
1503 
1504     for (i = 7; i >= 0; i--) {
1505         counter[i]++;
1506         if (counter[i])
1507           return;
1508     }
1509 }
1510 
1511 /**
1512  * @internal
1513  *
1514  * @brief Quote file name to be used on shell.
1515  *
1516  * Try to put the given file name between single quotes. There are special
1517  * cases:
1518  *
1519  * - When the '\'' char is found in the file name, it is double quoted
1520  *   - example:
1521  *     input: a'b
1522  *     output: 'a'"'"'b'
1523  * - When the '!' char is found in the file name, it is replaced by an unquoted
1524  *   verbatim char "\!"
1525  *   - example:
1526  *     input: a!b
1527  *     output 'a'\!'b'
1528  *
1529  * @param[in]   file_name  File name string to be quoted before used on shell
1530  * @param[out]  buf       Buffer to receive the final quoted file name.  Must
1531  *                        have room for the final quoted string.  The maximum
1532  *                        output length would be (3 * strlen(file_name) + 1)
1533  *                        since in the worst case each character would be
1534  *                        replaced by 3 characters, plus the terminating '\0'.
1535  * @param[in]   buf_len   The size of the provided output buffer
1536  *
1537  * @returns SSH_ERROR on error; length of the resulting string not counting the
1538  * string terminator '\0'
1539  * */
ssh_quote_file_name(const char * file_name,char * buf,size_t buf_len)1540 int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len)
1541 {
1542     const char *src = NULL;
1543     char *dst = NULL;
1544     size_t required_buf_len;
1545 
1546     enum ssh_quote_state_e state = NO_QUOTE;
1547 
1548     if (file_name == NULL || buf == NULL || buf_len == 0) {
1549         SSH_LOG(SSH_LOG_WARNING, "Invalid parameter");
1550         return SSH_ERROR;
1551     }
1552 
1553     /* Only allow file names smaller than 32kb. */
1554     if (strlen(file_name) > 32 * 1024) {
1555         SSH_LOG(SSH_LOG_WARNING, "File name too long");
1556         return SSH_ERROR;
1557     }
1558 
1559     /* Paranoia check */
1560     required_buf_len = (size_t)3 * strlen(file_name) + 1;
1561     if (required_buf_len > buf_len) {
1562         SSH_LOG(SSH_LOG_WARNING, "Buffer too small");
1563         return SSH_ERROR;
1564     }
1565 
1566     src = file_name;
1567     dst = buf;
1568 
1569     while ((*src != '\0')) {
1570         switch (*src) {
1571 
1572         /* The '\'' char is double quoted */
1573 
1574         case '\'':
1575             switch (state) {
1576             case NO_QUOTE:
1577                 /* Start a new double quoted string. The '\'' char will be
1578                  * copied to the beginning of it at the end of the loop. */
1579                 *dst++ = '"';
1580                 break;
1581             case SINGLE_QUOTE:
1582                 /* Close the current single quoted string and start a new double
1583                  * quoted string. The '\'' char will be copied to the beginning
1584                  * of it at the end of the loop. */
1585                 *dst++ = '\'';
1586                 *dst++ = '"';
1587                 break;
1588             case DOUBLE_QUOTE:
1589                 /* If already in the double quoted string, keep copying the
1590                  * sequence of chars. */
1591                 break;
1592             default:
1593                 /* Should never be reached */
1594                 goto error;
1595             }
1596 
1597             /* When the '\'' char is found, the resulting state will be
1598              * DOUBLE_QUOTE in any case*/
1599             state = DOUBLE_QUOTE;
1600             break;
1601 
1602         /* The '!' char is replaced by unquoted "\!" */
1603 
1604         case '!':
1605             switch (state) {
1606             case NO_QUOTE:
1607                 /* The '!' char is interpreted in some shells (e.g. CSH) even
1608                  * when is quoted with single quotes.  Replace it with unquoted
1609                  * "\!" which is correctly interpreted as the '!' character. */
1610                 *dst++ = '\\';
1611                 break;
1612             case SINGLE_QUOTE:
1613                 /* Close the current quoted string and replace '!' for unquoted
1614                  * "\!" */
1615                 *dst++ = '\'';
1616                 *dst++ = '\\';
1617                 break;
1618             case DOUBLE_QUOTE:
1619                 /* Close current quoted string and replace  "!" for unquoted
1620                  * "\!" */
1621                 *dst++ = '"';
1622                 *dst++ = '\\';
1623                 break;
1624             default:
1625                 /* Should never be reached */
1626                 goto error;
1627             }
1628 
1629             /* When the '!' char is found, the resulting state will be NO_QUOTE
1630              * in any case*/
1631             state = NO_QUOTE;
1632             break;
1633 
1634         /* Ordinary chars are single quoted */
1635 
1636         default:
1637             switch (state) {
1638             case NO_QUOTE:
1639                 /* Start a new single quoted string */
1640                 *dst++ = '\'';
1641                 break;
1642             case SINGLE_QUOTE:
1643                 /* If already in the single quoted string, keep copying the
1644                  * sequence of chars. */
1645                 break;
1646             case DOUBLE_QUOTE:
1647                 /* Close current double quoted string and start a new single
1648                  * quoted string. */
1649                 *dst++ = '"';
1650                 *dst++ = '\'';
1651                 break;
1652             default:
1653                 /* Should never be reached */
1654                 goto error;
1655             }
1656 
1657             /* When an ordinary char is found, the resulting state will be
1658              * SINGLE_QUOTE in any case*/
1659             state = SINGLE_QUOTE;
1660             break;
1661         }
1662 
1663         /* Copy the current char to output */
1664         *dst++ = *src++;
1665     }
1666 
1667     /* Close the quoted string when necessary */
1668 
1669     switch (state) {
1670     case NO_QUOTE:
1671         /* No open string */
1672         break;
1673     case SINGLE_QUOTE:
1674         /* Close current single quoted string */
1675         *dst++ = '\'';
1676         break;
1677     case DOUBLE_QUOTE:
1678         /* Close current double quoted string */
1679         *dst++ = '"';
1680         break;
1681     default:
1682         /* Should never be reached */
1683         goto error;
1684     }
1685 
1686     /* Put the string terminator */
1687     *dst = '\0';
1688 
1689     return dst - buf;
1690 
1691 error:
1692     return SSH_ERROR;
1693 }
1694 
1695 /**
1696  * @internal
1697  *
1698  * @brief Given a string, encode existing newlines as the string "\\n"
1699  *
1700  * @param[in]  string   Input string
1701  * @param[out] buf      Output buffer. This buffer must be at least (2 *
1702  *                      strlen(string)) + 1 long.  In the worst case,
1703  *                      each character can be encoded as 2 characters plus the
1704  *                      terminating '\0'.
1705  * @param[in]  buf_len  Size of the provided output buffer
1706  *
1707  * @returns SSH_ERROR on error; length of the resulting string not counting the
1708  * terminating '\0' otherwise
1709  */
ssh_newline_vis(const char * string,char * buf,size_t buf_len)1710 int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
1711 {
1712     const char *in = NULL;
1713     char *out = NULL;
1714 
1715     if (string == NULL || buf == NULL || buf_len == 0) {
1716         return SSH_ERROR;
1717     }
1718 
1719     if ((2 * strlen(string) + 1) > buf_len) {
1720         SSH_LOG(SSH_LOG_WARNING, "Buffer too small");
1721         return SSH_ERROR;
1722     }
1723 
1724     out = buf;
1725     for (in = string; *in != '\0'; in++) {
1726         if (*in == '\n') {
1727             *out++ = '\\';
1728             *out++ = 'n';
1729         } else {
1730             *out++ = *in;
1731         }
1732     }
1733     *out = '\0';
1734 
1735     return out - buf;
1736 }
1737 
1738 /** @} */
1739