summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--readconf.c227
-rw-r--r--readconf.h12
-rw-r--r--ssh-keysign.c4
-rw-r--r--ssh.c22
-rw-r--r--ssh_config.552
6 files changed, 287 insertions, 34 deletions
diff --git a/ChangeLog b/ChangeLog
index 5f704f1f9..2b0ca0b8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -33,6 +33,10 @@
33 [session.c session.h] 33 [session.c session.h]
34 Add logging of session starts in a useful format; ok markus@ feedback and 34 Add logging of session starts in a useful format; ok markus@ feedback and
35 ok dtucker@ 35 ok dtucker@
36 - djm@cvs.openbsd.org 2013/10/14 22:22:05
37 [readconf.c readconf.h ssh-keysign.c ssh.c ssh_config.5]
38 add a "Match" keyword to ssh_config that allows matching on hostname,
39 user and result of arbitrary commands. "nice work" markus@
36 40
3720131010 4120131010
38 - (dtucker) OpenBSD CVS Sync 42 - (dtucker) OpenBSD CVS Sync
diff --git a/readconf.c b/readconf.c
index 7450081cd..f7b912ef6 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.205 2013/08/20 00:11:37 djm Exp $ */ 1/* $OpenBSD: readconf.c,v 1.206 2013/10/14 22:22:02 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -17,6 +17,7 @@
17#include <sys/types.h> 17#include <sys/types.h>
18#include <sys/stat.h> 18#include <sys/stat.h>
19#include <sys/socket.h> 19#include <sys/socket.h>
20#include <sys/wait.h>
20 21
21#include <netinet/in.h> 22#include <netinet/in.h>
22#include <netinet/in_systm.h> 23#include <netinet/in_systm.h>
@@ -24,7 +25,10 @@
24 25
25#include <ctype.h> 26#include <ctype.h>
26#include <errno.h> 27#include <errno.h>
28#include <fcntl.h>
27#include <netdb.h> 29#include <netdb.h>
30#include <paths.h>
31#include <pwd.h>
28#include <signal.h> 32#include <signal.h>
29#include <stdarg.h> 33#include <stdarg.h>
30#include <stdio.h> 34#include <stdio.h>
@@ -47,6 +51,7 @@
47#include "buffer.h" 51#include "buffer.h"
48#include "kex.h" 52#include "kex.h"
49#include "mac.h" 53#include "mac.h"
54#include "uidswap.h"
50 55
51/* Format of the configuration file: 56/* Format of the configuration file:
52 57
@@ -115,12 +120,13 @@
115 120
116typedef enum { 121typedef enum {
117 oBadOption, 122 oBadOption,
123 oHost, oMatch,
118 oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, 124 oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
119 oGatewayPorts, oExitOnForwardFailure, 125 oGatewayPorts, oExitOnForwardFailure,
120 oPasswordAuthentication, oRSAAuthentication, 126 oPasswordAuthentication, oRSAAuthentication,
121 oChallengeResponseAuthentication, oXAuthLocation, 127 oChallengeResponseAuthentication, oXAuthLocation,
122 oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, 128 oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
123 oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, 129 oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
124 oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, 130 oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
125 oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, 131 oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
126 oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, 132 oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
@@ -194,6 +200,7 @@ static struct {
194 { "localforward", oLocalForward }, 200 { "localforward", oLocalForward },
195 { "user", oUser }, 201 { "user", oUser },
196 { "host", oHost }, 202 { "host", oHost },
203 { "match", oMatch },
197 { "escapechar", oEscapeChar }, 204 { "escapechar", oEscapeChar },
198 { "globalknownhostsfile", oGlobalKnownHostsFile }, 205 { "globalknownhostsfile", oGlobalKnownHostsFile },
199 { "globalknownhostsfile2", oDeprecated }, 206 { "globalknownhostsfile2", oDeprecated },
@@ -349,10 +356,188 @@ add_identity_file(Options *options, const char *dir, const char *filename,
349 options->identity_files[options->num_identity_files++] = path; 356 options->identity_files[options->num_identity_files++] = path;
350} 357}
351 358
359int
360default_ssh_port(void)
361{
362 static int port;
363 struct servent *sp;
364
365 if (port == 0) {
366 sp = getservbyname(SSH_SERVICE_NAME, "tcp");
367 port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
368 }
369 return port;
370}
371
352/* 372/*
353 * Returns the number of the token pointed to by cp or oBadOption. 373 * Execute a command in a shell.
374 * Return its exit status or -1 on abnormal exit.
354 */ 375 */
376static int
377execute_in_shell(const char *cmd)
378{
379 char *shell, *command_string;
380 pid_t pid;
381 int devnull, status;
382 extern uid_t original_real_uid;
355 383
384 if ((shell = getenv("SHELL")) == NULL)
385 shell = _PATH_BSHELL;
386
387 /*
388 * Use "exec" to avoid "sh -c" processes on some platforms
389 * (e.g. Solaris)
390 */
391 xasprintf(&command_string, "exec %s", cmd);
392
393 /* Need this to redirect subprocess stdin/out */
394 if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
395 fatal("open(/dev/null): %s", strerror(errno));
396
397 debug("Executing command: '%.500s'", cmd);
398
399 /* Fork and execute the command. */
400 if ((pid = fork()) == 0) {
401 char *argv[4];
402
403 /* Child. Permanently give up superuser privileges. */
404 permanently_drop_suid(original_real_uid);
405
406 /* Redirect child stdin and stdout. Leave stderr */
407 if (dup2(devnull, STDIN_FILENO) == -1)
408 fatal("dup2: %s", strerror(errno));
409 if (dup2(devnull, STDOUT_FILENO) == -1)
410 fatal("dup2: %s", strerror(errno));
411 if (devnull > STDERR_FILENO)
412 close(devnull);
413 closefrom(STDERR_FILENO + 1);
414
415 argv[0] = shell;
416 argv[1] = "-c";
417 argv[2] = command_string;
418 argv[3] = NULL;
419
420 execv(argv[0], argv);
421 error("Unable to execute '%.100s': %s", cmd, strerror(errno));
422 /* Die with signal to make this error apparent to parent. */
423 signal(SIGTERM, SIG_DFL);
424 kill(getpid(), SIGTERM);
425 _exit(1);
426 }
427 /* Parent. */
428 if (pid < 0)
429 fatal("%s: fork: %.100s", __func__, strerror(errno));
430
431 close(devnull);
432 free(command_string);
433
434 while (waitpid(pid, &status, 0) == -1) {
435 if (errno != EINTR && errno != EAGAIN)
436 fatal("%s: waitpid: %s", __func__, strerror(errno));
437 }
438 if (!WIFEXITED(status)) {
439 error("command '%.100s' exited abnormally", cmd);
440 return -1;
441 }
442 debug3("command returned status %d", WEXITSTATUS(status));
443 return WEXITSTATUS(status);
444}
445
446/*
447 * Parse and execute a Match directive.
448 */
449static int
450match_cfg_line(Options *options, char **condition, struct passwd *pw,
451 const char *host_arg, const char *filename, int linenum)
452{
453 char *arg, *attrib, *cmd, *cp = *condition;
454 const char *ruser, *host;
455 int r, port, result = 1;
456 size_t len;
457 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
458
459 /*
460 * Configuration is likely to be incomplete at this point so we
461 * must be prepared to use default values.
462 */
463 port = options->port <= 0 ? default_ssh_port() : options->port;
464 ruser = options->user == NULL ? pw->pw_name : options->user;
465 host = options->hostname == NULL ? host_arg : options->hostname;
466
467 debug3("checking match for '%s' host %s", cp, host);
468 while ((attrib = strdelim(&cp)) && *attrib != '\0') {
469 if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
470 error("Missing Match criteria for %s", attrib);
471 return -1;
472 }
473 len = strlen(arg);
474 if (strcasecmp(attrib, "host") == 0) {
475 if (match_hostname(host, arg, len) != 1)
476 result = 0;
477 else
478 debug("%.200s line %d: matched 'Host %.100s' ",
479 filename, linenum, host);
480 } else if (strcasecmp(attrib, "originalhost") == 0) {
481 if (match_hostname(host_arg, arg, len) != 1)
482 result = 0;
483 else
484 debug("%.200s line %d: matched "
485 "'OriginalHost %.100s' ",
486 filename, linenum, host_arg);
487 } else if (strcasecmp(attrib, "user") == 0) {
488 if (match_pattern_list(ruser, arg, len, 0) != 1)
489 result = 0;
490 else
491 debug("%.200s line %d: matched 'User %.100s' ",
492 filename, linenum, ruser);
493 } else if (strcasecmp(attrib, "localuser") == 0) {
494 if (match_pattern_list(pw->pw_name, arg, len, 0) != 1)
495 result = 0;
496 else
497 debug("%.200s line %d: matched "
498 "'LocalUser %.100s' ",
499 filename, linenum, pw->pw_name);
500 } else if (strcasecmp(attrib, "command") == 0) {
501 if (gethostname(thishost, sizeof(thishost)) == -1)
502 fatal("gethostname: %s", strerror(errno));
503 strlcpy(shorthost, thishost, sizeof(shorthost));
504 shorthost[strcspn(thishost, ".")] = '\0';
505 snprintf(portstr, sizeof(portstr), "%d", port);
506
507 cmd = percent_expand(arg,
508 "L", shorthost,
509 "d", pw->pw_dir,
510 "h", host,
511 "l", thishost,
512 "n", host_arg,
513 "p", portstr,
514 "r", ruser,
515 "u", pw->pw_name,
516 (char *)NULL);
517 r = execute_in_shell(cmd);
518 if (r == -1) {
519 fatal("%.200s line %d: match command '%.100s' "
520 "error", filename, linenum, cmd);
521 } else if (r == 0) {
522 debug("%.200s line %d: matched "
523 "'Command \"%.100s\"' ",
524 filename, linenum, cmd);
525 } else
526 result = 0;
527 free(cmd);
528 } else {
529 error("Unsupported Match attribute %s", attrib);
530 return -1;
531 }
532 }
533 debug3("match %sfound", result ? "" : "not ");
534 *condition = cp;
535 return result;
536}
537
538/*
539 * Returns the number of the token pointed to by cp or oBadOption.
540 */
356static OpCodes 541static OpCodes
357parse_token(const char *cp, const char *filename, int linenum, 542parse_token(const char *cp, const char *filename, int linenum,
358 const char *ignored_unknown) 543 const char *ignored_unknown)
@@ -375,21 +560,24 @@ parse_token(const char *cp, const char *filename, int linenum,
375 * only sets those values that have not already been set. 560 * only sets those values that have not already been set.
376 */ 561 */
377#define WHITESPACE " \t\r\n" 562#define WHITESPACE " \t\r\n"
378
379int 563int
380process_config_line(Options *options, const char *host, 564process_config_line(Options *options, struct passwd *pw, const char *host,
381 char *line, const char *filename, int linenum, 565 char *line, const char *filename, int linenum, int *activep, int userconfig)
382 int *activep, int userconfig)
383{ 566{
384 char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; 567 char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
385 char **cpptr, fwdarg[256]; 568 char **cpptr, fwdarg[256];
386 u_int i, *uintptr, max_entries = 0; 569 u_int i, *uintptr, max_entries = 0;
387 int negated, opcode, *intptr, value, value2; 570 int negated, opcode, *intptr, value, value2, cmdline = 0;
388 LogLevel *log_level_ptr; 571 LogLevel *log_level_ptr;
389 long long val64; 572 long long val64;
390 size_t len; 573 size_t len;
391 Forward fwd; 574 Forward fwd;
392 575
576 if (activep == NULL) { /* We are processing a command line directive */
577 cmdline = 1;
578 activep = &cmdline;
579 }
580
393 /* Strip trailing whitespace */ 581 /* Strip trailing whitespace */
394 for (len = strlen(line) - 1; len > 0; len--) { 582 for (len = strlen(line) - 1; len > 0; len--) {
395 if (strchr(WHITESPACE, line[len]) == NULL) 583 if (strchr(WHITESPACE, line[len]) == NULL)
@@ -828,6 +1016,9 @@ parse_int:
828 goto parse_flag; 1016 goto parse_flag;
829 1017
830 case oHost: 1018 case oHost:
1019 if (cmdline)
1020 fatal("Host directive not supported as a command-line "
1021 "option");
831 *activep = 0; 1022 *activep = 0;
832 arg2 = NULL; 1023 arg2 = NULL;
833 while ((arg = strdelim(&s)) != NULL && *arg != '\0') { 1024 while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
@@ -854,6 +1045,18 @@ parse_int:
854 /* Avoid garbage check below, as strdelim is done. */ 1045 /* Avoid garbage check below, as strdelim is done. */
855 return 0; 1046 return 0;
856 1047
1048 case oMatch:
1049 if (cmdline)
1050 fatal("Host directive not supported as a command-line "
1051 "option");
1052 value = match_cfg_line(options, &s, pw, host,
1053 filename, linenum);
1054 if (value < 0)
1055 fatal("%.200s line %d: Bad Match condition", filename,
1056 linenum);
1057 *activep = value;
1058 break;
1059
857 case oEscapeChar: 1060 case oEscapeChar:
858 intptr = &options->escape_char; 1061 intptr = &options->escape_char;
859 arg = strdelim(&s); 1062 arg = strdelim(&s);
@@ -1107,8 +1310,8 @@ parse_int:
1107 */ 1310 */
1108 1311
1109int 1312int
1110read_config_file(const char *filename, const char *host, Options *options, 1313read_config_file(const char *filename, struct passwd *pw, const char *host,
1111 int flags) 1314 Options *options, int flags)
1112{ 1315{
1113 FILE *f; 1316 FILE *f;
1114 char line[1024]; 1317 char line[1024];
@@ -1139,8 +1342,8 @@ read_config_file(const char *filename, const char *host, Options *options,
1139 while (fgets(line, sizeof(line), f)) { 1342 while (fgets(line, sizeof(line), f)) {
1140 /* Update line number counter. */ 1343 /* Update line number counter. */
1141 linenum++; 1344 linenum++;
1142 if (process_config_line(options, host, line, filename, linenum, 1345 if (process_config_line(options, pw, host, line, filename,
1143 &active, flags & SSHCONF_USERCONF) != 0) 1346 linenum, &active, flags & SSHCONF_USERCONF) != 0)
1144 bad_options++; 1347 bad_options++;
1145 } 1348 }
1146 fclose(f); 1349 fclose(f);
diff --git a/readconf.h b/readconf.h
index ca4a042ad..cde8b5242 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.96 2013/08/20 00:11:38 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.97 2013/10/14 22:22:03 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -159,12 +159,12 @@ typedef struct {
159 159
160void initialize_options(Options *); 160void initialize_options(Options *);
161void fill_default_options(Options *); 161void fill_default_options(Options *);
162int read_config_file(const char *, const char *, Options *, int); 162int process_config_line(Options *, struct passwd *, const char *, char *,
163 const char *, int, int *, int);
164int read_config_file(const char *, struct passwd *, const char *,
165 Options *, int);
163int parse_forward(Forward *, const char *, int, int); 166int parse_forward(Forward *, const char *, int, int);
164 167int default_ssh_port(void);
165int
166process_config_line(Options *, const char *, char *, const char *, int, int *,
167 int);
168 168
169void add_local_forward(Options *, const Forward *); 169void add_local_forward(Options *, const Forward *);
170void add_remote_forward(Options *, const Forward *); 170void add_remote_forward(Options *, const Forward *);
diff --git a/ssh-keysign.c b/ssh-keysign.c
index 9a6653c7c..b67ed1ead 100644
--- a/ssh-keysign.c
+++ b/ssh-keysign.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keysign.c,v 1.37 2013/05/17 00:13:14 djm Exp $ */ 1/* $OpenBSD: ssh-keysign.c,v 1.38 2013/10/14 22:22:04 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2002 Markus Friedl. All rights reserved. 3 * Copyright (c) 2002 Markus Friedl. All rights reserved.
4 * 4 *
@@ -187,7 +187,7 @@ main(int argc, char **argv)
187 187
188 /* verify that ssh-keysign is enabled by the admin */ 188 /* verify that ssh-keysign is enabled by the admin */
189 initialize_options(&options); 189 initialize_options(&options);
190 (void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0); 190 (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", &options, 0);
191 fill_default_options(&options); 191 fill_default_options(&options);
192 if (options.enable_ssh_keysign != 1) 192 if (options.enable_ssh_keysign != 1)
193 fatal("ssh-keysign not enabled in %s", 193 fatal("ssh-keysign not enabled in %s",
diff --git a/ssh.c b/ssh.c
index 87233bc91..13f384a92 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.381 2013/07/25 00:29:10 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.382 2013/10/14 22:22:04 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -242,7 +242,7 @@ main(int ac, char **av)
242 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; 242 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
243 struct stat st; 243 struct stat st;
244 struct passwd *pw; 244 struct passwd *pw;
245 int dummy, timeout_ms; 245 int timeout_ms;
246 extern int optind, optreset; 246 extern int optind, optreset;
247 extern char *optarg; 247 extern char *optarg;
248 248
@@ -595,10 +595,9 @@ main(int ac, char **av)
595 options.request_tty = REQUEST_TTY_NO; 595 options.request_tty = REQUEST_TTY_NO;
596 break; 596 break;
597 case 'o': 597 case 'o':
598 dummy = 1;
599 line = xstrdup(optarg); 598 line = xstrdup(optarg);
600 if (process_config_line(&options, host ? host : "", 599 if (process_config_line(&options, pw, host ? host : "",
601 line, "command-line", 0, &dummy, SSHCONF_USERCONF) 600 line, "command-line", 0, NULL, SSHCONF_USERCONF)
602 != 0) 601 != 0)
603 exit(255); 602 exit(255);
604 free(line); 603 free(line);
@@ -703,18 +702,19 @@ main(int ac, char **av)
703 */ 702 */
704 if (config != NULL) { 703 if (config != NULL) {
705 if (strcasecmp(config, "none") != 0 && 704 if (strcasecmp(config, "none") != 0 &&
706 !read_config_file(config, host, &options, SSHCONF_USERCONF)) 705 !read_config_file(config, pw, host, &options,
706 SSHCONF_USERCONF))
707 fatal("Can't open user config file %.100s: " 707 fatal("Can't open user config file %.100s: "
708 "%.100s", config, strerror(errno)); 708 "%.100s", config, strerror(errno));
709 } else { 709 } else {
710 r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, 710 r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
711 _PATH_SSH_USER_CONFFILE); 711 _PATH_SSH_USER_CONFFILE);
712 if (r > 0 && (size_t)r < sizeof(buf)) 712 if (r > 0 && (size_t)r < sizeof(buf))
713 (void)read_config_file(buf, host, &options, 713 (void)read_config_file(buf, pw, host, &options,
714 SSHCONF_CHECKPERM|SSHCONF_USERCONF); 714 SSHCONF_CHECKPERM|SSHCONF_USERCONF);
715 715
716 /* Read systemwide configuration file after user config. */ 716 /* Read systemwide configuration file after user config. */
717 (void)read_config_file(_PATH_HOST_CONFIG_FILE, host, 717 (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host,
718 &options, 0); 718 &options, 0);
719 } 719 }
720 720
@@ -752,10 +752,8 @@ main(int ac, char **av)
752 options.user = xstrdup(pw->pw_name); 752 options.user = xstrdup(pw->pw_name);
753 753
754 /* Get default port if port has not been set. */ 754 /* Get default port if port has not been set. */
755 if (options.port == 0) { 755 if (options.port == 0)
756 sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 756 options.port = default_ssh_port();
757 options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
758 }
759 757
760 /* preserve host name given on command line for %n expansion */ 758 /* preserve host name given on command line for %n expansion */
761 host_arg = host; 759 host_arg = host;
diff --git a/ssh_config.5 b/ssh_config.5
index 9ddd6b8a6..f35f468f4 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh_config.5,v 1.168 2013/08/20 06:56:07 jmc Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.169 2013/10/14 22:22:05 djm Exp $
37.Dd $Mdocdate: August 20 2013 $ 37.Dd $Mdocdate: October 14 2013 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -100,6 +100,8 @@ keywords are case-insensitive and arguments are case-sensitive):
100.It Cm Host 100.It Cm Host
101Restricts the following declarations (up to the next 101Restricts the following declarations (up to the next
102.Cm Host 102.Cm Host
103or
104.Cm Match
103keyword) to be only for those hosts that match one of the patterns 105keyword) to be only for those hosts that match one of the patterns
104given after the keyword. 106given after the keyword.
105If more than one pattern is provided, they should be separated by whitespace. 107If more than one pattern is provided, they should be separated by whitespace.
@@ -124,6 +126,52 @@ matches.
124See 126See
125.Sx PATTERNS 127.Sx PATTERNS
126for more information on patterns. 128for more information on patterns.
129.It Cm Match
130Restricts the following declarations (up to the next
131.Cm Host
132or
133.Cm Match
134keyword) to be used only when the conditions following the
135.Cm Match
136keyword are satisfied.
137Match conditions are specified using one or more keyword/criteria pairs.
138The available keywords are:
139.Cm command ,
140.Cm host ,
141.Cm originalhost ,
142.Cm user ,
143and
144.Cm localuser .
145.Pp
146The criteria for the
147.Cm command
148keyword is a path to a command that is executed.
149If the command returns a zero exit status then the condition is considered true.
150Commands containing whitespace characters must be quoted.
151.Pp
152The other keywords' criteria must be single entries or comma-separated
153lists and may use the wildcard and negation operators described in the
154.Sx PATTERNS
155section.
156The criteria for the
157.Cm host
158keyword are matched against the target hostname, after any substitution
159by the
160.Cm Hostname
161option.
162The
163.Cm originalhost
164keyword matches against the hostname as it was specified on the command-line.
165The
166.Cm user
167keyword matches against the target username on the remote host.
168The
169.Cm localuser
170keyword matches against the name of the local user running
171.Xr ssh 1
172(this keyword may be useful in system-wide
173.Nm
174files).
127.It Cm AddressFamily 175.It Cm AddressFamily
128Specifies which address family to use when connecting. 176Specifies which address family to use when connecting.
129Valid arguments are 177Valid arguments are