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