1 /* Acknowledgement: this file gathers config file parsing related functions in
2  * libssh */
3 #include <netdb.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <fstream>
7 #include <iostream>
8 #include <string>
9 
10 /* This is needed for a standard getpwuid_r on opensolaris */
11 #define _POSIX_PTHREAD_SEMANTICS
12 #include <arpa/inet.h>
13 #include <netinet/in.h>
14 #include <pwd.h>
15 #include <sys/socket.h>
16 #include <sys/types.h>
17 
18 #include <ctype.h>
19 #include <errno.h>
20 #include <limits.h>
21 #if __FreeBSD__
22 #define _WITH_GETLINE
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <time.h>
30 #ifdef HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif /* HAVE_SYS_TIME_H */
33 
34 using namespace std;
35 
36 #define MAX_LINE_SIZE 1024
37 
38 #define SAFE_FREE(x)   \
39   do {                 \
40     if ((x) != NULL) { \
41       free(x);         \
42       x = NULL;        \
43     }                  \
44   } while (0)
45 
46 #ifndef NSS_BUFLEN_PASSWD
47 #define NSS_BUFLEN_PASSWD 4096
48 #endif /* NSS_BUFLEN_PASSWD */
49 
50 #ifndef MAX_BUF_SIZE
51 #define MAX_BUF_SIZE 4096
52 #endif
53 
54 /* Socket type */
55 #ifndef socket_t
56 typedef int socket_t;
57 #endif
58 
59 enum ssh_config_opcode_e {
60   SOC_UNSUPPORTED = -1,
61   SOC_HOST,
62   SOC_HOSTNAME,
63   SOC_PORT,
64   SOC_USERNAME,
65   SOC_TIMEOUT,
66   SOC_PROTOCOL,
67   SOC_STRICTHOSTKEYCHECK,
68   SOC_KNOWNHOSTS,
69   SOC_PROXYCOMMAND,
70   SOC_GSSAPISERVERIDENTITY,
71   SOC_GSSAPICLIENTIDENTITY,
72   SOC_GSSAPIDELEGATECREDENTIALS,
73   SOC_INCLUDE,
74   SOC_PROXYJUMP,
75   SOC_END /* Keep this one last in the list */
76 };
77 
78 enum ssh_options_e {
79   SSH_OPTIONS_HOST,
80   SSH_OPTIONS_PORT,
81   SSH_OPTIONS_PORT_STR,
82   SSH_OPTIONS_FD,
83   SSH_OPTIONS_USER,
84   SSH_OPTIONS_SSH_DIR,
85   SSH_OPTIONS_IDENTITY,
86   SSH_OPTIONS_ADD_IDENTITY,
87   SSH_OPTIONS_KNOWNHOSTS,
88   SSH_OPTIONS_TIMEOUT,
89   SSH_OPTIONS_TIMEOUT_USEC,
90   SSH_OPTIONS_SSH1,
91   SSH_OPTIONS_SSH2,
92   SSH_OPTIONS_LOG_VERBOSITY,
93   SSH_OPTIONS_LOG_VERBOSITY_STR,
94   SSH_OPTIONS_CIPHERS_C_S,
95   SSH_OPTIONS_CIPHERS_S_C,
96   SSH_OPTIONS_COMPRESSION_C_S,
97   SSH_OPTIONS_COMPRESSION_S_C,
98   SSH_OPTIONS_PROXYCOMMAND,
99   SSH_OPTIONS_BINDADDR,
100   SSH_OPTIONS_STRICTHOSTKEYCHECK,
101   SSH_OPTIONS_COMPRESSION,
102   SSH_OPTIONS_COMPRESSION_LEVEL,
103   SSH_OPTIONS_KEY_EXCHANGE,
104   SSH_OPTIONS_HOSTKEYS,
105   SSH_OPTIONS_GSSAPI_SERVER_IDENTITY,
106   SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY,
107   SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
108   SSH_OPTIONS_HMAC_C_S,
109   SSH_OPTIONS_HMAC_S_C,
110   SSH_OPTIONS_PROXYJUMP,
111 };
112 
113 struct Options {
114   char *username;
115   char *host;
116   char *sshdir;
117   char *knownhosts;
118   char *ProxyCommand;
119   char *ProxyJump;
120   unsigned long timeout; /* seconds */
121   unsigned int port;
122   int StrictHostKeyChecking;
123   int ssh2;
124   int ssh1;
125   char *gss_server_identity;
126   char *gss_client_identity;
127   int gss_delegate_creds;
128 };
129 
130 struct ssh_config_keyword_table_s {
131   const char *name;
132   enum ssh_config_opcode_e opcode;
133 };
134 
135 static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
136     {"host", SOC_HOST},
137     {"hostname", SOC_HOSTNAME},
138     {"port", SOC_PORT},
139     {"user", SOC_USERNAME},
140     {"connecttimeout", SOC_TIMEOUT},
141     {"protocol", SOC_PROTOCOL},
142     {"stricthostkeychecking", SOC_STRICTHOSTKEYCHECK},
143     {"userknownhostsfile", SOC_KNOWNHOSTS},
144     {"proxycommand", SOC_PROXYCOMMAND},
145     {"gssapiserveridentity", SOC_GSSAPISERVERIDENTITY},
146     {"gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY},
147     {"gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS},
148     {"include", SOC_INCLUDE},
149     {"proxyjump", SOC_PROXYJUMP},
150     {NULL, SOC_UNSUPPORTED}};
151 
ssh_config_get_opcode(char * keyword)152 static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
153   int i;
154 
155   for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
156     if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
157       return ssh_config_keyword_table[i].opcode;
158     }
159   }
160 
161   return SOC_UNSUPPORTED;
162 }
163 
164 static int ssh_config_parse_line(struct Options *options, const char *line,
165                                  unsigned int count, int *parsing, int seen[]);
166 
ssh_get_user_home_dir(void)167 char *ssh_get_user_home_dir(void) {
168   char *szPath = NULL;
169   struct passwd pwd;
170   struct passwd *pwdbuf;
171   char buf[NSS_BUFLEN_PASSWD];
172   int rc;
173 
174   rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
175   if (rc != 0) {
176     szPath = getenv("HOME");
177     if (szPath == NULL) {
178       return NULL;
179     }
180     memset(buf, 0, sizeof(buf));
181     snprintf(buf, sizeof(buf), "%s", szPath);
182 
183     return strdup(buf);
184   }
185 
186   szPath = strdup(pwd.pw_dir);
187 
188   return szPath;
189 }
190 
ssh_get_local_username(void)191 char *ssh_get_local_username(void) {
192   struct passwd pwd;
193   struct passwd *pwdbuf;
194   char buf[NSS_BUFLEN_PASSWD];
195   char *name;
196   int rc;
197 
198   rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
199   if (rc != 0) {
200     return NULL;
201   }
202 
203   name = strdup(pwd.pw_name);
204 
205   if (name == NULL) {
206     return NULL;
207   }
208 
209   return name;
210 }
211 
ssh_lowercase(const char * str)212 char *ssh_lowercase(const char *str) {
213   char *n, *p;
214 
215   if (str == NULL) {
216     return NULL;
217   }
218 
219   n = strdup(str);
220   if (n == NULL) {
221     return NULL;
222   }
223 
224   for (p = n; *p; p++) {
225     *p = tolower(*p);
226   }
227 
228   return n;
229 }
230 
231 /*
232  * Returns true if the given string matches the pattern (which may contain ?
233  * and * as wildcards), and zero if it does not match.
234  */
match_pattern(const char * s,const char * pattern)235 static int match_pattern(const char *s, const char *pattern) {
236   if (s == NULL || pattern == NULL) {
237     return 0;
238   }
239 
240   for (;;) {
241     /* If at end of pattern, accept if also at end of string. */
242     if (*pattern == '\0') {
243       return (*s == '\0');
244     }
245 
246     if (*pattern == '*') {
247       /* Skip the asterisk. */
248       pattern++;
249 
250       /* If at end of pattern, accept immediately. */
251       if (!*pattern) return 1;
252 
253       /* If next character in pattern is known, optimize. */
254       if (*pattern != '?' && *pattern != '*') {
255         /*
256          * Look instances of the next character in
257          * pattern, and try to match starting from
258          * those.
259          */
260         for (; *s; s++)
261           if (*s == *pattern && match_pattern(s + 1, pattern + 1)) {
262             return 1;
263           }
264         /* Failed. */
265         return 0;
266       }
267       /*
268        * Move ahead one character at a time and try to
269        * match at each position.
270        */
271       for (; *s; s++) {
272         if (match_pattern(s, pattern)) {
273           return 1;
274         }
275       }
276       /* Failed. */
277       return 0;
278     }
279     /*
280      * There must be at least one more character in the string.
281      * If we are at the end, fail.
282      */
283     if (!*s) {
284       return 0;
285     }
286 
287     /* Check if the next character of the string is acceptable. */
288     if (*pattern != '?' && *pattern != *s) {
289       return 0;
290     }
291 
292     /* Move to the next character, both in string and in pattern. */
293     s++;
294     pattern++;
295   }
296 
297   /* NOTREACHED */
298   return 0;
299 }
300 
301 /*
302  * Tries to match the string against the comma-separated sequence of subpatterns
303  * (each possibly preceded by ! to indicate negation).
304  * Returns -1 if negation matches, 1 if there is a positive match, 0 if there is
305  * no match at all.
306  */
match_pattern_list(const char * string,const char * pattern,unsigned int len,int dolower)307 static int match_pattern_list(const char *string, const char *pattern,
308                               unsigned int len, int dolower) {
309   char sub[1024];
310   int negated;
311   int got_positive;
312   unsigned int i, subi;
313 
314   got_positive = 0;
315   for (i = 0; i < len;) {
316     /* Check if the subpattern is negated. */
317     if (pattern[i] == '!') {
318       negated = 1;
319       i++;
320     } else {
321       negated = 0;
322     }
323 
324     /*
325      * Extract the subpattern up to a comma or end.  Convert the
326      * subpattern to lowercase.
327      */
328     for (subi = 0; i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
329          subi++, i++) {
330       sub[subi] = dolower && isupper(pattern[i]) ? (char)tolower(pattern[i])
331                                                  : pattern[i];
332     }
333 
334     /* If subpattern too long, return failure (no match). */
335     if (subi >= sizeof(sub) - 1) {
336       return 0;
337     }
338 
339     /* If the subpattern was terminated by a comma, skip the comma. */
340     if (i < len && pattern[i] == ',') {
341       i++;
342     }
343 
344     /* Null-terminate the subpattern. */
345     sub[subi] = '\0';
346 
347     /* Try to match the subpattern against the string. */
348     if (match_pattern(string, sub)) {
349       if (negated) {
350         return -1; /* Negative */
351       } else {
352         got_positive = 1; /* Positive */
353       }
354     }
355   }
356 
357   /*
358    * Return success if got a positive match.  If there was a negative
359    * match, we have already returned -1 and never get here.
360    */
361   return got_positive;
362 }
363 
364 /*
365  * Tries to match the host name (which must be in all lowercase) against the
366  * comma-separated sequence of subpatterns (each possibly preceded by ! to
367  * indicate negation).
368  * Returns -1 if negation matches, 1 if there is a positive match, 0 if there
369  * is no match at all.
370  */
match_hostname(const char * host,const char * pattern,unsigned int len)371 int match_hostname(const char *host, const char *pattern, unsigned int len) {
372   return match_pattern_list(host, pattern, len, 1);
373 }
374 
375 /**
376  * @brief Expand a directory starting with a tilde '~'
377  *
378  * @param[in]  d        The directory to expand.
379  *
380  * @return              The expanded directory, NULL on error.
381  */
ssh_path_expand_tilde(const char * d)382 char *ssh_path_expand_tilde(const char *d) {
383   char *h = NULL, *r;
384   const char *p;
385   size_t ld;
386   size_t lh = 0;
387 
388   if (d[0] != '~') {
389     return strdup(d);
390   }
391   d++;
392 
393   /* handle ~user/path */
394   p = strchr(d, '/');
395   if (p != NULL && p > d) {
396     struct passwd *pw;
397     size_t s = p - d;
398     char u[128];
399 
400     if (s >= sizeof(u)) {
401       return NULL;
402     }
403     memcpy(u, d, s);
404     u[s] = '\0';
405     pw = getpwnam(u);
406     if (pw == NULL) {
407       return NULL;
408     }
409     ld = strlen(p);
410     h = strdup(pw->pw_dir);
411   } else {
412     ld = strlen(d);
413     p = (char *)d;
414     h = ssh_get_user_home_dir();
415   }
416   if (h == NULL) {
417     return NULL;
418   }
419   lh = strlen(h);
420 
421   r = static_cast<char *>(malloc(ld + lh + 1));
422   if (r == NULL) {
423     SAFE_FREE(h);
424     return NULL;
425   }
426 
427   if (lh > 0) {
428     memcpy(r, h, lh);
429   }
430   SAFE_FREE(h);
431   memcpy(r + lh, p, ld + 1);
432 
433   return r;
434 }
435 
ssh_path_expand_escape(struct Options * options,const char * s)436 char *ssh_path_expand_escape(struct Options *options, const char *s) {
437   char host[NI_MAXHOST];
438   char buf[MAX_BUF_SIZE];
439   char *r, *x = NULL;
440   const char *p;
441   size_t i, l;
442 
443   r = ssh_path_expand_tilde(s);
444   if (r == NULL) {
445     cout << "error" << endl;
446     return NULL;
447   }
448 
449   if (strlen(r) > MAX_BUF_SIZE) {
450     cout << "string to expand too long" << endl;
451     free(r);
452     return NULL;
453   }
454 
455   p = r;
456   buf[0] = '\0';
457 
458   for (i = 0; *p != '\0'; p++) {
459     if (*p != '%') {
460       buf[i] = *p;
461       i++;
462       if (i >= MAX_BUF_SIZE) {
463         free(r);
464         return NULL;
465       }
466       buf[i] = '\0';
467       continue;
468     }
469 
470     p++;
471     if (*p == '\0') {
472       break;
473     }
474 
475     switch (*p) {
476       case 'd':
477         x = strdup(options->sshdir);
478         break;
479       case 'u':
480         x = ssh_get_local_username();
481         break;
482       case 'l':
483         if (gethostname(host, sizeof(host) == 0)) {
484           x = strdup(host);
485         }
486         break;
487       case 'h':
488         x = strdup(options->host);
489         break;
490       case 'r':
491         x = strdup(options->username);
492         break;
493       case 'p':
494         if (options->port < 65536) {
495           char tmp[6];
496 
497           snprintf(tmp, sizeof(tmp), "%u", options->port);
498           x = strdup(tmp);
499         }
500         break;
501       default:
502         cout << "Wrong escape sequence detected" << endl;
503         free(r);
504         return NULL;
505     }
506 
507     if (x == NULL) {
508       cout << "error" << endl;
509       free(r);
510       return NULL;
511     }
512 
513     i += strlen(x);
514     if (i >= MAX_BUF_SIZE) {
515       cout << "String too long" << endl;
516       free(x);
517       free(r);
518       return NULL;
519     }
520     l = strlen(buf);
521     strncpy(buf + l, x, sizeof(buf) - l - 1);
522     buf[i] = '\0';
523     SAFE_FREE(x);
524   }
525 
526   free(r);
527   return strdup(buf);
528 #undef MAX_BUF_SIZE
529 }
530 
531 // ssh_options_set
532 /**
533  * @brief This function can set all possible ssh options.
534  *
535  * @param  session An allocated SSH session structure.
536  *
537  * @param  type The option type to set. This could be one of the
538  *              following:
539  *
540  *              - SSH_OPTIONS_HOST:
541  *                The hostname or ip address to connect to (const char *).
542  *
543  *              - SSH_OPTIONS_PORT:
544  *                The port to connect to (unsigned int).
545  *
546  *              - SSH_OPTIONS_PORT_STR:
547  *                The port to connect to (const char *).
548  *
549  *              - SSH_OPTIONS_FD:
550  *                The file descriptor to use (socket_t).\n
551  *                \n
552  *                If you wish to open the socket yourself for a reason
553  *                or another, set the file descriptor. Don't forget to
554  *                set the hostname as the hostname is used as a key in
555  *                the known_host mechanism.
556  *
557  *              - SSH_OPTIONS_BINDADDR:
558  *                The address to bind the client to (const char *).
559  *
560  *              - SSH_OPTIONS_USER:
561  *                The username for authentication (const char *).\n
562  *                \n
563  *                If the value is NULL, the username is set to the
564  *                default username.
565  *
566  *              - SSH_OPTIONS_SSH_DIR:
567  *                Set the ssh directory (const char *,format string).\n
568  *                \n
569  *                If the value is NULL, the directory is set to the
570  *                default ssh directory.\n
571  *                \n
572  *                The ssh directory is used for files like known_hosts
573  *                and identity (private and public key). It may include
574  *                "%s" which will be replaced by the user home
575  *                directory.
576  *
577  *              - SSH_OPTIONS_KNOWNHOSTS:
578  *                Set the known hosts file name (const char *,format string).\n
579  *                \n
580  *                If the value is NULL, the directory is set to the
581  *                default known hosts file, normally
582  *                ~/.ssh/known_hosts.\n
583  *                \n
584  *                The known hosts file is used to certify remote hosts
585  *                are genuine. It may include "%s" which will be
586  *                replaced by the user home directory.
587  *
588  *              - SSH_OPTIONS_IDENTITY:
589  *                Set the identity file name (const char *,format string).\n
590  *                \n
591  *                By default identity, id_dsa and id_rsa are checked.\n
592  *                \n
593  *                The identity file used authenticate with public key.
594  *                It may include "%s" which will be replaced by the
595  *                user home directory.
596  *
597  *              - SSH_OPTIONS_TIMEOUT:
598  *                Set a timeout for the connection in seconds (long).
599  *
600  *              - SSH_OPTIONS_TIMEOUT_USEC:
601  *                Set a timeout for the connection in micro seconds
602  *                        (long).
603  *
604  *              - SSH_OPTIONS_SSH1:
605  *                Allow or deny the connection to SSH1 servers
606  *                (int, 0 is false).
607  *
608  *              - SSH_OPTIONS_SSH2:
609  *                Allow or deny the connection to SSH2 servers
610  *                (int, 0 is false).
611  *
612  *              - SSH_OPTIONS_LOG_VERBOSITY:
613  *                Set the session logging verbosity (int).\n
614  *                \n
615  *                The verbosity of the messages. Every log smaller or
616  *                equal to verbosity will be shown.
617  *                - SSH_LOG_NOLOG: No logging
618  *                - SSH_LOG_RARE: Rare conditions or warnings
619  *                - SSH_LOG_ENTRY: API-accessible entrypoints
620  *                - SSH_LOG_PACKET: Packet id and size
621  *                - SSH_LOG_FUNCTIONS: Function entering and leaving
622  *
623  *              - SSH_OPTIONS_LOG_VERBOSITY_STR:
624  *                Set the session logging verbosity (const char *).\n
625  *                \n
626  *                The verbosity of the messages. Every log smaller or
627  *                equal to verbosity will be shown.
628  *                - SSH_LOG_NOLOG: No logging
629  *                - SSH_LOG_RARE: Rare conditions or warnings
630  *                - SSH_LOG_ENTRY: API-accessible entrypoints
631  *                - SSH_LOG_PACKET: Packet id and size
632  *                - SSH_LOG_FUNCTIONS: Function entering and leaving
633  *                \n
634  *                See the corresponding numbers in libssh.h.
635  *
636  *              - SSH_OPTIONS_AUTH_CALLBACK:
637  *                Set a callback to use your own authentication function
638  *                (function pointer).
639  *
640  *              - SSH_OPTIONS_AUTH_USERDATA:
641  *                Set the user data passed to the authentication
642  *                function (generic pointer).
643  *
644  *              - SSH_OPTIONS_LOG_CALLBACK:
645  *                Set a callback to use your own logging function
646  *                (function pointer).
647  *
648  *              - SSH_OPTIONS_LOG_USERDATA:
649  *                Set the user data passed to the logging function
650  *                (generic pointer).
651  *
652  *              - SSH_OPTIONS_STATUS_CALLBACK:
653  *                Set a callback to show connection status in realtime
654  *                (function pointer).\n
655  *                \n
656  *                @code
657  *                fn(void *arg, float status)
658  *                @endcode
659  *                \n
660  *                During ssh_connect(), libssh will call the callback
661  *                with status from 0.0 to 1.0.
662  *
663  *              - SSH_OPTIONS_STATUS_ARG:
664  *                Set the status argument which should be passed to the
665  *                status callback (generic pointer).
666  *
667  *              - SSH_OPTIONS_CIPHERS_C_S:
668  *                Set the symmetric cipher client to server (const char *,
669  *                comma-separated list).
670  *
671  *              - SSH_OPTIONS_CIPHERS_S_C:
672  *                Set the symmetric cipher server to client (const char *,
673  *                comma-separated list).
674  *
675  *              - SSH_OPTIONS_KEY_EXCHANGE:
676  *                Set the key exchange method to be used (const char *,
677  *                comma-separated list). ex:
678  *                "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
679  *
680  *              - SSH_OPTIONS_HOSTKEYS:
681  *                Set the preferred server host key types (const char *,
682  *                comma-separated list). ex:
683  *                "ssh-rsa,ssh-dss,ecdh-sha2-nistp256"
684  *
685  *              - SSH_OPTIONS_COMPRESSION_C_S:
686  *                Set the compression to use for client to server
687  *                communication (const char *, "yes", "no" or a specific
688  *                algorithm name if needed ("zlib","zlib@openssh.com","none").
689  *
690  *              - SSH_OPTIONS_COMPRESSION_S_C:
691  *                Set the compression to use for server to client
692  *                communication (const char *, "yes", "no" or a specific
693  *                algorithm name if needed ("zlib","zlib@openssh.com","none").
694  *
695  *              - SSH_OPTIONS_COMPRESSION:
696  *                Set the compression to use for both directions
697  *                communication (const char *, "yes", "no" or a specific
698  *                algorithm name if needed ("zlib","zlib@openssh.com","none").
699  *
700  *              - SSH_OPTIONS_COMPRESSION_LEVEL:
701  *                Set the compression level to use for zlib functions. (int,
702  *                value from 1 to 9, 9 being the most efficient but slower).
703  *
704  *              - SSH_OPTIONS_STRICTHOSTKEYCHECK:
705  *                Set the parameter StrictHostKeyChecking to avoid
706  *                asking about a fingerprint (int, 0 = false).
707  *
708  *              - SSH_OPTIONS_PROXYCOMMAND:
709  *                Set the command to be executed in order to connect to
710  *                server (const char *).
711  *
712  *              - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY
713  *                Set it to specify the GSSAPI server identity that libssh
714  *                should expect when connecting to the server (const char *).
715  *
716  *              - SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY
717  *                Set it to specify the GSSAPI client identity that libssh
718  *                should expect when connecting to the server (const char *).
719  *
720  *              - SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS
721  *                Set it to specify that GSSAPI should delegate credentials
722  *                to the server (int, 0 = false).
723  *
724  * @param  value The value to set. This is a generic pointer and the
725  *               datatype which is used should be set according to the
726  *               type set.
727  *
728  * @return       0 on success, < 0 on error.
729  */
ssh_options_set(struct Options * options,enum ssh_options_e type,const void * value)730 int ssh_options_set(struct Options *options, enum ssh_options_e type,
731                     const void *value) {
732   const char *v;
733   char *p, *q;
734   long int i;
735   int rc;
736 
737   if (options == NULL) {
738     return -1;
739   }
740 
741   switch (type) {
742     case SSH_OPTIONS_HOST:
743       v = static_cast<const char *>(value);
744       if (v == NULL || v[0] == '\0') {
745         cout << "invalid error" << endl;
746         return -1;
747       } else {
748         q = strdup(static_cast<const char *>(value));
749         if (q == NULL) {
750           cout << "error" << endl;
751           return -1;
752         }
753         p = strchr(q, '@');
754 
755         if (options->host) SAFE_FREE(options->host);
756 
757         if (p) {
758           *p = '\0';
759           options->host = strdup(p + 1);
760           if (options->host == NULL) {
761             SAFE_FREE(q);
762             cout << "error" << endl;
763             return -1;
764           }
765 
766           SAFE_FREE(options->username);
767           options->username = strdup(q);
768           SAFE_FREE(q);
769           if (options->username == NULL) {
770             cout << "error" << endl;
771             return -1;
772           }
773         } else {
774           options->host = q;
775         }
776       }
777       break;
778     case SSH_OPTIONS_PORT:
779       if (value == NULL) {
780         cout << "invalid error" << endl;
781         return -1;
782       } else {
783         int *x = (int *)value;
784         if (*x <= 0) {
785           cout << "invalid error" << endl;
786           return -1;
787         }
788 
789         options->port = *x & 0xffff;
790       }
791       break;
792     case SSH_OPTIONS_PORT_STR:
793       v = static_cast<const char *>(value);
794       if (v == NULL || v[0] == '\0') {
795         cout << "invalid error" << endl;
796         return -1;
797       } else {
798         q = strdup(v);
799         if (q == NULL) {
800           cout << "error" << endl;
801           return -1;
802         }
803         i = strtol(q, &p, 10);
804         if (q == p) {
805           SAFE_FREE(q);
806         }
807         SAFE_FREE(q);
808         if (i <= 0) {
809           cout << "invalid error" << endl;
810           return -1;
811         }
812 
813         options->port = i & 0xffff;
814       }
815       break;
816     case SSH_OPTIONS_USER:
817       v = static_cast<const char *>(value);
818       SAFE_FREE(options->username);
819       if (v == NULL) {
820         q = ssh_get_local_username();
821         if (q == NULL) {
822           cout << "error" << endl;
823           return -1;
824         }
825         options->username = q;
826       } else if (v[0] == '\0') {
827         cout << "invalid error" << endl;
828         return -1;
829       } else { /* username provided */
830         options->username = strdup(static_cast<const char *>(value));
831         if (options->username == NULL) {
832           cout << "error" << endl;
833           return -1;
834         }
835       }
836       break;
837     case SSH_OPTIONS_PROXYJUMP:
838       v = static_cast<const char *>(value);
839       SAFE_FREE(options->ProxyJump);
840       if (v == NULL || v[0] == '\0') {
841         cout << "invalid error" << endl;
842         return -1;
843       } else { /* ProxyJump provided */
844         options->ProxyJump = strdup(static_cast<const char *>(value));
845         if (options->ProxyJump == NULL) {
846           cout << "error" << endl;
847           return -1;
848         }
849       }
850       break;
851     case SSH_OPTIONS_KNOWNHOSTS:
852       v = static_cast<const char *>(value);
853       SAFE_FREE(options->knownhosts);
854       if (v == NULL) {
855         options->knownhosts = ssh_path_expand_escape(options, "%d/known_hosts");
856         if (options->knownhosts == NULL) {
857           cout << "error" << endl;
858           return -1;
859         }
860       } else if (v[0] == '\0') {
861         cout << "invalid error" << endl;
862         return -1;
863       } else {
864         options->knownhosts = strdup(v);
865         if (options->knownhosts == NULL) {
866           cout << "error" << endl;
867           return -1;
868         }
869       }
870       break;
871     case SSH_OPTIONS_TIMEOUT:
872       if (value == NULL) {
873         cout << "invalid error" << endl;
874         return -1;
875       } else {
876         long *x = (long *)value;
877         if (*x < 0) {
878           cout << "invalid error" << endl;
879           return -1;
880         }
881 
882         options->timeout = *x & 0xffffffff;
883       }
884       break;
885     case SSH_OPTIONS_SSH1:
886       if (value == NULL) {
887         cout << "invalid error" << endl;
888         return -1;
889       } else {
890         int *x = (int *)value;
891         if (*x < 0) {
892           cout << "invalid error" << endl;
893           return -1;
894         }
895 
896         options->ssh1 = *x;
897       }
898       break;
899     case SSH_OPTIONS_SSH2:
900       if (value == NULL) {
901         cout << "invalid error" << endl;
902         return -1;
903       } else {
904         int *x = (int *)value;
905         if (*x < 0) {
906           cout << "invalid error" << endl;
907           return -1;
908         }
909 
910         options->ssh2 = *x & 0xffff;
911       }
912       break;
913     case SSH_OPTIONS_STRICTHOSTKEYCHECK:
914       if (value == NULL) {
915         cout << "invalid error" << endl;
916         return -1;
917       } else {
918         int *x = (int *)value;
919 
920         options->StrictHostKeyChecking = (*x & 0xff) > 0 ? 1 : 0;
921       }
922       options->StrictHostKeyChecking = *(int *)value;
923       break;
924     case SSH_OPTIONS_PROXYCOMMAND:
925       v = static_cast<const char *>(value);
926       if (v == NULL || v[0] == '\0') {
927         cout << "invalid error" << endl;
928         return -1;
929       } else {
930         SAFE_FREE(options->ProxyCommand);
931         /* Setting the command to 'none' disables this option. */
932         rc = strcasecmp(v, "none");
933         if (rc != 0) {
934           q = strdup(v);
935           if (q == NULL) {
936             return -1;
937           }
938           options->ProxyCommand = q;
939         }
940       }
941       break;
942     case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY:
943       v = static_cast<const char *>(value);
944       if (v == NULL || v[0] == '\0') {
945         cout << "invalid error" << endl;
946         return -1;
947       } else {
948         SAFE_FREE(options->gss_server_identity);
949         options->gss_server_identity = strdup(v);
950         if (options->gss_server_identity == NULL) {
951           cout << "error" << endl;
952           return -1;
953         }
954       }
955       break;
956     case SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY:
957       v = static_cast<const char *>(value);
958       if (v == NULL || v[0] == '\0') {
959         cout << "invalid error" << endl;
960         return -1;
961       } else {
962         SAFE_FREE(options->gss_client_identity);
963         options->gss_client_identity = strdup(v);
964         if (options->gss_client_identity == NULL) {
965           cout << "error" << endl;
966           return -1;
967         }
968       }
969       break;
970     case SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS:
971       if (value == NULL) {
972         cout << "invalid error" << endl;
973         return -1;
974       } else {
975         int x = *(int *)value;
976 
977         options->gss_delegate_creds = (x & 0xff);
978       }
979       break;
980 
981     default:
982       cout << "Unknown ssh option" << endl;
983       return -1;
984       break;
985   }
986 
987   return 0;
988 }
989 
ssh_config_get_cmd(char ** str)990 static char *ssh_config_get_cmd(char **str) {
991   char *c;
992   char *r;
993 
994   /* Ignore leading spaces */
995   for (c = *str; *c; c++) {
996     if (!isblank(*c)) {
997       break;
998     }
999   }
1000 
1001   if (*c == '\"') {
1002     for (r = ++c; *c; c++) {
1003       if (*c == '\"') {
1004         *c = '\0';
1005         goto out;
1006       }
1007     }
1008   }
1009 
1010   for (r = c; *c; c++) {
1011     if (*c == '\n') {
1012       *c = '\0';
1013       goto out;
1014     }
1015   }
1016 
1017 out:
1018   *str = c + 1;
1019 
1020   return r;
1021 }
1022 
ssh_config_get_token(char ** str)1023 static char *ssh_config_get_token(char **str) {
1024   char *c;
1025   char *r;
1026 
1027   c = ssh_config_get_cmd(str);
1028 
1029   for (r = c; *c; c++) {
1030     if (isblank(*c) || *c == '=') {
1031       *c = '\0';
1032       goto out;
1033     }
1034   }
1035 
1036 out:
1037   *str = c + 1;
1038 
1039   return r;
1040 }
1041 
ssh_config_get_int(char ** str,int notfound)1042 static int ssh_config_get_int(char **str, int notfound) {
1043   char *p, *endp;
1044   int i;
1045 
1046   p = ssh_config_get_token(str);
1047   if (p && *p) {
1048     i = strtol(p, &endp, 10);
1049     if (p == endp) {
1050       return notfound;
1051     }
1052     return i;
1053   }
1054 
1055   return notfound;
1056 }
1057 
ssh_config_get_str_tok(char ** str,const char * def)1058 static const char *ssh_config_get_str_tok(char **str, const char *def) {
1059   char *p;
1060 
1061   p = ssh_config_get_token(str);
1062   if (p && *p) {
1063     return p;
1064   }
1065 
1066   return def;
1067 }
1068 
ssh_config_get_yesno(char ** str,int notfound)1069 static int ssh_config_get_yesno(char **str, int notfound) {
1070   const char *p;
1071 
1072   p = ssh_config_get_str_tok(str, NULL);
1073   if (p == NULL) {
1074     return notfound;
1075   }
1076 
1077   if (strncasecmp(p, "yes", 3) == 0) {
1078     return 1;
1079   } else if (strncasecmp(p, "no", 2) == 0) {
1080     return 0;
1081   }
1082 
1083   return notfound;
1084 }
1085 
local_parse_file(struct Options * options,const char * filename,int * parsing,int seen[])1086 static void local_parse_file(struct Options *options, const char *filename,
1087                              int *parsing, int seen[]) {
1088   char *line = NULL;
1089   size_t len = 0;
1090   ssize_t read = 0;
1091 
1092   unsigned int count = 0;
1093 
1094   FILE *fp;
1095   fp = fopen(filename, "r");
1096   if (fp == NULL) {
1097     LOG(INFO) << filename << "not found";
1098     return;
1099   }
1100 
1101   while ((read = getline(&line, &len, fp)) != -1) {
1102     count++;
1103     if (ssh_config_parse_line(options, line, count, parsing, seen) < 0) {
1104       fclose(fp);
1105       return;
1106     }
1107   }
1108   fclose(fp);
1109   return;
1110 }
1111 
ssh_config_parse_line(struct Options * options,const char * line,unsigned int count,int * parsing,int seen[])1112 static int ssh_config_parse_line(struct Options *options, const char *line,
1113                                  unsigned int count, int *parsing, int seen[]) {
1114   enum ssh_config_opcode_e opcode;
1115   const char *p;
1116   char *s, *x;
1117   char *keyword;
1118   char *lowerhost;
1119   size_t len;
1120   int i;
1121 
1122   x = s = strdup(line);
1123   if (s == NULL) {
1124     cout << "error reading file" << endl;
1125     return -1;
1126   }
1127 
1128   /* Remove trailing spaces */
1129   for (len = strlen(s) - 1; len > 0; len--) {
1130     if (!isspace(s[len])) {
1131       break;
1132     }
1133     s[len] = '\0';
1134   }
1135 
1136   keyword = ssh_config_get_token(&s);
1137   if (keyword == NULL || *keyword == '#' || *keyword == '\0' ||
1138       *keyword == '\n') {
1139     SAFE_FREE(x);
1140     return 0;
1141   }
1142 
1143   opcode = ssh_config_get_opcode(keyword);
1144   if (*parsing == 1 && opcode != SOC_HOST && opcode != SOC_UNSUPPORTED &&
1145       opcode != SOC_INCLUDE) {
1146     if (seen[opcode] != 0) {
1147       SAFE_FREE(x);
1148       return 0;
1149     }
1150     seen[opcode] = 1;
1151   }
1152 
1153   switch (opcode) {
1154     case SOC_INCLUDE: /* recursive include of other files */
1155 
1156       p = ssh_config_get_str_tok(&s, NULL);
1157       if (p && *parsing) {
1158         local_parse_file(options, p, parsing, seen);
1159       }
1160       break;
1161     case SOC_HOST: {
1162       int ok = 0;
1163 
1164       *parsing = 0;
1165       lowerhost = (options->host) ? ssh_lowercase(options->host) : NULL;
1166       for (p = ssh_config_get_str_tok(&s, NULL); p != NULL && p[0] != '\0';
1167            p = ssh_config_get_str_tok(&s, NULL)) {
1168         if (ok >= 0) {
1169           ok = match_hostname(lowerhost, p, strlen(p));
1170           if (ok < 0) {
1171             *parsing = 0;
1172           } else if (ok > 0) {
1173             *parsing = 1;
1174           }
1175         }
1176       }
1177       SAFE_FREE(lowerhost);
1178       break;
1179     }
1180     case SOC_HOSTNAME:
1181       p = ssh_config_get_str_tok(&s, NULL);
1182       if (p && *parsing) {
1183         char *z = ssh_path_expand_escape(options, p);
1184         if (z == NULL) {
1185           z = strdup(p);
1186         }
1187         ssh_options_set(options, SSH_OPTIONS_HOST, z);
1188         free(z);
1189       }
1190       break;
1191     case SOC_PORT:
1192       if (options->port == 0) {
1193         p = ssh_config_get_str_tok(&s, NULL);
1194         if (p && *parsing) {
1195           ssh_options_set(options, SSH_OPTIONS_PORT_STR, p);
1196         }
1197       }
1198       break;
1199     case SOC_USERNAME:
1200       if (options->username == NULL) {
1201         p = ssh_config_get_str_tok(&s, NULL);
1202         if (p && *parsing) {
1203           ssh_options_set(options, SSH_OPTIONS_USER, p);
1204         }
1205       }
1206       break;
1207     case SOC_PROXYJUMP:
1208       if (options->ProxyJump == NULL) {
1209         p = ssh_config_get_str_tok(&s, NULL);
1210         if (p && *parsing) {
1211           ssh_options_set(options, SSH_OPTIONS_PROXYJUMP, p);
1212         }
1213       }
1214       break;
1215     case SOC_PROTOCOL:
1216       p = ssh_config_get_str_tok(&s, NULL);
1217       if (p && *parsing) {
1218         char *a, *b;
1219         b = strdup(p);
1220         if (b == NULL) {
1221           SAFE_FREE(x);
1222           cout << "error" << endl;
1223           return -1;
1224         }
1225         i = 0;
1226         ssh_options_set(options, SSH_OPTIONS_SSH1, &i);
1227         ssh_options_set(options, SSH_OPTIONS_SSH2, &i);
1228 
1229         for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
1230           switch (atoi(a)) {
1231             case 1:
1232               i = 1;
1233               ssh_options_set(options, SSH_OPTIONS_SSH1, &i);
1234               break;
1235             case 2:
1236               i = 1;
1237               ssh_options_set(options, SSH_OPTIONS_SSH2, &i);
1238               break;
1239             default:
1240               break;
1241           }
1242         }
1243         SAFE_FREE(b);
1244       }
1245       break;
1246     case SOC_TIMEOUT:
1247       i = ssh_config_get_int(&s, -1);
1248       if (i >= 0 && *parsing) {
1249         ssh_options_set(options, SSH_OPTIONS_TIMEOUT, &i);
1250       }
1251       break;
1252     case SOC_STRICTHOSTKEYCHECK:
1253       i = ssh_config_get_yesno(&s, -1);
1254       if (i >= 0 && *parsing) {
1255         ssh_options_set(options, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
1256       }
1257       break;
1258     case SOC_KNOWNHOSTS:
1259       p = ssh_config_get_str_tok(&s, NULL);
1260       if (p && *parsing) {
1261         ssh_options_set(options, SSH_OPTIONS_KNOWNHOSTS, p);
1262       }
1263       break;
1264     case SOC_PROXYCOMMAND:
1265       p = ssh_config_get_cmd(&s);
1266       if (p && *parsing) {
1267         ssh_options_set(options, SSH_OPTIONS_PROXYCOMMAND, p);
1268       }
1269       break;
1270     case SOC_GSSAPISERVERIDENTITY:
1271       p = ssh_config_get_str_tok(&s, NULL);
1272       if (p && *parsing) {
1273         ssh_options_set(options, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
1274       }
1275       break;
1276     case SOC_GSSAPICLIENTIDENTITY:
1277       p = ssh_config_get_str_tok(&s, NULL);
1278       if (p && *parsing) {
1279         ssh_options_set(options, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
1280       }
1281       break;
1282     case SOC_GSSAPIDELEGATECREDENTIALS:
1283       i = ssh_config_get_yesno(&s, -1);
1284       if (i >= 0 && *parsing) {
1285         ssh_options_set(options, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
1286       }
1287       break;
1288     case SOC_UNSUPPORTED:
1289       LOG(INFO) << "unsupported config line: " << string(line) << ", ignored";
1290       break;
1291     default:
1292       cout << "parse error" << endl;
1293       SAFE_FREE(x);
1294       return -1;
1295       break;
1296   }
1297 
1298   SAFE_FREE(x);
1299   return 0;
1300 }
1301 
parse_ssh_config_file(struct Options * options,string filename)1302 int parse_ssh_config_file(struct Options *options, string filename) {
1303   char *line = NULL;
1304   size_t len = 0;
1305   ssize_t read = 0;
1306   unsigned int count = 0;
1307   int parsing;
1308   int seen[SOC_END - SOC_UNSUPPORTED] = {0};
1309 
1310   FILE *fp;
1311   fp = fopen(filename.c_str(), "r");
1312   if (fp == NULL) {
1313     LOG(INFO) << filename << "not found";
1314     return 0;
1315   }
1316 
1317   parsing = 1;
1318 
1319   while ((read = getline(&line, &len, fp)) != -1) {
1320     count++;
1321     if (ssh_config_parse_line(options, line, count, &parsing, seen) < 0) {
1322       fclose(fp);
1323       return -1;
1324     }
1325   }
1326   fclose(fp);
1327   return 0;
1328 }
1329