]> andersk Git - openssh.git/blame - servconf.c
- stevesk@cvs.openbsd.org 2008/07/01 23:12:47
[openssh.git] / servconf.c
CommitLineData
8086aeb2 1/* $OpenBSD: servconf.c,v 1.184 2008/06/15 16:58:40 dtucker Exp $ */
8efc0c15 2/*
5260325f 3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved
6ae2364d 5 *
bcbf86ec 6 * As far as I am concerned, the code I have written for this software
7 * can be used freely for any purpose. Any derived versions of this
8 * software must be clearly marked as such, and if the derived work is
9 * incompatible with the protocol description in the RFC file, it must be
10 * called by a name other than "ssh" or "Secure Shell".
5260325f 11 */
8efc0c15 12
13#include "includes.h"
8efc0c15 14
5b04a8bf 15#include <sys/types.h>
16#include <sys/socket.h>
17
28cb0a43 18#include <netdb.h>
fa47fe3c 19#include <pwd.h>
cf851879 20#include <stdio.h>
ffa517a8 21#include <stdlib.h>
00146caa 22#include <string.h>
31652869 23#include <signal.h>
5188ba17 24#include <unistd.h>
31652869 25#include <stdarg.h>
1760c982 26#include <errno.h>
c8dfff33 27
3593bdc0 28#include "openbsd-compat/sys-queue.h"
31652869 29#include "xmalloc.h"
8efc0c15 30#include "ssh.h"
42f11eb2 31#include "log.h"
31652869 32#include "buffer.h"
8efc0c15 33#include "servconf.h"
a8be9f80 34#include "compat.h"
42f11eb2 35#include "pathnames.h"
42f11eb2 36#include "misc.h"
37#include "cipher.h"
31652869 38#include "key.h"
b2552997 39#include "kex.h"
40#include "mac.h"
d231781a 41#include "match.h"
2fefbadf 42#include "channels.h"
fa47fe3c 43#include "groupaccess.h"
42f11eb2 44
396c147e 45static void add_listen_addr(ServerOptions *, char *, u_short);
46static void add_one_listen_addr(ServerOptions *, char *, u_short);
48e671d5 47
1853d1ef 48/* Use of privilege separation or not */
49extern int use_privsep;
d231781a 50extern Buffer cfg;
42f11eb2 51
8efc0c15 52/* Initializes the server options to their default values. */
53
6ae2364d 54void
5260325f 55initialize_server_options(ServerOptions *options)
8efc0c15 56{
5260325f 57 memset(options, 0, sizeof(*options));
e15895cd 58
59 /* Portable-specific options */
7fceb20d 60 options->use_pam = -1;
e15895cd 61
62 /* Standard Options */
48e671d5 63 options->num_ports = 0;
64 options->ports_from_cmdline = 0;
65 options->listen_addrs = NULL;
31b41ceb 66 options->address_family = -1;
fa08c86b 67 options->num_host_key_files = 0;
0fbe8c74 68 options->pid_file = NULL;
5260325f 69 options->server_key_bits = -1;
70 options->login_grace_time = -1;
71 options->key_regeneration_time = -1;
15853e93 72 options->permit_root_login = PERMIT_NOT_SET;
5260325f 73 options->ignore_rhosts = -1;
74 options->ignore_user_known_hosts = -1;
75 options->print_motd = -1;
4f4648f9 76 options->print_lastlog = -1;
5260325f 77 options->x11_forwarding = -1;
78 options->x11_display_offset = -1;
e6e573bd 79 options->x11_use_localhost = -1;
fa649821 80 options->xauth_location = NULL;
5260325f 81 options->strict_modes = -1;
fd573618 82 options->tcp_keep_alive = -1;
5eaf8578 83 options->log_facility = SYSLOG_FACILITY_NOT_SET;
84 options->log_level = SYSLOG_LEVEL_NOT_SET;
5260325f 85 options->rhosts_rsa_authentication = -1;
8002af61 86 options->hostbased_authentication = -1;
87 options->hostbased_uses_name_from_packet_only = -1;
5260325f 88 options->rsa_authentication = -1;
fa08c86b 89 options->pubkey_authentication = -1;
5260325f 90 options->kerberos_authentication = -1;
91 options->kerberos_or_local_passwd = -1;
92 options->kerberos_ticket_cleanup = -1;
a1e30b47 93 options->kerberos_get_afs_token = -1;
7364bd04 94 options->gss_authentication=-1;
95 options->gss_cleanup_creds = -1;
5260325f 96 options->password_authentication = -1;
94ec8c6b 97 options->kbd_interactive_authentication = -1;
5ba55ada 98 options->challenge_response_authentication = -1;
5260325f 99 options->permit_empty_passwd = -1;
f00bab84 100 options->permit_user_env = -1;
5260325f 101 options->use_login = -1;
636f76ca 102 options->compression = -1;
33de75a3 103 options->allow_tcp_forwarding = -1;
43c3f85c 104 options->allow_agent_forwarding = -1;
5260325f 105 options->num_allow_users = 0;
106 options->num_deny_users = 0;
107 options->num_allow_groups = 0;
108 options->num_deny_groups = 0;
a8be9f80 109 options->ciphers = NULL;
b2552997 110 options->macs = NULL;
a8be9f80 111 options->protocol = SSH_PROTO_UNKNOWN;
1d1ffb87 112 options->gateway_ports = -1;
38c295d6 113 options->num_subsystems = 0;
c345cf9d 114 options->max_startups_begin = -1;
115 options->max_startups_rate = -1;
089fbbd2 116 options->max_startups = -1;
af4bd935 117 options->max_authtries = -1;
c6dca55e 118 options->max_sessions = -1;
eea39c02 119 options->banner = NULL;
c5a7d788 120 options->use_dns = -1;
3ffc6336 121 options->client_alive_interval = -1;
122 options->client_alive_count_max = -1;
c8445989 123 options->authorized_keys_file = NULL;
124 options->authorized_keys_file2 = NULL;
61a2c1da 125 options->num_accept_env = 0;
d20f3c9e 126 options->permit_tun = -1;
ea46e550 127 options->num_permitted_opens = -1;
e7259e8d 128 options->adm_forced_command = NULL;
db49deeb 129 options->chroot_directory = NULL;
8efc0c15 130}
131
6ae2364d 132void
5260325f 133fill_default_server_options(ServerOptions *options)
8efc0c15 134{
e15895cd 135 /* Portable-specific options */
7fceb20d 136 if (options->use_pam == -1)
0a23d79f 137 options->use_pam = 0;
e15895cd 138
139 /* Standard Options */
fa08c86b 140 if (options->protocol == SSH_PROTO_UNKNOWN)
141 options->protocol = SSH_PROTO_1|SSH_PROTO_2;
142 if (options->num_host_key_files == 0) {
143 /* fill default hostkeys for protocols */
144 if (options->protocol & SSH_PROTO_1)
0f84fe37 145 options->host_key_files[options->num_host_key_files++] =
146 _PATH_HOST_KEY_FILE;
147 if (options->protocol & SSH_PROTO_2) {
148 options->host_key_files[options->num_host_key_files++] =
149 _PATH_HOST_RSA_KEY_FILE;
150 options->host_key_files[options->num_host_key_files++] =
151 _PATH_HOST_DSA_KEY_FILE;
152 }
fa08c86b 153 }
48e671d5 154 if (options->num_ports == 0)
155 options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
156 if (options->listen_addrs == NULL)
2d2a2c65 157 add_listen_addr(options, NULL, 0);
0fbe8c74 158 if (options->pid_file == NULL)
42f11eb2 159 options->pid_file = _PATH_SSH_DAEMON_PID_FILE;
5260325f 160 if (options->server_key_bits == -1)
161 options->server_key_bits = 768;
162 if (options->login_grace_time == -1)
3445ca02 163 options->login_grace_time = 120;
5260325f 164 if (options->key_regeneration_time == -1)
165 options->key_regeneration_time = 3600;
15853e93 166 if (options->permit_root_login == PERMIT_NOT_SET)
167 options->permit_root_login = PERMIT_YES;
5260325f 168 if (options->ignore_rhosts == -1)
c8d54615 169 options->ignore_rhosts = 1;
5260325f 170 if (options->ignore_user_known_hosts == -1)
171 options->ignore_user_known_hosts = 0;
5260325f 172 if (options->print_motd == -1)
173 options->print_motd = 1;
4f4648f9 174 if (options->print_lastlog == -1)
175 options->print_lastlog = 1;
5260325f 176 if (options->x11_forwarding == -1)
c8d54615 177 options->x11_forwarding = 0;
5260325f 178 if (options->x11_display_offset == -1)
c8d54615 179 options->x11_display_offset = 10;
e6e573bd 180 if (options->x11_use_localhost == -1)
181 options->x11_use_localhost = 1;
fa649821 182 if (options->xauth_location == NULL)
fd9ede94 183 options->xauth_location = _PATH_XAUTH;
5260325f 184 if (options->strict_modes == -1)
185 options->strict_modes = 1;
fd573618 186 if (options->tcp_keep_alive == -1)
187 options->tcp_keep_alive = 1;
5eaf8578 188 if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
5260325f 189 options->log_facility = SYSLOG_FACILITY_AUTH;
5eaf8578 190 if (options->log_level == SYSLOG_LEVEL_NOT_SET)
59c97189 191 options->log_level = SYSLOG_LEVEL_INFO;
5260325f 192 if (options->rhosts_rsa_authentication == -1)
c8d54615 193 options->rhosts_rsa_authentication = 0;
8002af61 194 if (options->hostbased_authentication == -1)
195 options->hostbased_authentication = 0;
196 if (options->hostbased_uses_name_from_packet_only == -1)
197 options->hostbased_uses_name_from_packet_only = 0;
5260325f 198 if (options->rsa_authentication == -1)
199 options->rsa_authentication = 1;
fa08c86b 200 if (options->pubkey_authentication == -1)
201 options->pubkey_authentication = 1;
5260325f 202 if (options->kerberos_authentication == -1)
eadc806d 203 options->kerberos_authentication = 0;
5260325f 204 if (options->kerberos_or_local_passwd == -1)
205 options->kerberos_or_local_passwd = 1;
206 if (options->kerberos_ticket_cleanup == -1)
207 options->kerberos_ticket_cleanup = 1;
a1e30b47 208 if (options->kerberos_get_afs_token == -1)
209 options->kerberos_get_afs_token = 0;
7364bd04 210 if (options->gss_authentication == -1)
211 options->gss_authentication = 0;
212 if (options->gss_cleanup_creds == -1)
213 options->gss_cleanup_creds = 1;
5260325f 214 if (options->password_authentication == -1)
215 options->password_authentication = 1;
94ec8c6b 216 if (options->kbd_interactive_authentication == -1)
217 options->kbd_interactive_authentication = 0;
5ba55ada 218 if (options->challenge_response_authentication == -1)
219 options->challenge_response_authentication = 1;
5260325f 220 if (options->permit_empty_passwd == -1)
c8d54615 221 options->permit_empty_passwd = 0;
f00bab84 222 if (options->permit_user_env == -1)
223 options->permit_user_env = 0;
5260325f 224 if (options->use_login == -1)
225 options->use_login = 0;
636f76ca 226 if (options->compression == -1)
07200973 227 options->compression = COMP_DELAYED;
33de75a3 228 if (options->allow_tcp_forwarding == -1)
229 options->allow_tcp_forwarding = 1;
43c3f85c 230 if (options->allow_agent_forwarding == -1)
231 options->allow_agent_forwarding = 1;
1d1ffb87 232 if (options->gateway_ports == -1)
233 options->gateway_ports = 0;
089fbbd2 234 if (options->max_startups == -1)
235 options->max_startups = 10;
c345cf9d 236 if (options->max_startups_rate == -1)
237 options->max_startups_rate = 100; /* 100% */
238 if (options->max_startups_begin == -1)
239 options->max_startups_begin = options->max_startups;
af4bd935 240 if (options->max_authtries == -1)
241 options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
c6dca55e 242 if (options->max_sessions == -1)
243 options->max_sessions = DEFAULT_SESSIONS_MAX;
c5a7d788 244 if (options->use_dns == -1)
245 options->use_dns = 1;
3ffc6336 246 if (options->client_alive_interval == -1)
184eed6a 247 options->client_alive_interval = 0;
3ffc6336 248 if (options->client_alive_count_max == -1)
249 options->client_alive_count_max = 3;
5df83e07 250 if (options->authorized_keys_file2 == NULL) {
251 /* authorized_keys_file2 falls back to authorized_keys_file */
252 if (options->authorized_keys_file != NULL)
253 options->authorized_keys_file2 = options->authorized_keys_file;
254 else
255 options->authorized_keys_file2 = _PATH_SSH_USER_PERMITTED_KEYS2;
256 }
257 if (options->authorized_keys_file == NULL)
258 options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
d20f3c9e 259 if (options->permit_tun == -1)
a4f24bf8 260 options->permit_tun = SSH_TUNMODE_NO;
1853d1ef 261
2ee1b704 262 /* Turn privilege separation on by default */
1853d1ef 263 if (use_privsep == -1)
2ee1b704 264 use_privsep = 1;
e299a298 265
4165b82e 266#ifndef HAVE_MMAP
e299a298 267 if (use_privsep && options->compression == 1) {
268 error("This platform does not support both privilege "
269 "separation and compression");
270 error("Compression disabled");
271 options->compression = 0;
272 }
273#endif
274
8efc0c15 275}
276
8efc0c15 277/* Keyword tokens. */
5260325f 278typedef enum {
279 sBadOption, /* == unknown option */
e15895cd 280 /* Portable-specific options */
7fceb20d 281 sUsePAM,
e15895cd 282 /* Standard Options */
5260325f 283 sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
284 sPermitRootLogin, sLogFacility, sLogLevel,
0598d99d 285 sRhostsRSAAuthentication, sRSAAuthentication,
5260325f 286 sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
a1e30b47 287 sKerberosGetAFSToken,
1c590258 288 sKerberosTgtPassing, sChallengeResponseAuthentication,
31b41ceb 289 sPasswordAuthentication, sKbdInteractiveAuthentication,
290 sListenAddress, sAddressFamily,
4f4648f9 291 sPrintMotd, sPrintLastLog, sIgnoreRhosts,
e6e573bd 292 sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
fd573618 293 sStrictModes, sEmptyPasswd, sTCPKeepAlive,
f00bab84 294 sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression,
33de75a3 295 sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
b2552997 296 sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
af4bd935 297 sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem,
c6dca55e 298 sMaxStartups, sMaxAuthTries, sMaxSessions,
c5a7d788 299 sBanner, sUseDNS, sHostbasedAuthentication,
184eed6a 300 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
c8445989 301 sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
d20f3c9e 302 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
db49deeb 303 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
43c3f85c 304 sUsePrivilegeSeparation, sAllowAgentForwarding,
a2144546 305 sDeprecated, sUnsupported
8efc0c15 306} ServerOpCodes;
307
d231781a 308#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */
309#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */
310#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH)
311
8efc0c15 312/* Textual representation of the tokens. */
5260325f 313static struct {
314 const char *name;
315 ServerOpCodes opcode;
d231781a 316 u_int flags;
5260325f 317} keywords[] = {
e15895cd 318 /* Portable-specific options */
b06b11ad 319#ifdef USE_PAM
d231781a 320 { "usepam", sUsePAM, SSHCFG_GLOBAL },
b06b11ad 321#else
d231781a 322 { "usepam", sUnsupported, SSHCFG_GLOBAL },
b06b11ad 323#endif
d231781a 324 { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
e15895cd 325 /* Standard Options */
d231781a 326 { "port", sPort, SSHCFG_GLOBAL },
327 { "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
328 { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */
329 { "pidfile", sPidFile, SSHCFG_GLOBAL },
330 { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL },
331 { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL },
332 { "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL },
2ef741a3 333 { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL },
d231781a 334 { "syslogfacility", sLogFacility, SSHCFG_GLOBAL },
335 { "loglevel", sLogLevel, SSHCFG_GLOBAL },
336 { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL },
03bcbf84 337 { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL },
338 { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL },
d231781a 339 { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_GLOBAL },
03bcbf84 340 { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL },
341 { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL },
d231781a 342 { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */
1c590258 343#ifdef KRB5
03bcbf84 344 { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL },
d231781a 345 { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL },
346 { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL },
bcfcc5f9 347#ifdef USE_AFS
d231781a 348 { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL },
309af4e5 349#else
d231781a 350 { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL },
309af4e5 351#endif
a2144546 352#else
03bcbf84 353 { "kerberosauthentication", sUnsupported, SSHCFG_ALL },
d231781a 354 { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL },
355 { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL },
356 { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL },
a2144546 357#endif
d231781a 358 { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL },
359 { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL },
7364bd04 360#ifdef GSSAPI
03bcbf84 361 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
d231781a 362 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
7364bd04 363#else
03bcbf84 364 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
d231781a 365 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
7364bd04 366#endif
03bcbf84 367 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
368 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
121c4a34 369 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
d231781a 370 { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */
371 { "checkmail", sDeprecated, SSHCFG_GLOBAL },
372 { "listenaddress", sListenAddress, SSHCFG_GLOBAL },
373 { "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
374 { "printmotd", sPrintMotd, SSHCFG_GLOBAL },
375 { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL },
376 { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL },
377 { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL },
691712e0 378 { "x11forwarding", sX11Forwarding, SSHCFG_ALL },
379 { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL },
380 { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
d231781a 381 { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
382 { "strictmodes", sStrictModes, SSHCFG_GLOBAL },
383 { "permitemptypasswords", sEmptyPasswd, SSHCFG_GLOBAL },
384 { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
385 { "uselogin", sUseLogin, SSHCFG_GLOBAL },
386 { "compression", sCompression, SSHCFG_GLOBAL },
387 { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL },
388 { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */
389 { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL },
43c3f85c 390 { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL },
d231781a 391 { "allowusers", sAllowUsers, SSHCFG_GLOBAL },
392 { "denyusers", sDenyUsers, SSHCFG_GLOBAL },
393 { "allowgroups", sAllowGroups, SSHCFG_GLOBAL },
394 { "denygroups", sDenyGroups, SSHCFG_GLOBAL },
395 { "ciphers", sCiphers, SSHCFG_GLOBAL },
396 { "macs", sMacs, SSHCFG_GLOBAL },
397 { "protocol", sProtocol, SSHCFG_GLOBAL },
398 { "gatewayports", sGatewayPorts, SSHCFG_ALL },
399 { "subsystem", sSubsystem, SSHCFG_GLOBAL },
400 { "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
8086aeb2 401 { "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
c6dca55e 402 { "maxsessions", sMaxSessions, SSHCFG_ALL },
03bcbf84 403 { "banner", sBanner, SSHCFG_ALL },
d231781a 404 { "usedns", sUseDNS, SSHCFG_GLOBAL },
405 { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
406 { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL },
407 { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL },
408 { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL },
409 { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL },
410 { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL },
411 { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL },
412 { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL },
413 { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL },
2fefbadf 414 { "match", sMatch, SSHCFG_ALL },
415 { "permitopen", sPermitOpen, SSHCFG_ALL },
e7259e8d 416 { "forcecommand", sForceCommand, SSHCFG_ALL },
db49deeb 417 { "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
d231781a 418 { NULL, sBadOption, 0 }
8efc0c15 419};
420
1760c982 421static struct {
422 int val;
423 char *text;
424} tunmode_desc[] = {
425 { SSH_TUNMODE_NO, "no" },
426 { SSH_TUNMODE_POINTOPOINT, "point-to-point" },
427 { SSH_TUNMODE_ETHERNET, "ethernet" },
428 { SSH_TUNMODE_YES, "yes" },
429 { -1, NULL }
430};
431
aa3378df 432/*
6be9a5e8 433 * Returns the number of the token pointed to by cp or sBadOption.
aa3378df 434 */
8efc0c15 435
6ae2364d 436static ServerOpCodes
5260325f 437parse_token(const char *cp, const char *filename,
d231781a 438 int linenum, u_int *flags)
8efc0c15 439{
1e3b8b07 440 u_int i;
8efc0c15 441
5260325f 442 for (i = 0; keywords[i].name; i++)
d231781a 443 if (strcasecmp(cp, keywords[i].name) == 0) {
444 *flags = keywords[i].flags;
5260325f 445 return keywords[i].opcode;
d231781a 446 }
8efc0c15 447
b7c70970 448 error("%s: line %d: Bad configuration option: %s",
449 filename, linenum, cp);
5260325f 450 return sBadOption;
8efc0c15 451}
452
396c147e 453static void
2d2a2c65 454add_listen_addr(ServerOptions *options, char *addr, u_short port)
48e671d5 455{
2ceb8101 456 u_int i;
48e671d5 457
458 if (options->num_ports == 0)
459 options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
31b41ceb 460 if (options->address_family == -1)
461 options->address_family = AF_UNSPEC;
2d2a2c65 462 if (port == 0)
d11c1288 463 for (i = 0; i < options->num_ports; i++)
464 add_one_listen_addr(options, addr, options->ports[i]);
465 else
2d2a2c65 466 add_one_listen_addr(options, addr, port);
d11c1288 467}
468
396c147e 469static void
d11c1288 470add_one_listen_addr(ServerOptions *options, char *addr, u_short port)
471{
472 struct addrinfo hints, *ai, *aitop;
473 char strport[NI_MAXSERV];
474 int gaierr;
475
476 memset(&hints, 0, sizeof(hints));
31b41ceb 477 hints.ai_family = options->address_family;
d11c1288 478 hints.ai_socktype = SOCK_STREAM;
479 hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0;
7528d467 480 snprintf(strport, sizeof strport, "%u", port);
d11c1288 481 if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0)
482 fatal("bad addr or host: %s (%s)",
483 addr ? addr : "<NULL>",
bb4626fe 484 ssh_gai_strerror(gaierr));
d11c1288 485 for (ai = aitop; ai->ai_next; ai = ai->ai_next)
486 ;
487 ai->ai_next = options->listen_addrs;
488 options->listen_addrs = aitop;
48e671d5 489}
490
d231781a 491/*
492 * The strategy for the Match blocks is that the config file is parsed twice.
493 *
494 * The first time is at startup. activep is initialized to 1 and the
495 * directives in the global context are processed and acted on. Hitting a
496 * Match directive unsets activep and the directives inside the block are
497 * checked for syntax only.
498 *
499 * The second time is after a connection has been established but before
500 * authentication. activep is initialized to 2 and global config directives
501 * are ignored since they have already been processed. If the criteria in a
502 * Match block is met, activep is set and the subsequent directives
503 * processed and actioned until EOF or another Match block unsets it. Any
504 * options set are copied into the main server config.
505 *
506 * Potential additions/improvements:
507 * - Add Match support for pre-kex directives, eg Protocol, Ciphers.
508 *
509 * - Add a Tag directive (idea from David Leonard) ala pf, eg:
510 * Match Address 192.168.0.*
511 * Tag trusted
512 * Match Group wheel
513 * Tag trusted
514 * Match Tag trusted
515 * AllowTcpForwarding yes
516 * GatewayPorts clientspecified
517 * [...]
518 *
519 * - Add a PermittedChannelRequests directive
520 * Match Group shell
521 * PermittedChannelRequests session,forwarded-tcpip
522 */
523
fa47fe3c 524static int
525match_cfg_line_group(const char *grps, int line, const char *user)
526{
527 int result = 0;
528 u_int ngrps = 0;
529 char *arg, *p, *cp, *grplist[MAX_MATCH_GROUPS];
530 struct passwd *pw;
531
532 /*
533 * Even if we do not have a user yet, we still need to check for
534 * valid syntax.
535 */
536 arg = cp = xstrdup(grps);
537 while ((p = strsep(&cp, ",")) != NULL && *p != '\0') {
538 if (ngrps >= MAX_MATCH_GROUPS) {
539 error("line %d: too many groups in Match Group", line);
540 result = -1;
541 goto out;
542 }
543 grplist[ngrps++] = p;
544 }
545
546 if (user == NULL)
547 goto out;
548
549 if ((pw = getpwnam(user)) == NULL) {
550 debug("Can't match group at line %d because user %.100s does "
551 "not exist", line, user);
552 } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
553 debug("Can't Match group because user %.100s not in any group "
554 "at line %d", user, line);
555 } else if (ga_match(grplist, ngrps) != 1) {
556 debug("user %.100s does not match group %.100s at line %d",
557 user, arg, line);
558 } else {
559 debug("user %.100s matched group %.100s at line %d", user,
560 arg, line);
561 result = 1;
562 }
563out:
564 ga_free();
565 xfree(arg);
566 return result;
567}
568
d231781a 569static int
570match_cfg_line(char **condition, int line, const char *user, const char *host,
571 const char *address)
572{
573 int result = 1;
574 char *arg, *attrib, *cp = *condition;
575 size_t len;
576
577 if (user == NULL)
578 debug3("checking syntax for 'Match %s'", cp);
579 else
580 debug3("checking match for '%s' user %s host %s addr %s", cp,
581 user ? user : "(null)", host ? host : "(null)",
582 address ? address : "(null)");
583
584 while ((attrib = strdelim(&cp)) && *attrib != '\0') {
585 if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
586 error("Missing Match criteria for %s", attrib);
587 return -1;
588 }
589 len = strlen(arg);
590 if (strcasecmp(attrib, "user") == 0) {
591 if (!user) {
592 result = 0;
593 continue;
594 }
595 if (match_pattern_list(user, arg, len, 0) != 1)
596 result = 0;
597 else
598 debug("user %.100s matched 'User %.100s' at "
599 "line %d", user, arg, line);
fa47fe3c 600 } else if (strcasecmp(attrib, "group") == 0) {
601 switch (match_cfg_line_group(arg, line, user)) {
602 case -1:
603 return -1;
604 case 0:
605 result = 0;
606 }
d231781a 607 } else if (strcasecmp(attrib, "host") == 0) {
608 if (!host) {
609 result = 0;
610 continue;
611 }
612 if (match_hostname(host, arg, len) != 1)
613 result = 0;
614 else
615 debug("connection from %.100s matched 'Host "
616 "%.100s' at line %d", host, arg, line);
617 } else if (strcasecmp(attrib, "address") == 0) {
15b5fa9b 618 switch (addr_match_list(address, arg)) {
619 case 1:
d231781a 620 debug("connection from %.100s matched 'Address "
621 "%.100s' at line %d", address, arg, line);
15b5fa9b 622 break;
623 case 0:
b3b048d6 624 case -1:
15b5fa9b 625 result = 0;
626 break;
b3b048d6 627 case -2:
15b5fa9b 628 return -1;
629 }
d231781a 630 } else {
631 error("Unsupported Match attribute %s", attrib);
632 return -1;
633 }
634 }
635 if (user != NULL)
636 debug3("match %sfound", result ? "" : "not ");
637 *condition = cp;
638 return result;
639}
640
e7259e8d 641#define WHITESPACE " \t\r\n"
642
2717fa0f 643int
644process_server_config_line(ServerOptions *options, char *line,
d231781a 645 const char *filename, int linenum, int *activep, const char *user,
646 const char *host, const char *address)
8efc0c15 647{
d11c1288 648 char *cp, **charptr, *arg, *p;
d231781a 649 int cmdline = 0, *intptr, value, n;
2a72bc03 650 SyslogFacility *log_facility_ptr;
651 LogLevel *log_level_ptr;
5260325f 652 ServerOpCodes opcode;
3867aa0a 653 u_short port;
d231781a 654 u_int i, flags = 0;
d66ce1a1 655 size_t len;
5260325f 656
2717fa0f 657 cp = line;
0f8cd5a6 658 if ((arg = strdelim(&cp)) == NULL)
88299971 659 return 0;
2717fa0f 660 /* Ignore leading whitespace */
661 if (*arg == '\0')
704b1659 662 arg = strdelim(&cp);
2717fa0f 663 if (!arg || !*arg || *arg == '#')
664 return 0;
665 intptr = NULL;
666 charptr = NULL;
d231781a 667 opcode = parse_token(arg, filename, linenum, &flags);
668
669 if (activep == NULL) { /* We are processing a command line directive */
670 cmdline = 1;
671 activep = &cmdline;
672 }
673 if (*activep && opcode != sMatch)
674 debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
675 if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
676 if (user == NULL) {
677 fatal("%s line %d: Directive '%s' is not allowed "
678 "within a Match block", filename, linenum, arg);
679 } else { /* this is a directive we have already processed */
680 while (arg)
681 arg = strdelim(&cp);
682 return 0;
683 }
684 }
685
2717fa0f 686 switch (opcode) {
687 /* Portable-specific options */
7fceb20d 688 case sUsePAM:
689 intptr = &options->use_pam;
2717fa0f 690 goto parse_flag;
48e671d5 691
2717fa0f 692 /* Standard Options */
693 case sBadOption:
694 return -1;
695 case sPort:
696 /* ignore ports from configfile if cmdline specifies ports */
697 if (options->ports_from_cmdline)
698 return 0;
699 if (options->listen_addrs != NULL)
700 fatal("%s line %d: ports must be specified before "
3a454b6a 701 "ListenAddress.", filename, linenum);
2717fa0f 702 if (options->num_ports >= MAX_PORTS)
703 fatal("%s line %d: too many ports.",
704 filename, linenum);
705 arg = strdelim(&cp);
706 if (!arg || *arg == '\0')
707 fatal("%s line %d: missing port number.",
708 filename, linenum);
709 options->ports[options->num_ports++] = a2port(arg);
710 if (options->ports[options->num_ports-1] == 0)
711 fatal("%s line %d: Badly formatted port number.",
712 filename, linenum);
713 break;
714
715 case sServerKeyBits:
716 intptr = &options->server_key_bits;
c6dca55e 717 parse_int:
2717fa0f 718 arg = strdelim(&cp);
719 if (!arg || *arg == '\0')
720 fatal("%s line %d: missing integer value.",
721 filename, linenum);
722 value = atoi(arg);
d231781a 723 if (*activep && *intptr == -1)
2717fa0f 724 *intptr = value;
725 break;
726
727 case sLoginGraceTime:
728 intptr = &options->login_grace_time;
c6dca55e 729 parse_time:
2717fa0f 730 arg = strdelim(&cp);
731 if (!arg || *arg == '\0')
732 fatal("%s line %d: missing time value.",
733 filename, linenum);
734 if ((value = convtime(arg)) == -1)
735 fatal("%s line %d: invalid time value.",
736 filename, linenum);
737 if (*intptr == -1)
738 *intptr = value;
739 break;
740
741 case sKeyRegenerationTime:
742 intptr = &options->key_regeneration_time;
743 goto parse_time;
744
745 case sListenAddress:
746 arg = strdelim(&cp);
3867aa0a 747 if (arg == NULL || *arg == '\0')
748 fatal("%s line %d: missing address",
2717fa0f 749 filename, linenum);
91135a0e 750 /* check for bare IPv6 address: no "[]" and 2 or more ":" */
751 if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL
752 && strchr(p+1, ':') != NULL) {
753 add_listen_addr(options, arg, 0);
754 break;
755 }
3867aa0a 756 p = hpdelim(&arg);
757 if (p == NULL)
758 fatal("%s line %d: bad address:port usage",
2717fa0f 759 filename, linenum);
3867aa0a 760 p = cleanhostname(p);
761 if (arg == NULL)
762 port = 0;
763 else if ((port = a2port(arg)) == 0)
764 fatal("%s line %d: bad port number", filename, linenum);
765
766 add_listen_addr(options, p, port);
767
2717fa0f 768 break;
769
31b41ceb 770 case sAddressFamily:
771 arg = strdelim(&cp);
38634ff6 772 if (!arg || *arg == '\0')
773 fatal("%s line %d: missing address family.",
774 filename, linenum);
31b41ceb 775 intptr = &options->address_family;
776 if (options->listen_addrs != NULL)
777 fatal("%s line %d: address family must be specified before "
778 "ListenAddress.", filename, linenum);
779 if (strcasecmp(arg, "inet") == 0)
780 value = AF_INET;
781 else if (strcasecmp(arg, "inet6") == 0)
782 value = AF_INET6;
783 else if (strcasecmp(arg, "any") == 0)
784 value = AF_UNSPEC;
785 else
786 fatal("%s line %d: unsupported address family \"%s\".",
787 filename, linenum, arg);
788 if (*intptr == -1)
789 *intptr = value;
790 break;
791
2717fa0f 792 case sHostKeyFile:
793 intptr = &options->num_host_key_files;
794 if (*intptr >= MAX_HOSTKEYS)
795 fatal("%s line %d: too many host keys specified (max %d).",
796 filename, linenum, MAX_HOSTKEYS);
797 charptr = &options->host_key_files[*intptr];
c6dca55e 798 parse_filename:
2717fa0f 799 arg = strdelim(&cp);
800 if (!arg || *arg == '\0')
801 fatal("%s line %d: missing file name.",
802 filename, linenum);
d231781a 803 if (*activep && *charptr == NULL) {
2717fa0f 804 *charptr = tilde_expand_filename(arg, getuid());
805 /* increase optional counter */
806 if (intptr != NULL)
807 *intptr = *intptr + 1;
808 }
809 break;
0fbe8c74 810
2717fa0f 811 case sPidFile:
812 charptr = &options->pid_file;
813 goto parse_filename;
5260325f 814
2717fa0f 815 case sPermitRootLogin:
816 intptr = &options->permit_root_login;
817 arg = strdelim(&cp);
818 if (!arg || *arg == '\0')
819 fatal("%s line %d: missing yes/"
820 "without-password/forced-commands-only/no "
821 "argument.", filename, linenum);
822 value = 0; /* silence compiler */
823 if (strcmp(arg, "without-password") == 0)
824 value = PERMIT_NO_PASSWD;
825 else if (strcmp(arg, "forced-commands-only") == 0)
826 value = PERMIT_FORCED_ONLY;
827 else if (strcmp(arg, "yes") == 0)
828 value = PERMIT_YES;
829 else if (strcmp(arg, "no") == 0)
830 value = PERMIT_NO;
831 else
832 fatal("%s line %d: Bad yes/"
833 "without-password/forced-commands-only/no "
834 "argument: %s", filename, linenum, arg);
2ef741a3 835 if (*activep && *intptr == -1)
2717fa0f 836 *intptr = value;
837 break;
838
839 case sIgnoreRhosts:
840 intptr = &options->ignore_rhosts;
c6dca55e 841 parse_flag:
2717fa0f 842 arg = strdelim(&cp);
843 if (!arg || *arg == '\0')
844 fatal("%s line %d: missing yes/no argument.",
845 filename, linenum);
846 value = 0; /* silence compiler */
847 if (strcmp(arg, "yes") == 0)
848 value = 1;
849 else if (strcmp(arg, "no") == 0)
850 value = 0;
851 else
852 fatal("%s line %d: Bad yes/no argument: %s",
853 filename, linenum, arg);
d231781a 854 if (*activep && *intptr == -1)
2717fa0f 855 *intptr = value;
856 break;
857
858 case sIgnoreUserKnownHosts:
859 intptr = &options->ignore_user_known_hosts;
860 goto parse_flag;
861
2717fa0f 862 case sRhostsRSAAuthentication:
863 intptr = &options->rhosts_rsa_authentication;
864 goto parse_flag;
865
866 case sHostbasedAuthentication:
867 intptr = &options->hostbased_authentication;
868 goto parse_flag;
869
870 case sHostbasedUsesNameFromPacketOnly:
871 intptr = &options->hostbased_uses_name_from_packet_only;
872 goto parse_flag;
873
874 case sRSAAuthentication:
875 intptr = &options->rsa_authentication;
876 goto parse_flag;
877
878 case sPubkeyAuthentication:
879 intptr = &options->pubkey_authentication;
880 goto parse_flag;
d0ec7f42 881
2717fa0f 882 case sKerberosAuthentication:
883 intptr = &options->kerberos_authentication;
884 goto parse_flag;
5260325f 885
2717fa0f 886 case sKerberosOrLocalPasswd:
887 intptr = &options->kerberos_or_local_passwd;
888 goto parse_flag;
5260325f 889
2717fa0f 890 case sKerberosTicketCleanup:
891 intptr = &options->kerberos_ticket_cleanup;
892 goto parse_flag;
d0ec7f42 893
a1e30b47 894 case sKerberosGetAFSToken:
895 intptr = &options->kerberos_get_afs_token;
896 goto parse_flag;
897
7364bd04 898 case sGssAuthentication:
899 intptr = &options->gss_authentication;
900 goto parse_flag;
901
902 case sGssCleanupCreds:
903 intptr = &options->gss_cleanup_creds;
904 goto parse_flag;
905
2717fa0f 906 case sPasswordAuthentication:
907 intptr = &options->password_authentication;
908 goto parse_flag;
5260325f 909
2717fa0f 910 case sKbdInteractiveAuthentication:
911 intptr = &options->kbd_interactive_authentication;
912 goto parse_flag;
8002af61 913
2717fa0f 914 case sChallengeResponseAuthentication:
915 intptr = &options->challenge_response_authentication;
916 goto parse_flag;
8002af61 917
2717fa0f 918 case sPrintMotd:
919 intptr = &options->print_motd;
920 goto parse_flag;
5260325f 921
2717fa0f 922 case sPrintLastLog:
923 intptr = &options->print_lastlog;
924 goto parse_flag;
5260325f 925
2717fa0f 926 case sX11Forwarding:
927 intptr = &options->x11_forwarding;
928 goto parse_flag;
5260325f 929
2717fa0f 930 case sX11DisplayOffset:
931 intptr = &options->x11_display_offset;
932 goto parse_int;
8efc0c15 933
e6e573bd 934 case sX11UseLocalhost:
935 intptr = &options->x11_use_localhost;
936 goto parse_flag;
937
2717fa0f 938 case sXAuthLocation:
939 charptr = &options->xauth_location;
940 goto parse_filename;
5260325f 941
2717fa0f 942 case sStrictModes:
943 intptr = &options->strict_modes;
944 goto parse_flag;
5260325f 945
fd573618 946 case sTCPKeepAlive:
947 intptr = &options->tcp_keep_alive;
2717fa0f 948 goto parse_flag;
33de75a3 949
2717fa0f 950 case sEmptyPasswd:
951 intptr = &options->permit_empty_passwd;
952 goto parse_flag;
5260325f 953
f00bab84 954 case sPermitUserEnvironment:
955 intptr = &options->permit_user_env;
956 goto parse_flag;
957
2717fa0f 958 case sUseLogin:
959 intptr = &options->use_login;
960 goto parse_flag;
5260325f 961
636f76ca 962 case sCompression:
963 intptr = &options->compression;
07200973 964 arg = strdelim(&cp);
965 if (!arg || *arg == '\0')
966 fatal("%s line %d: missing yes/no/delayed "
967 "argument.", filename, linenum);
968 value = 0; /* silence compiler */
969 if (strcmp(arg, "delayed") == 0)
970 value = COMP_DELAYED;
971 else if (strcmp(arg, "yes") == 0)
972 value = COMP_ZLIB;
973 else if (strcmp(arg, "no") == 0)
974 value = COMP_NONE;
975 else
976 fatal("%s line %d: Bad yes/no/delayed "
977 "argument: %s", filename, linenum, arg);
978 if (*intptr == -1)
979 *intptr = value;
980 break;
636f76ca 981
2717fa0f 982 case sGatewayPorts:
983 intptr = &options->gateway_ports;
3867aa0a 984 arg = strdelim(&cp);
985 if (!arg || *arg == '\0')
986 fatal("%s line %d: missing yes/no/clientspecified "
987 "argument.", filename, linenum);
988 value = 0; /* silence compiler */
989 if (strcmp(arg, "clientspecified") == 0)
990 value = 2;
991 else if (strcmp(arg, "yes") == 0)
992 value = 1;
993 else if (strcmp(arg, "no") == 0)
994 value = 0;
995 else
996 fatal("%s line %d: Bad yes/no/clientspecified "
997 "argument: %s", filename, linenum, arg);
f3e3d6ce 998 if (*activep && *intptr == -1)
3867aa0a 999 *intptr = value;
1000 break;
5260325f 1001
c5a7d788 1002 case sUseDNS:
1003 intptr = &options->use_dns;
2717fa0f 1004 goto parse_flag;
5260325f 1005
2717fa0f 1006 case sLogFacility:
2a72bc03 1007 log_facility_ptr = &options->log_facility;
2717fa0f 1008 arg = strdelim(&cp);
1009 value = log_facility_number(arg);
5eaf8578 1010 if (value == SYSLOG_FACILITY_NOT_SET)
2717fa0f 1011 fatal("%.200s line %d: unsupported log facility '%s'",
1012 filename, linenum, arg ? arg : "<NONE>");
2a72bc03 1013 if (*log_facility_ptr == -1)
1014 *log_facility_ptr = (SyslogFacility) value;
2717fa0f 1015 break;
1016
1017 case sLogLevel:
2a72bc03 1018 log_level_ptr = &options->log_level;
2717fa0f 1019 arg = strdelim(&cp);
1020 value = log_level_number(arg);
5eaf8578 1021 if (value == SYSLOG_LEVEL_NOT_SET)
2717fa0f 1022 fatal("%.200s line %d: unsupported log level '%s'",
1023 filename, linenum, arg ? arg : "<NONE>");
2a72bc03 1024 if (*log_level_ptr == -1)
1025 *log_level_ptr = (LogLevel) value;
2717fa0f 1026 break;
1027
1028 case sAllowTcpForwarding:
1029 intptr = &options->allow_tcp_forwarding;
1030 goto parse_flag;
1031
43c3f85c 1032 case sAllowAgentForwarding:
1033 intptr = &options->allow_agent_forwarding;
1034 goto parse_flag;
1035
1853d1ef 1036 case sUsePrivilegeSeparation:
1037 intptr = &use_privsep;
1038 goto parse_flag;
1039
2717fa0f 1040 case sAllowUsers:
1041 while ((arg = strdelim(&cp)) && *arg != '\0') {
1042 if (options->num_allow_users >= MAX_ALLOW_USERS)
1043 fatal("%s line %d: too many allow users.",
1044 filename, linenum);
7528d467 1045 options->allow_users[options->num_allow_users++] =
1046 xstrdup(arg);
2717fa0f 1047 }
1048 break;
a8be9f80 1049
2717fa0f 1050 case sDenyUsers:
1051 while ((arg = strdelim(&cp)) && *arg != '\0') {
1052 if (options->num_deny_users >= MAX_DENY_USERS)
d4f40d92 1053 fatal("%s line %d: too many deny users.",
2717fa0f 1054 filename, linenum);
7528d467 1055 options->deny_users[options->num_deny_users++] =
1056 xstrdup(arg);
2717fa0f 1057 }
1058 break;
b2552997 1059
2717fa0f 1060 case sAllowGroups:
1061 while ((arg = strdelim(&cp)) && *arg != '\0') {
1062 if (options->num_allow_groups >= MAX_ALLOW_GROUPS)
1063 fatal("%s line %d: too many allow groups.",
1064 filename, linenum);
7528d467 1065 options->allow_groups[options->num_allow_groups++] =
1066 xstrdup(arg);
2717fa0f 1067 }
1068 break;
a8be9f80 1069
2717fa0f 1070 case sDenyGroups:
1071 while ((arg = strdelim(&cp)) && *arg != '\0') {
1072 if (options->num_deny_groups >= MAX_DENY_GROUPS)
1073 fatal("%s line %d: too many deny groups.",
1074 filename, linenum);
1075 options->deny_groups[options->num_deny_groups++] = xstrdup(arg);
1076 }
1077 break;
38c295d6 1078
2717fa0f 1079 case sCiphers:
1080 arg = strdelim(&cp);
1081 if (!arg || *arg == '\0')
1082 fatal("%s line %d: Missing argument.", filename, linenum);
1083 if (!ciphers_valid(arg))
1084 fatal("%s line %d: Bad SSH2 cipher spec '%s'.",
1085 filename, linenum, arg ? arg : "<NONE>");
1086 if (options->ciphers == NULL)
1087 options->ciphers = xstrdup(arg);
1088 break;
1089
1090 case sMacs:
1091 arg = strdelim(&cp);
1092 if (!arg || *arg == '\0')
1093 fatal("%s line %d: Missing argument.", filename, linenum);
1094 if (!mac_valid(arg))
1095 fatal("%s line %d: Bad SSH2 mac spec '%s'.",
1096 filename, linenum, arg ? arg : "<NONE>");
1097 if (options->macs == NULL)
1098 options->macs = xstrdup(arg);
1099 break;
1100
1101 case sProtocol:
1102 intptr = &options->protocol;
1103 arg = strdelim(&cp);
1104 if (!arg || *arg == '\0')
1105 fatal("%s line %d: Missing argument.", filename, linenum);
1106 value = proto_spec(arg);
1107 if (value == SSH_PROTO_UNKNOWN)
1108 fatal("%s line %d: Bad protocol spec '%s'.",
184eed6a 1109 filename, linenum, arg ? arg : "<NONE>");
2717fa0f 1110 if (*intptr == SSH_PROTO_UNKNOWN)
1111 *intptr = value;
1112 break;
1113
1114 case sSubsystem:
1115 if (options->num_subsystems >= MAX_SUBSYSTEMS) {
1116 fatal("%s line %d: too many subsystems defined.",
184eed6a 1117 filename, linenum);
2717fa0f 1118 }
1119 arg = strdelim(&cp);
1120 if (!arg || *arg == '\0')
1121 fatal("%s line %d: Missing subsystem name.",
184eed6a 1122 filename, linenum);
d231781a 1123 if (!*activep) {
1124 arg = strdelim(&cp);
1125 break;
1126 }
2717fa0f 1127 for (i = 0; i < options->num_subsystems; i++)
1128 if (strcmp(arg, options->subsystem_name[i]) == 0)
1129 fatal("%s line %d: Subsystem '%s' already defined.",
184eed6a 1130 filename, linenum, arg);
2717fa0f 1131 options->subsystem_name[options->num_subsystems] = xstrdup(arg);
1132 arg = strdelim(&cp);
1133 if (!arg || *arg == '\0')
1134 fatal("%s line %d: Missing subsystem command.",
184eed6a 1135 filename, linenum);
2717fa0f 1136 options->subsystem_command[options->num_subsystems] = xstrdup(arg);
d66ce1a1 1137
1138 /* Collect arguments (separate to executable) */
1139 p = xstrdup(arg);
1140 len = strlen(p) + 1;
1141 while ((arg = strdelim(&cp)) != NULL && *arg != '\0') {
1142 len += 1 + strlen(arg);
1143 p = xrealloc(p, 1, len);
1144 strlcat(p, " ", len);
1145 strlcat(p, arg, len);
1146 }
1147 options->subsystem_args[options->num_subsystems] = p;
2717fa0f 1148 options->num_subsystems++;
1149 break;
1150
1151 case sMaxStartups:
1152 arg = strdelim(&cp);
1153 if (!arg || *arg == '\0')
1154 fatal("%s line %d: Missing MaxStartups spec.",
184eed6a 1155 filename, linenum);
2717fa0f 1156 if ((n = sscanf(arg, "%d:%d:%d",
1157 &options->max_startups_begin,
1158 &options->max_startups_rate,
1159 &options->max_startups)) == 3) {
1160 if (options->max_startups_begin >
1161 options->max_startups ||
1162 options->max_startups_rate > 100 ||
1163 options->max_startups_rate < 1)
c345cf9d 1164 fatal("%s line %d: Illegal MaxStartups spec.",
97de229c 1165 filename, linenum);
2717fa0f 1166 } else if (n != 1)
1167 fatal("%s line %d: Illegal MaxStartups spec.",
1168 filename, linenum);
1169 else
1170 options->max_startups = options->max_startups_begin;
1171 break;
1172
af4bd935 1173 case sMaxAuthTries:
1174 intptr = &options->max_authtries;
1175 goto parse_int;
1176
c6dca55e 1177 case sMaxSessions:
1178 intptr = &options->max_sessions;
1179 goto parse_int;
1180
2717fa0f 1181 case sBanner:
1182 charptr = &options->banner;
1183 goto parse_filename;
db49deeb 1184
2717fa0f 1185 /*
1186 * These options can contain %X options expanded at
1187 * connect time, so that you can specify paths like:
1188 *
1189 * AuthorizedKeysFile /etc/ssh_keys/%u
1190 */
1191 case sAuthorizedKeysFile:
1192 case sAuthorizedKeysFile2:
d4f40d92 1193 charptr = (opcode == sAuthorizedKeysFile) ?
2717fa0f 1194 &options->authorized_keys_file :
1195 &options->authorized_keys_file2;
1196 goto parse_filename;
1197
1198 case sClientAliveInterval:
1199 intptr = &options->client_alive_interval;
1200 goto parse_time;
1201
1202 case sClientAliveCountMax:
1203 intptr = &options->client_alive_count_max;
1204 goto parse_int;
1205
61a2c1da 1206 case sAcceptEnv:
1207 while ((arg = strdelim(&cp)) && *arg != '\0') {
1208 if (strchr(arg, '=') != NULL)
1209 fatal("%s line %d: Invalid environment name.",
1210 filename, linenum);
1211 if (options->num_accept_env >= MAX_ACCEPT_ENV)
1212 fatal("%s line %d: too many allow env.",
1213 filename, linenum);
d231781a 1214 if (!*activep)
1215 break;
61a2c1da 1216 options->accept_env[options->num_accept_env++] =
1217 xstrdup(arg);
1218 }
1219 break;
1220
d20f3c9e 1221 case sPermitTunnel:
1222 intptr = &options->permit_tun;
a4f24bf8 1223 arg = strdelim(&cp);
1224 if (!arg || *arg == '\0')
1225 fatal("%s line %d: Missing yes/point-to-point/"
1226 "ethernet/no argument.", filename, linenum);
1760c982 1227 value = -1;
1228 for (i = 0; tunmode_desc[i].val != -1; i++)
1229 if (strcmp(tunmode_desc[i].text, arg) == 0) {
1230 value = tunmode_desc[i].val;
1231 break;
1232 }
1233 if (value == -1)
a4f24bf8 1234 fatal("%s line %d: Bad yes/point-to-point/ethernet/"
1235 "no argument: %s", filename, linenum, arg);
1236 if (*intptr == -1)
1237 *intptr = value;
1238 break;
d20f3c9e 1239
d231781a 1240 case sMatch:
1241 if (cmdline)
1242 fatal("Match directive not supported as a command-line "
1243 "option");
1244 value = match_cfg_line(&cp, linenum, user, host, address);
1245 if (value < 0)
1246 fatal("%s line %d: Bad Match condition", filename,
1247 linenum);
1248 *activep = value;
1249 break;
1250
2fefbadf 1251 case sPermitOpen:
1252 arg = strdelim(&cp);
1253 if (!arg || *arg == '\0')
1254 fatal("%s line %d: missing PermitOpen specification",
1255 filename, linenum);
38757197 1256 n = options->num_permitted_opens; /* modified later */
2fefbadf 1257 if (strcmp(arg, "any") == 0) {
38757197 1258 if (*activep && n == -1) {
2fefbadf 1259 channel_clear_adm_permitted_opens();
ea46e550 1260 options->num_permitted_opens = 0;
1261 }
2fefbadf 1262 break;
1263 }
75b6d52c 1264 if (*activep && n == -1)
1265 channel_clear_adm_permitted_opens();
ea46e550 1266 for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) {
1267 p = hpdelim(&arg);
1268 if (p == NULL)
1269 fatal("%s line %d: missing host in PermitOpen",
1270 filename, linenum);
1271 p = cleanhostname(p);
1272 if (arg == NULL || (port = a2port(arg)) == 0)
1273 fatal("%s line %d: bad port number in "
1274 "PermitOpen", filename, linenum);
75b6d52c 1275 if (*activep && n == -1)
ea46e550 1276 options->num_permitted_opens =
1277 channel_add_adm_permitted_opens(p, port);
ea46e550 1278 }
2fefbadf 1279 break;
1280
e7259e8d 1281 case sForceCommand:
1282 if (cp == NULL)
1283 fatal("%.200s line %d: Missing argument.", filename,
1284 linenum);
1285 len = strspn(cp, WHITESPACE);
1286 if (*activep && options->adm_forced_command == NULL)
1287 options->adm_forced_command = xstrdup(cp + len);
1288 return 0;
1289
db49deeb 1290 case sChrootDirectory:
1291 charptr = &options->chroot_directory;
c5bf32e6 1292
1293 arg = strdelim(&cp);
1294 if (!arg || *arg == '\0')
1295 fatal("%s line %d: missing file name.",
1296 filename, linenum);
1297 if (*activep && *charptr == NULL)
1298 *charptr = xstrdup(arg);
1299 break;
db49deeb 1300
2717fa0f 1301 case sDeprecated:
bbe88b6d 1302 logit("%s line %d: Deprecated option %s",
2717fa0f 1303 filename, linenum, arg);
1304 while (arg)
1305 arg = strdelim(&cp);
1306 break;
1307
a2144546 1308 case sUnsupported:
1309 logit("%s line %d: Unsupported option %s",
1310 filename, linenum, arg);
1311 while (arg)
1312 arg = strdelim(&cp);
1313 break;
1314
2717fa0f 1315 default:
1316 fatal("%s line %d: Missing handler for opcode %s (%d)",
1317 filename, linenum, arg, opcode);
1318 }
1319 if ((arg = strdelim(&cp)) != NULL && *arg != '\0')
1320 fatal("%s line %d: garbage at end of line; \"%.200s\".",
1321 filename, linenum, arg);
1322 return 0;
1323}
089fbbd2 1324
2717fa0f 1325/* Reads the server configuration file. */
5c53a31e 1326
2717fa0f 1327void
b9a549d7 1328load_server_config(const char *filename, Buffer *conf)
2717fa0f 1329{
b9a549d7 1330 char line[1024], *cp;
7528d467 1331 FILE *f;
2717fa0f 1332
b9a549d7 1333 debug2("%s: filename %s", __func__, filename);
1334 if ((f = fopen(filename, "r")) == NULL) {
2717fa0f 1335 perror(filename);
1336 exit(1);
1337 }
b9a549d7 1338 buffer_clear(conf);
2717fa0f 1339 while (fgets(line, sizeof(line), f)) {
b9a549d7 1340 /*
1341 * Trim out comments and strip whitespace
f2107e97 1342 * NB - preserve newlines, they are needed to reproduce
b9a549d7 1343 * line numbers later for error messages
1344 */
1345 if ((cp = strchr(line, '#')) != NULL)
1346 memcpy(cp, "\n", 2);
1347 cp = line + strspn(line, " \t\r");
1348
1349 buffer_append(conf, cp, strlen(cp));
8efc0c15 1350 }
b9a549d7 1351 buffer_append(conf, "\0", 1);
5260325f 1352 fclose(f);
b9a549d7 1353 debug2("%s: done config len = %d", __func__, buffer_len(conf));
1354}
1355
1356void
d231781a 1357parse_server_match_config(ServerOptions *options, const char *user,
1358 const char *host, const char *address)
1359{
1360 ServerOptions mo;
1361
1362 initialize_server_options(&mo);
1363 parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
03bcbf84 1364 copy_set_server_options(options, &mo, 0);
d231781a 1365}
1366
03bcbf84 1367/* Helper macros */
1368#define M_CP_INTOPT(n) do {\
1369 if (src->n != -1) \
1370 dst->n = src->n; \
1371} while (0)
1372#define M_CP_STROPT(n) do {\
1373 if (src->n != NULL) { \
1374 if (dst->n != NULL) \
1375 xfree(dst->n); \
1376 dst->n = src->n; \
1377 } \
1378} while(0)
1379
1380/*
1381 * Copy any supported values that are set.
1382 *
1383 * If the preauth flag is set, we do not bother copying the the string or
1384 * array values that are not used pre-authentication, because any that we
1385 * do use must be explictly sent in mm_getpwnamallow().
1386 */
d231781a 1387void
03bcbf84 1388copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
d231781a 1389{
03bcbf84 1390 M_CP_INTOPT(password_authentication);
1391 M_CP_INTOPT(gss_authentication);
1392 M_CP_INTOPT(rsa_authentication);
1393 M_CP_INTOPT(pubkey_authentication);
1394 M_CP_INTOPT(kerberos_authentication);
1395 M_CP_INTOPT(hostbased_authentication);
1396 M_CP_INTOPT(kbd_interactive_authentication);
2ef741a3 1397 M_CP_INTOPT(permit_root_login);
03bcbf84 1398
1399 M_CP_INTOPT(allow_tcp_forwarding);
43c3f85c 1400 M_CP_INTOPT(allow_agent_forwarding);
03bcbf84 1401 M_CP_INTOPT(gateway_ports);
1402 M_CP_INTOPT(x11_display_offset);
1403 M_CP_INTOPT(x11_forwarding);
1404 M_CP_INTOPT(x11_use_localhost);
c6dca55e 1405 M_CP_INTOPT(max_sessions);
8086aeb2 1406 M_CP_INTOPT(max_authtries);
03bcbf84 1407
1408 M_CP_STROPT(banner);
1409 if (preauth)
1410 return;
1411 M_CP_STROPT(adm_forced_command);
db49deeb 1412 M_CP_STROPT(chroot_directory);
d231781a 1413}
1414
03bcbf84 1415#undef M_CP_INTOPT
1416#undef M_CP_STROPT
1417
d231781a 1418void
1419parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
1420 const char *user, const char *host, const char *address)
b9a549d7 1421{
d231781a 1422 int active, linenum, bad_options = 0;
16acb158 1423 char *cp, *obuf, *cbuf;
b9a549d7 1424
1425 debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
1426
16acb158 1427 obuf = cbuf = xstrdup(buffer_ptr(conf));
d231781a 1428 active = user ? 0 : 1;
861cc543 1429 linenum = 1;
f8cc7664 1430 while ((cp = strsep(&cbuf, "\n")) != NULL) {
b9a549d7 1431 if (process_server_config_line(options, cp, filename,
d231781a 1432 linenum++, &active, user, host, address) != 0)
b9a549d7 1433 bad_options++;
1434 }
16acb158 1435 xfree(obuf);
b7c70970 1436 if (bad_options > 0)
1437 fatal("%s: terminating, %d bad configuration options",
1438 filename, bad_options);
8efc0c15 1439}
1760c982 1440
1441static const char *
1442fmt_intarg(ServerOpCodes code, int val)
1443{
1444 if (code == sAddressFamily) {
1445 switch (val) {
1446 case AF_INET:
1447 return "inet";
1448 case AF_INET6:
1449 return "inet6";
1450 case AF_UNSPEC:
1451 return "any";
1452 default:
1453 return "UNKNOWN";
1454 }
1455 }
1456 if (code == sPermitRootLogin) {
1457 switch (val) {
1458 case PERMIT_NO_PASSWD:
1459 return "without-passord";
1460 case PERMIT_FORCED_ONLY:
1461 return "forced-commands-only";
1462 case PERMIT_YES:
1463 return "yes";
1464 }
1465 }
1466 if (code == sProtocol) {
1467 switch (val) {
1468 case SSH_PROTO_1:
1469 return "1";
1470 case SSH_PROTO_2:
1471 return "2";
1472 case (SSH_PROTO_1|SSH_PROTO_2):
1473 return "2,1";
1474 default:
1475 return "UNKNOWN";
1476 }
1477 }
1478 if (code == sGatewayPorts && val == 2)
1479 return "clientspecified";
1480 if (code == sCompression && val == COMP_DELAYED)
1481 return "delayed";
1482 switch (val) {
1483 case -1:
1484 return "unset";
1485 case 0:
1486 return "no";
1487 case 1:
1488 return "yes";
1489 }
1490 return "UNKNOWN";
1491}
1492
1493static const char *
1494lookup_opcode_name(ServerOpCodes code)
1495{
1496 u_int i;
1497
1498 for (i = 0; keywords[i].name != NULL; i++)
1499 if (keywords[i].opcode == code)
1500 return(keywords[i].name);
1501 return "UNKNOWN";
1502}
1503
1504static void
1505dump_cfg_int(ServerOpCodes code, int val)
1506{
1507 printf("%s %d\n", lookup_opcode_name(code), val);
1508}
1509
1510static void
1511dump_cfg_fmtint(ServerOpCodes code, int val)
1512{
1513 printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
1514}
1515
1516static void
1517dump_cfg_string(ServerOpCodes code, const char *val)
1518{
1519 if (val == NULL)
1520 return;
1521 printf("%s %s\n", lookup_opcode_name(code), val);
1522}
1523
1524static void
1525dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals)
1526{
1527 u_int i;
1528
1529 for (i = 0; i < count; i++)
1530 printf("%s %s\n", lookup_opcode_name(code), vals[i]);
1531}
1532
1533void
1534dump_config(ServerOptions *o)
1535{
1536 u_int i;
1537 int ret;
1538 struct addrinfo *ai;
1539 char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL;
1540
1541 /* these are usually at the top of the config */
1542 for (i = 0; i < o->num_ports; i++)
1543 printf("port %d\n", o->ports[i]);
1544 dump_cfg_fmtint(sProtocol, o->protocol);
1545 dump_cfg_fmtint(sAddressFamily, o->address_family);
1546
1547 /* ListenAddress must be after Port */
1548 for (ai = o->listen_addrs; ai; ai = ai->ai_next) {
1549 if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr,
1550 sizeof(addr), port, sizeof(port),
1551 NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
1552 error("getnameinfo failed: %.100s",
1553 (ret != EAI_SYSTEM) ? gai_strerror(ret) :
1554 strerror(errno));
1555 } else {
1556 if (ai->ai_family == AF_INET6)
1557 printf("listenaddress [%s]:%s\n", addr, port);
1558 else
1559 printf("listenaddress %s:%s\n", addr, port);
1560 }
1561 }
1562
1563 /* integer arguments */
1564 dump_cfg_int(sServerKeyBits, o->server_key_bits);
1565 dump_cfg_int(sLoginGraceTime, o->login_grace_time);
1566 dump_cfg_int(sKeyRegenerationTime, o->key_regeneration_time);
1567 dump_cfg_int(sX11DisplayOffset, o->x11_display_offset);
1568 dump_cfg_int(sMaxAuthTries, o->max_authtries);
1569 dump_cfg_int(sClientAliveInterval, o->client_alive_interval);
1570 dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max);
1571
1572 /* formatted integer arguments */
1573 dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login);
1574 dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts);
1575 dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts);
1576 dump_cfg_fmtint(sRhostsRSAAuthentication, o->rhosts_rsa_authentication);
1577 dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication);
1578 dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly,
1579 o->hostbased_uses_name_from_packet_only);
1580 dump_cfg_fmtint(sRSAAuthentication, o->rsa_authentication);
1581 dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication);
1582 dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication);
1583 dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd);
1584 dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup);
1585 dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token);
1586 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
1587 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
1588 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
1589 dump_cfg_fmtint(sKbdInteractiveAuthentication,
1590 o->kbd_interactive_authentication);
1591 dump_cfg_fmtint(sChallengeResponseAuthentication,
1592 o->challenge_response_authentication);
1593 dump_cfg_fmtint(sPrintMotd, o->print_motd);
1594 dump_cfg_fmtint(sPrintLastLog, o->print_lastlog);
1595 dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding);
1596 dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost);
1597 dump_cfg_fmtint(sStrictModes, o->strict_modes);
1598 dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive);
1599 dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd);
1600 dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env);
1601 dump_cfg_fmtint(sUseLogin, o->use_login);
1602 dump_cfg_fmtint(sCompression, o->compression);
1603 dump_cfg_fmtint(sGatewayPorts, o->gateway_ports);
1604 dump_cfg_fmtint(sUseDNS, o->use_dns);
1605 dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding);
1606 dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep);
1607
1608 /* string arguments */
1609 dump_cfg_string(sPidFile, o->pid_file);
1610 dump_cfg_string(sXAuthLocation, o->xauth_location);
1611 dump_cfg_string(sCiphers, o->ciphers);
1612 dump_cfg_string(sMacs, o->macs);
1613 dump_cfg_string(sBanner, o->banner);
1614 dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file);
1615 dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2);
1616 dump_cfg_string(sForceCommand, o->adm_forced_command);
1617
1618 /* string arguments requiring a lookup */
1619 dump_cfg_string(sLogLevel, log_level_name(o->log_level));
1620 dump_cfg_string(sLogFacility, log_facility_name(o->log_facility));
1621
1622 /* string array arguments */
1623 dump_cfg_strarray(sHostKeyFile, o->num_host_key_files,
1624 o->host_key_files);
1625 dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users);
1626 dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users);
1627 dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
1628 dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups);
1629 dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env);
1630
1631 /* other arguments */
1632 for (i = 0; i < o->num_subsystems; i++)
1633 printf("subsystem %s %s\n", o->subsystem_name[i],
1634 o->subsystem_args[i]);
1635
1636 printf("maxstartups %d:%d:%d\n", o->max_startups_begin,
1637 o->max_startups_rate, o->max_startups);
1638
1639 for (i = 0; tunmode_desc[i].val != -1; i++)
1640 if (tunmode_desc[i].val == o->permit_tun) {
1641 s = tunmode_desc[i].text;
1642 break;
1643 }
1644 dump_cfg_string(sPermitTunnel, s);
1645
1646 printf("permitopen");
1647 channel_print_adm_permitted_opens();
1648 printf("\n");
1649}
This page took 0.800404 seconds and 5 git commands to generate.