diff options
author | Colin Watson <cjwatson@debian.org> | 2007-06-12 16:16:35 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2007-06-12 16:16:35 +0000 |
commit | b7e40fa9da0b5491534a429dadb321eab5a77558 (patch) | |
tree | bed1da11e9f829925797aa093e379fc0b5868ecd /servconf.c | |
parent | 4f84beedf1005e44ff33c854abd6b711ffc0adb7 (diff) | |
parent | 086ea76990b1e6287c24b6db74adffd4605eb3b0 (diff) |
* New upstream release (closes: #395507, #397961, #420035). Important
changes not previously backported to 4.3p2:
- 4.4/4.4p1 (http://www.openssh.org/txt/release-4.4):
+ On portable OpenSSH, fix a GSSAPI authentication abort that could be
used to determine the validity of usernames on some platforms.
+ Implemented conditional configuration in sshd_config(5) using the
"Match" directive. This allows some configuration options to be
selectively overridden if specific criteria (based on user, group,
hostname and/or address) are met. So far a useful subset of
post-authentication options are supported and more are expected to
be added in future releases.
+ Add support for Diffie-Hellman group exchange key agreement with a
final hash of SHA256.
+ Added a "ForceCommand" directive to sshd_config(5). Similar to the
command="..." option accepted in ~/.ssh/authorized_keys, this forces
the execution of the specified command regardless of what the user
requested. This is very useful in conjunction with the new "Match"
option.
+ Add a "PermitOpen" directive to sshd_config(5). This mirrors the
permitopen="..." authorized_keys option, allowing fine-grained
control over the port-forwardings that a user is allowed to
establish.
+ Add optional logging of transactions to sftp-server(8).
+ ssh(1) will now record port numbers for hosts stored in
~/.ssh/known_hosts when a non-standard port has been requested
(closes: #50612).
+ Add an "ExitOnForwardFailure" option to cause ssh(1) to exit (with a
non-zero exit code) when requested port forwardings could not be
established.
+ Extend sshd_config(5) "SubSystem" declarations to allow the
specification of command-line arguments.
+ Replacement of all integer overflow susceptible invocations of
malloc(3) and realloc(3) with overflow-checking equivalents.
+ Many manpage fixes and improvements.
+ Add optional support for OpenSSL hardware accelerators (engines),
enabled using the --with-ssl-engine configure option.
+ Tokens in configuration files may be double-quoted in order to
contain spaces (closes: #319639).
+ Move a debug() call out of a SIGCHLD handler, fixing a hang when the
session exits very quickly (closes: #307890).
+ Fix some incorrect buffer allocation calculations (closes: #410599).
+ ssh-add doesn't ask for a passphrase if key file permissions are too
liberal (closes: #103677).
+ Likewise, ssh doesn't ask either (closes: #99675).
- 4.6/4.6p1 (http://www.openssh.org/txt/release-4.6):
+ sshd now allows the enabling and disabling of authentication methods
on a per user, group, host and network basis via the Match directive
in sshd_config.
+ Fixed an inconsistent check for a terminal when displaying scp
progress meter (closes: #257524).
+ Fix "hang on exit" when background processes are running at the time
of exit on a ttyful/login session (closes: #88337).
* Update to current GSSAPI patch from
http://www.sxw.org.uk/computing/patches/openssh-4.6p1-gsskex-20070312.patch;
install ChangeLog.gssapi.
Diffstat (limited to 'servconf.c')
-rw-r--r-- | servconf.c | 557 |
1 files changed, 448 insertions, 109 deletions
diff --git a/servconf.c b/servconf.c index 60febff99..951bbc4bf 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -1,3 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.c,v 1.170 2007/03/01 10:28:02 dtucker Exp $ */ | ||
1 | /* | 2 | /* |
2 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
3 | * All rights reserved | 4 | * All rights reserved |
@@ -10,24 +11,41 @@ | |||
10 | */ | 11 | */ |
11 | 12 | ||
12 | #include "includes.h" | 13 | #include "includes.h" |
13 | RCSID("$OpenBSD: servconf.c,v 1.146 2005/12/08 18:34:11 reyk Exp $"); | ||
14 | 14 | ||
15 | #include <sys/types.h> | ||
16 | #include <sys/socket.h> | ||
17 | |||
18 | #include <netdb.h> | ||
19 | #include <pwd.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | #include <signal.h> | ||
24 | #include <unistd.h> | ||
25 | #include <stdarg.h> | ||
26 | |||
27 | #include "xmalloc.h" | ||
15 | #include "ssh.h" | 28 | #include "ssh.h" |
16 | #include "log.h" | 29 | #include "log.h" |
30 | #include "buffer.h" | ||
17 | #include "servconf.h" | 31 | #include "servconf.h" |
18 | #include "xmalloc.h" | ||
19 | #include "compat.h" | 32 | #include "compat.h" |
20 | #include "pathnames.h" | 33 | #include "pathnames.h" |
21 | #include "misc.h" | 34 | #include "misc.h" |
22 | #include "cipher.h" | 35 | #include "cipher.h" |
36 | #include "key.h" | ||
23 | #include "kex.h" | 37 | #include "kex.h" |
24 | #include "mac.h" | 38 | #include "mac.h" |
39 | #include "match.h" | ||
40 | #include "channels.h" | ||
41 | #include "groupaccess.h" | ||
25 | 42 | ||
26 | static void add_listen_addr(ServerOptions *, char *, u_short); | 43 | static void add_listen_addr(ServerOptions *, char *, u_short); |
27 | static void add_one_listen_addr(ServerOptions *, char *, u_short); | 44 | static void add_one_listen_addr(ServerOptions *, char *, u_short); |
28 | 45 | ||
29 | /* Use of privilege separation or not */ | 46 | /* Use of privilege separation or not */ |
30 | extern int use_privsep; | 47 | extern int use_privsep; |
48 | extern Buffer cfg; | ||
31 | 49 | ||
32 | /* Initializes the server options to their default values. */ | 50 | /* Initializes the server options to their default values. */ |
33 | 51 | ||
@@ -74,6 +92,7 @@ initialize_server_options(ServerOptions *options) | |||
74 | options->gss_authentication=-1; | 92 | options->gss_authentication=-1; |
75 | options->gss_keyex = -1; | 93 | options->gss_keyex = -1; |
76 | options->gss_cleanup_creds = -1; | 94 | options->gss_cleanup_creds = -1; |
95 | options->gss_strict_acceptor = -1; | ||
77 | options->password_authentication = -1; | 96 | options->password_authentication = -1; |
78 | options->kbd_interactive_authentication = -1; | 97 | options->kbd_interactive_authentication = -1; |
79 | options->challenge_response_authentication = -1; | 98 | options->challenge_response_authentication = -1; |
@@ -103,9 +122,8 @@ initialize_server_options(ServerOptions *options) | |||
103 | options->authorized_keys_file2 = NULL; | 122 | options->authorized_keys_file2 = NULL; |
104 | options->num_accept_env = 0; | 123 | options->num_accept_env = 0; |
105 | options->permit_tun = -1; | 124 | options->permit_tun = -1; |
106 | 125 | options->num_permitted_opens = -1; | |
107 | /* Needs to be accessable in many places */ | 126 | options->adm_forced_command = NULL; |
108 | use_privsep = -1; | ||
109 | } | 127 | } |
110 | 128 | ||
111 | void | 129 | void |
@@ -192,6 +210,8 @@ fill_default_server_options(ServerOptions *options) | |||
192 | options->gss_keyex = 0; | 210 | options->gss_keyex = 0; |
193 | if (options->gss_cleanup_creds == -1) | 211 | if (options->gss_cleanup_creds == -1) |
194 | options->gss_cleanup_creds = 1; | 212 | options->gss_cleanup_creds = 1; |
213 | if (options->gss_strict_acceptor == -1) | ||
214 | options->gss_strict_acceptor = 1; | ||
195 | if (options->password_authentication == -1) | 215 | if (options->password_authentication == -1) |
196 | options->password_authentication = 1; | 216 | options->password_authentication = 1; |
197 | if (options->kbd_interactive_authentication == -1) | 217 | if (options->kbd_interactive_authentication == -1) |
@@ -276,118 +296,130 @@ typedef enum { | |||
276 | sBanner, sUseDNS, sHostbasedAuthentication, | 296 | sBanner, sUseDNS, sHostbasedAuthentication, |
277 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, | 297 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, |
278 | sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, | 298 | sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, |
279 | sGssAuthentication, sGssKeyEx, sGssCleanupCreds, | 299 | sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, |
300 | sGssKeyEx, | ||
280 | sAcceptEnv, sPermitTunnel, | 301 | sAcceptEnv, sPermitTunnel, |
302 | sMatch, sPermitOpen, sForceCommand, | ||
281 | sUsePrivilegeSeparation, | 303 | sUsePrivilegeSeparation, |
282 | sDeprecated, sUnsupported | 304 | sDeprecated, sUnsupported |
283 | } ServerOpCodes; | 305 | } ServerOpCodes; |
284 | 306 | ||
307 | #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ | ||
308 | #define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ | ||
309 | #define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) | ||
310 | |||
285 | /* Textual representation of the tokens. */ | 311 | /* Textual representation of the tokens. */ |
286 | static struct { | 312 | static struct { |
287 | const char *name; | 313 | const char *name; |
288 | ServerOpCodes opcode; | 314 | ServerOpCodes opcode; |
315 | u_int flags; | ||
289 | } keywords[] = { | 316 | } keywords[] = { |
290 | /* Portable-specific options */ | 317 | /* Portable-specific options */ |
291 | #ifdef USE_PAM | 318 | #ifdef USE_PAM |
292 | { "usepam", sUsePAM }, | 319 | { "usepam", sUsePAM, SSHCFG_GLOBAL }, |
293 | #else | 320 | #else |
294 | { "usepam", sUnsupported }, | 321 | { "usepam", sUnsupported, SSHCFG_GLOBAL }, |
295 | #endif | 322 | #endif |
296 | { "pamauthenticationviakbdint", sDeprecated }, | 323 | { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, |
297 | /* Standard Options */ | 324 | /* Standard Options */ |
298 | { "port", sPort }, | 325 | { "port", sPort, SSHCFG_GLOBAL }, |
299 | { "hostkey", sHostKeyFile }, | 326 | { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, |
300 | { "hostdsakey", sHostKeyFile }, /* alias */ | 327 | { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ |
301 | { "pidfile", sPidFile }, | 328 | { "pidfile", sPidFile, SSHCFG_GLOBAL }, |
302 | { "serverkeybits", sServerKeyBits }, | 329 | { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL }, |
303 | { "logingracetime", sLoginGraceTime }, | 330 | { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, |
304 | { "keyregenerationinterval", sKeyRegenerationTime }, | 331 | { "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL }, |
305 | { "permitrootlogin", sPermitRootLogin }, | 332 | { "permitrootlogin", sPermitRootLogin, SSHCFG_GLOBAL }, |
306 | { "syslogfacility", sLogFacility }, | 333 | { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, |
307 | { "loglevel", sLogLevel }, | 334 | { "loglevel", sLogLevel, SSHCFG_GLOBAL }, |
308 | { "rhostsauthentication", sDeprecated }, | 335 | { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, |
309 | { "rhostsrsaauthentication", sRhostsRSAAuthentication }, | 336 | { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL }, |
310 | { "hostbasedauthentication", sHostbasedAuthentication }, | 337 | { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, |
311 | { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly }, | 338 | { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_GLOBAL }, |
312 | { "rsaauthentication", sRSAAuthentication }, | 339 | { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL }, |
313 | { "pubkeyauthentication", sPubkeyAuthentication }, | 340 | { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, |
314 | { "dsaauthentication", sPubkeyAuthentication }, /* alias */ | 341 | { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ |
315 | #ifdef KRB5 | 342 | #ifdef KRB5 |
316 | { "kerberosauthentication", sKerberosAuthentication }, | 343 | { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, |
317 | { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, | 344 | { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, |
318 | { "kerberosticketcleanup", sKerberosTicketCleanup }, | 345 | { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, |
319 | #ifdef USE_AFS | 346 | #ifdef USE_AFS |
320 | { "kerberosgetafstoken", sKerberosGetAFSToken }, | 347 | { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, |
321 | #else | 348 | #else |
322 | { "kerberosgetafstoken", sUnsupported }, | 349 | { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, |
323 | #endif | 350 | #endif |
324 | #else | 351 | #else |
325 | { "kerberosauthentication", sUnsupported }, | 352 | { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, |
326 | { "kerberosorlocalpasswd", sUnsupported }, | 353 | { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, |
327 | { "kerberosticketcleanup", sUnsupported }, | 354 | { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, |
328 | { "kerberosgetafstoken", sUnsupported }, | 355 | { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, |
329 | #endif | 356 | #endif |
330 | { "kerberostgtpassing", sUnsupported }, | 357 | { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, |
331 | { "afstokenpassing", sUnsupported }, | 358 | { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, |
332 | #ifdef GSSAPI | 359 | #ifdef GSSAPI |
333 | { "gssapiauthentication", sGssAuthentication }, | 360 | { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, |
334 | { "gssapikeyexchange", sGssKeyEx }, | 361 | { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, |
335 | { "gssapicleanupcredentials", sGssCleanupCreds }, | 362 | { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, |
336 | { "gssapicleanupcreds", sGssCleanupCreds }, | 363 | { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, |
364 | { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, | ||
337 | #else | 365 | #else |
338 | { "gssapiauthentication", sUnsupported }, | 366 | { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, |
339 | { "gssapikeyexchange", sUnsupported }, | 367 | { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, |
340 | { "gssapicleanupcredentials", sUnsupported }, | 368 | { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, |
341 | { "gssapicleanupcreds", sUnsupported }, | 369 | { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, |
370 | { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, | ||
342 | #endif | 371 | #endif |
343 | { "gssusesessionccache", sUnsupported }, | 372 | { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, |
344 | { "gssapiusesessioncredcache", sUnsupported }, | 373 | { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, |
345 | { "passwordauthentication", sPasswordAuthentication }, | 374 | { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, |
346 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication }, | 375 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, |
347 | { "challengeresponseauthentication", sChallengeResponseAuthentication }, | 376 | { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, |
348 | { "skeyauthentication", sChallengeResponseAuthentication }, /* alias */ | 377 | { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ |
349 | { "checkmail", sDeprecated }, | 378 | { "checkmail", sDeprecated, SSHCFG_GLOBAL }, |
350 | { "listenaddress", sListenAddress }, | 379 | { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, |
351 | { "addressfamily", sAddressFamily }, | 380 | { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, |
352 | { "printmotd", sPrintMotd }, | 381 | { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, |
353 | { "printlastlog", sPrintLastLog }, | 382 | { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, |
354 | { "ignorerhosts", sIgnoreRhosts }, | 383 | { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, |
355 | { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, | 384 | { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, |
356 | { "x11forwarding", sX11Forwarding }, | 385 | { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, |
357 | { "x11displayoffset", sX11DisplayOffset }, | 386 | { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, |
358 | { "x11uselocalhost", sX11UseLocalhost }, | 387 | { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, |
359 | { "xauthlocation", sXAuthLocation }, | 388 | { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, |
360 | { "strictmodes", sStrictModes }, | 389 | { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, |
361 | { "permitemptypasswords", sEmptyPasswd }, | 390 | { "permitemptypasswords", sEmptyPasswd, SSHCFG_GLOBAL }, |
362 | { "permituserenvironment", sPermitUserEnvironment }, | 391 | { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, |
363 | { "uselogin", sUseLogin }, | 392 | { "uselogin", sUseLogin, SSHCFG_GLOBAL }, |
364 | { "compression", sCompression }, | 393 | { "compression", sCompression, SSHCFG_GLOBAL }, |
365 | { "tcpkeepalive", sTCPKeepAlive }, | 394 | { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, |
366 | { "keepalive", sTCPKeepAlive }, /* obsolete alias */ | 395 | { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ |
367 | { "allowtcpforwarding", sAllowTcpForwarding }, | 396 | { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, |
368 | { "allowusers", sAllowUsers }, | 397 | { "allowusers", sAllowUsers, SSHCFG_GLOBAL }, |
369 | { "denyusers", sDenyUsers }, | 398 | { "denyusers", sDenyUsers, SSHCFG_GLOBAL }, |
370 | { "allowgroups", sAllowGroups }, | 399 | { "allowgroups", sAllowGroups, SSHCFG_GLOBAL }, |
371 | { "denygroups", sDenyGroups }, | 400 | { "denygroups", sDenyGroups, SSHCFG_GLOBAL }, |
372 | { "ciphers", sCiphers }, | 401 | { "ciphers", sCiphers, SSHCFG_GLOBAL }, |
373 | { "macs", sMacs }, | 402 | { "macs", sMacs, SSHCFG_GLOBAL }, |
374 | { "protocol", sProtocol }, | 403 | { "protocol", sProtocol, SSHCFG_GLOBAL }, |
375 | { "gatewayports", sGatewayPorts }, | 404 | { "gatewayports", sGatewayPorts, SSHCFG_ALL }, |
376 | { "subsystem", sSubsystem }, | 405 | { "subsystem", sSubsystem, SSHCFG_GLOBAL }, |
377 | { "maxstartups", sMaxStartups }, | 406 | { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, |
378 | { "maxauthtries", sMaxAuthTries }, | 407 | { "maxauthtries", sMaxAuthTries, SSHCFG_GLOBAL }, |
379 | { "banner", sBanner }, | 408 | { "banner", sBanner, SSHCFG_ALL }, |
380 | { "usedns", sUseDNS }, | 409 | { "usedns", sUseDNS, SSHCFG_GLOBAL }, |
381 | { "verifyreversemapping", sDeprecated }, | 410 | { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, |
382 | { "reversemappingcheck", sDeprecated }, | 411 | { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, |
383 | { "clientaliveinterval", sClientAliveInterval }, | 412 | { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, |
384 | { "clientalivecountmax", sClientAliveCountMax }, | 413 | { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, |
385 | { "authorizedkeysfile", sAuthorizedKeysFile }, | 414 | { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, |
386 | { "authorizedkeysfile2", sAuthorizedKeysFile2 }, | 415 | { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, |
387 | { "useprivilegeseparation", sUsePrivilegeSeparation}, | 416 | { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL }, |
388 | { "acceptenv", sAcceptEnv }, | 417 | { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, |
389 | { "permittunnel", sPermitTunnel }, | 418 | { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, |
390 | { NULL, sBadOption } | 419 | { "match", sMatch, SSHCFG_ALL }, |
420 | { "permitopen", sPermitOpen, SSHCFG_ALL }, | ||
421 | { "forcecommand", sForceCommand, SSHCFG_ALL }, | ||
422 | { NULL, sBadOption, 0 } | ||
391 | }; | 423 | }; |
392 | 424 | ||
393 | /* | 425 | /* |
@@ -396,13 +428,15 @@ static struct { | |||
396 | 428 | ||
397 | static ServerOpCodes | 429 | static ServerOpCodes |
398 | parse_token(const char *cp, const char *filename, | 430 | parse_token(const char *cp, const char *filename, |
399 | int linenum) | 431 | int linenum, u_int *flags) |
400 | { | 432 | { |
401 | u_int i; | 433 | u_int i; |
402 | 434 | ||
403 | for (i = 0; keywords[i].name; i++) | 435 | for (i = 0; keywords[i].name; i++) |
404 | if (strcasecmp(cp, keywords[i].name) == 0) | 436 | if (strcasecmp(cp, keywords[i].name) == 0) { |
437 | *flags = keywords[i].flags; | ||
405 | return keywords[i].opcode; | 438 | return keywords[i].opcode; |
439 | } | ||
406 | 440 | ||
407 | error("%s: line %d: Bad configuration option: %s", | 441 | error("%s: line %d: Bad configuration option: %s", |
408 | filename, linenum, cp); | 442 | filename, linenum, cp); |
@@ -447,18 +481,171 @@ add_one_listen_addr(ServerOptions *options, char *addr, u_short port) | |||
447 | options->listen_addrs = aitop; | 481 | options->listen_addrs = aitop; |
448 | } | 482 | } |
449 | 483 | ||
484 | /* | ||
485 | * The strategy for the Match blocks is that the config file is parsed twice. | ||
486 | * | ||
487 | * The first time is at startup. activep is initialized to 1 and the | ||
488 | * directives in the global context are processed and acted on. Hitting a | ||
489 | * Match directive unsets activep and the directives inside the block are | ||
490 | * checked for syntax only. | ||
491 | * | ||
492 | * The second time is after a connection has been established but before | ||
493 | * authentication. activep is initialized to 2 and global config directives | ||
494 | * are ignored since they have already been processed. If the criteria in a | ||
495 | * Match block is met, activep is set and the subsequent directives | ||
496 | * processed and actioned until EOF or another Match block unsets it. Any | ||
497 | * options set are copied into the main server config. | ||
498 | * | ||
499 | * Potential additions/improvements: | ||
500 | * - Add Match support for pre-kex directives, eg Protocol, Ciphers. | ||
501 | * | ||
502 | * - Add a Tag directive (idea from David Leonard) ala pf, eg: | ||
503 | * Match Address 192.168.0.* | ||
504 | * Tag trusted | ||
505 | * Match Group wheel | ||
506 | * Tag trusted | ||
507 | * Match Tag trusted | ||
508 | * AllowTcpForwarding yes | ||
509 | * GatewayPorts clientspecified | ||
510 | * [...] | ||
511 | * | ||
512 | * - Add a PermittedChannelRequests directive | ||
513 | * Match Group shell | ||
514 | * PermittedChannelRequests session,forwarded-tcpip | ||
515 | */ | ||
516 | |||
517 | static int | ||
518 | match_cfg_line_group(const char *grps, int line, const char *user) | ||
519 | { | ||
520 | int result = 0; | ||
521 | u_int ngrps = 0; | ||
522 | char *arg, *p, *cp, *grplist[MAX_MATCH_GROUPS]; | ||
523 | struct passwd *pw; | ||
524 | |||
525 | /* | ||
526 | * Even if we do not have a user yet, we still need to check for | ||
527 | * valid syntax. | ||
528 | */ | ||
529 | arg = cp = xstrdup(grps); | ||
530 | while ((p = strsep(&cp, ",")) != NULL && *p != '\0') { | ||
531 | if (ngrps >= MAX_MATCH_GROUPS) { | ||
532 | error("line %d: too many groups in Match Group", line); | ||
533 | result = -1; | ||
534 | goto out; | ||
535 | } | ||
536 | grplist[ngrps++] = p; | ||
537 | } | ||
538 | |||
539 | if (user == NULL) | ||
540 | goto out; | ||
541 | |||
542 | if ((pw = getpwnam(user)) == NULL) { | ||
543 | debug("Can't match group at line %d because user %.100s does " | ||
544 | "not exist", line, user); | ||
545 | } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { | ||
546 | debug("Can't Match group because user %.100s not in any group " | ||
547 | "at line %d", user, line); | ||
548 | } else if (ga_match(grplist, ngrps) != 1) { | ||
549 | debug("user %.100s does not match group %.100s at line %d", | ||
550 | user, arg, line); | ||
551 | } else { | ||
552 | debug("user %.100s matched group %.100s at line %d", user, | ||
553 | arg, line); | ||
554 | result = 1; | ||
555 | } | ||
556 | out: | ||
557 | ga_free(); | ||
558 | xfree(arg); | ||
559 | return result; | ||
560 | } | ||
561 | |||
562 | static int | ||
563 | match_cfg_line(char **condition, int line, const char *user, const char *host, | ||
564 | const char *address) | ||
565 | { | ||
566 | int result = 1; | ||
567 | char *arg, *attrib, *cp = *condition; | ||
568 | size_t len; | ||
569 | |||
570 | if (user == NULL) | ||
571 | debug3("checking syntax for 'Match %s'", cp); | ||
572 | else | ||
573 | debug3("checking match for '%s' user %s host %s addr %s", cp, | ||
574 | user ? user : "(null)", host ? host : "(null)", | ||
575 | address ? address : "(null)"); | ||
576 | |||
577 | while ((attrib = strdelim(&cp)) && *attrib != '\0') { | ||
578 | if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { | ||
579 | error("Missing Match criteria for %s", attrib); | ||
580 | return -1; | ||
581 | } | ||
582 | len = strlen(arg); | ||
583 | if (strcasecmp(attrib, "user") == 0) { | ||
584 | if (!user) { | ||
585 | result = 0; | ||
586 | continue; | ||
587 | } | ||
588 | if (match_pattern_list(user, arg, len, 0) != 1) | ||
589 | result = 0; | ||
590 | else | ||
591 | debug("user %.100s matched 'User %.100s' at " | ||
592 | "line %d", user, arg, line); | ||
593 | } else if (strcasecmp(attrib, "group") == 0) { | ||
594 | switch (match_cfg_line_group(arg, line, user)) { | ||
595 | case -1: | ||
596 | return -1; | ||
597 | case 0: | ||
598 | result = 0; | ||
599 | } | ||
600 | } else if (strcasecmp(attrib, "host") == 0) { | ||
601 | if (!host) { | ||
602 | result = 0; | ||
603 | continue; | ||
604 | } | ||
605 | if (match_hostname(host, arg, len) != 1) | ||
606 | result = 0; | ||
607 | else | ||
608 | debug("connection from %.100s matched 'Host " | ||
609 | "%.100s' at line %d", host, arg, line); | ||
610 | } else if (strcasecmp(attrib, "address") == 0) { | ||
611 | debug("address '%s' arg '%s'", address, arg); | ||
612 | if (!address) { | ||
613 | result = 0; | ||
614 | continue; | ||
615 | } | ||
616 | if (match_hostname(address, arg, len) != 1) | ||
617 | result = 0; | ||
618 | else | ||
619 | debug("connection from %.100s matched 'Address " | ||
620 | "%.100s' at line %d", address, arg, line); | ||
621 | } else { | ||
622 | error("Unsupported Match attribute %s", attrib); | ||
623 | return -1; | ||
624 | } | ||
625 | } | ||
626 | if (user != NULL) | ||
627 | debug3("match %sfound", result ? "" : "not "); | ||
628 | *condition = cp; | ||
629 | return result; | ||
630 | } | ||
631 | |||
632 | #define WHITESPACE " \t\r\n" | ||
633 | |||
450 | int | 634 | int |
451 | process_server_config_line(ServerOptions *options, char *line, | 635 | process_server_config_line(ServerOptions *options, char *line, |
452 | const char *filename, int linenum) | 636 | const char *filename, int linenum, int *activep, const char *user, |
637 | const char *host, const char *address) | ||
453 | { | 638 | { |
454 | char *cp, **charptr, *arg, *p; | 639 | char *cp, **charptr, *arg, *p; |
455 | int *intptr, value, n; | 640 | int cmdline = 0, *intptr, value, n; |
456 | ServerOpCodes opcode; | 641 | ServerOpCodes opcode; |
457 | u_short port; | 642 | u_short port; |
458 | u_int i; | 643 | u_int i, flags = 0; |
644 | size_t len; | ||
459 | 645 | ||
460 | cp = line; | 646 | cp = line; |
461 | arg = strdelim(&cp); | 647 | if ((arg = strdelim(&cp)) == NULL) |
648 | return 0; | ||
462 | /* Ignore leading whitespace */ | 649 | /* Ignore leading whitespace */ |
463 | if (*arg == '\0') | 650 | if (*arg == '\0') |
464 | arg = strdelim(&cp); | 651 | arg = strdelim(&cp); |
@@ -466,7 +653,25 @@ process_server_config_line(ServerOptions *options, char *line, | |||
466 | return 0; | 653 | return 0; |
467 | intptr = NULL; | 654 | intptr = NULL; |
468 | charptr = NULL; | 655 | charptr = NULL; |
469 | opcode = parse_token(arg, filename, linenum); | 656 | opcode = parse_token(arg, filename, linenum, &flags); |
657 | |||
658 | if (activep == NULL) { /* We are processing a command line directive */ | ||
659 | cmdline = 1; | ||
660 | activep = &cmdline; | ||
661 | } | ||
662 | if (*activep && opcode != sMatch) | ||
663 | debug3("%s:%d setting %s %s", filename, linenum, arg, cp); | ||
664 | if (*activep == 0 && !(flags & SSHCFG_MATCH)) { | ||
665 | if (user == NULL) { | ||
666 | fatal("%s line %d: Directive '%s' is not allowed " | ||
667 | "within a Match block", filename, linenum, arg); | ||
668 | } else { /* this is a directive we have already processed */ | ||
669 | while (arg) | ||
670 | arg = strdelim(&cp); | ||
671 | return 0; | ||
672 | } | ||
673 | } | ||
674 | |||
470 | switch (opcode) { | 675 | switch (opcode) { |
471 | /* Portable-specific options */ | 676 | /* Portable-specific options */ |
472 | case sUsePAM: | 677 | case sUsePAM: |
@@ -504,7 +709,7 @@ parse_int: | |||
504 | fatal("%s line %d: missing integer value.", | 709 | fatal("%s line %d: missing integer value.", |
505 | filename, linenum); | 710 | filename, linenum); |
506 | value = atoi(arg); | 711 | value = atoi(arg); |
507 | if (*intptr == -1) | 712 | if (*activep && *intptr == -1) |
508 | *intptr = value; | 713 | *intptr = value; |
509 | break; | 714 | break; |
510 | 715 | ||
@@ -584,7 +789,7 @@ parse_filename: | |||
584 | if (!arg || *arg == '\0') | 789 | if (!arg || *arg == '\0') |
585 | fatal("%s line %d: missing file name.", | 790 | fatal("%s line %d: missing file name.", |
586 | filename, linenum); | 791 | filename, linenum); |
587 | if (*charptr == NULL) { | 792 | if (*activep && *charptr == NULL) { |
588 | *charptr = tilde_expand_filename(arg, getuid()); | 793 | *charptr = tilde_expand_filename(arg, getuid()); |
589 | /* increase optional counter */ | 794 | /* increase optional counter */ |
590 | if (intptr != NULL) | 795 | if (intptr != NULL) |
@@ -635,7 +840,7 @@ parse_flag: | |||
635 | else | 840 | else |
636 | fatal("%s line %d: Bad yes/no argument: %s", | 841 | fatal("%s line %d: Bad yes/no argument: %s", |
637 | filename, linenum, arg); | 842 | filename, linenum, arg); |
638 | if (*intptr == -1) | 843 | if (*activep && *intptr == -1) |
639 | *intptr = value; | 844 | *intptr = value; |
640 | break; | 845 | break; |
641 | 846 | ||
@@ -691,6 +896,10 @@ parse_flag: | |||
691 | intptr = &options->gss_cleanup_creds; | 896 | intptr = &options->gss_cleanup_creds; |
692 | goto parse_flag; | 897 | goto parse_flag; |
693 | 898 | ||
899 | case sGssStrictAcceptor: | ||
900 | intptr = &options->gss_strict_acceptor; | ||
901 | goto parse_flag; | ||
902 | |||
694 | case sPasswordAuthentication: | 903 | case sPasswordAuthentication: |
695 | intptr = &options->password_authentication; | 904 | intptr = &options->password_authentication; |
696 | goto parse_flag; | 905 | goto parse_flag; |
@@ -783,7 +992,7 @@ parse_flag: | |||
783 | else | 992 | else |
784 | fatal("%s line %d: Bad yes/no/clientspecified " | 993 | fatal("%s line %d: Bad yes/no/clientspecified " |
785 | "argument: %s", filename, linenum, arg); | 994 | "argument: %s", filename, linenum, arg); |
786 | if (*intptr == -1) | 995 | if (*activep && *intptr == -1) |
787 | *intptr = value; | 996 | *intptr = value; |
788 | break; | 997 | break; |
789 | 998 | ||
@@ -834,7 +1043,7 @@ parse_flag: | |||
834 | case sDenyUsers: | 1043 | case sDenyUsers: |
835 | while ((arg = strdelim(&cp)) && *arg != '\0') { | 1044 | while ((arg = strdelim(&cp)) && *arg != '\0') { |
836 | if (options->num_deny_users >= MAX_DENY_USERS) | 1045 | if (options->num_deny_users >= MAX_DENY_USERS) |
837 | fatal( "%s line %d: too many deny users.", | 1046 | fatal("%s line %d: too many deny users.", |
838 | filename, linenum); | 1047 | filename, linenum); |
839 | options->deny_users[options->num_deny_users++] = | 1048 | options->deny_users[options->num_deny_users++] = |
840 | xstrdup(arg); | 1049 | xstrdup(arg); |
@@ -904,6 +1113,10 @@ parse_flag: | |||
904 | if (!arg || *arg == '\0') | 1113 | if (!arg || *arg == '\0') |
905 | fatal("%s line %d: Missing subsystem name.", | 1114 | fatal("%s line %d: Missing subsystem name.", |
906 | filename, linenum); | 1115 | filename, linenum); |
1116 | if (!*activep) { | ||
1117 | arg = strdelim(&cp); | ||
1118 | break; | ||
1119 | } | ||
907 | for (i = 0; i < options->num_subsystems; i++) | 1120 | for (i = 0; i < options->num_subsystems; i++) |
908 | if (strcmp(arg, options->subsystem_name[i]) == 0) | 1121 | if (strcmp(arg, options->subsystem_name[i]) == 0) |
909 | fatal("%s line %d: Subsystem '%s' already defined.", | 1122 | fatal("%s line %d: Subsystem '%s' already defined.", |
@@ -914,6 +1127,17 @@ parse_flag: | |||
914 | fatal("%s line %d: Missing subsystem command.", | 1127 | fatal("%s line %d: Missing subsystem command.", |
915 | filename, linenum); | 1128 | filename, linenum); |
916 | options->subsystem_command[options->num_subsystems] = xstrdup(arg); | 1129 | options->subsystem_command[options->num_subsystems] = xstrdup(arg); |
1130 | |||
1131 | /* Collect arguments (separate to executable) */ | ||
1132 | p = xstrdup(arg); | ||
1133 | len = strlen(p) + 1; | ||
1134 | while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { | ||
1135 | len += 1 + strlen(arg); | ||
1136 | p = xrealloc(p, 1, len); | ||
1137 | strlcat(p, " ", len); | ||
1138 | strlcat(p, arg, len); | ||
1139 | } | ||
1140 | options->subsystem_args[options->num_subsystems] = p; | ||
917 | options->num_subsystems++; | 1141 | options->num_subsystems++; |
918 | break; | 1142 | break; |
919 | 1143 | ||
@@ -954,7 +1178,7 @@ parse_flag: | |||
954 | */ | 1178 | */ |
955 | case sAuthorizedKeysFile: | 1179 | case sAuthorizedKeysFile: |
956 | case sAuthorizedKeysFile2: | 1180 | case sAuthorizedKeysFile2: |
957 | charptr = (opcode == sAuthorizedKeysFile ) ? | 1181 | charptr = (opcode == sAuthorizedKeysFile) ? |
958 | &options->authorized_keys_file : | 1182 | &options->authorized_keys_file : |
959 | &options->authorized_keys_file2; | 1183 | &options->authorized_keys_file2; |
960 | goto parse_filename; | 1184 | goto parse_filename; |
@@ -975,6 +1199,8 @@ parse_flag: | |||
975 | if (options->num_accept_env >= MAX_ACCEPT_ENV) | 1199 | if (options->num_accept_env >= MAX_ACCEPT_ENV) |
976 | fatal("%s line %d: too many allow env.", | 1200 | fatal("%s line %d: too many allow env.", |
977 | filename, linenum); | 1201 | filename, linenum); |
1202 | if (!*activep) | ||
1203 | break; | ||
978 | options->accept_env[options->num_accept_env++] = | 1204 | options->accept_env[options->num_accept_env++] = |
979 | xstrdup(arg); | 1205 | xstrdup(arg); |
980 | } | 1206 | } |
@@ -1002,6 +1228,56 @@ parse_flag: | |||
1002 | *intptr = value; | 1228 | *intptr = value; |
1003 | break; | 1229 | break; |
1004 | 1230 | ||
1231 | case sMatch: | ||
1232 | if (cmdline) | ||
1233 | fatal("Match directive not supported as a command-line " | ||
1234 | "option"); | ||
1235 | value = match_cfg_line(&cp, linenum, user, host, address); | ||
1236 | if (value < 0) | ||
1237 | fatal("%s line %d: Bad Match condition", filename, | ||
1238 | linenum); | ||
1239 | *activep = value; | ||
1240 | break; | ||
1241 | |||
1242 | case sPermitOpen: | ||
1243 | arg = strdelim(&cp); | ||
1244 | if (!arg || *arg == '\0') | ||
1245 | fatal("%s line %d: missing PermitOpen specification", | ||
1246 | filename, linenum); | ||
1247 | n = options->num_permitted_opens; /* modified later */ | ||
1248 | if (strcmp(arg, "any") == 0) { | ||
1249 | if (*activep && n == -1) { | ||
1250 | channel_clear_adm_permitted_opens(); | ||
1251 | options->num_permitted_opens = 0; | ||
1252 | } | ||
1253 | break; | ||
1254 | } | ||
1255 | if (*activep && n == -1) | ||
1256 | channel_clear_adm_permitted_opens(); | ||
1257 | for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { | ||
1258 | p = hpdelim(&arg); | ||
1259 | if (p == NULL) | ||
1260 | fatal("%s line %d: missing host in PermitOpen", | ||
1261 | filename, linenum); | ||
1262 | p = cleanhostname(p); | ||
1263 | if (arg == NULL || (port = a2port(arg)) == 0) | ||
1264 | fatal("%s line %d: bad port number in " | ||
1265 | "PermitOpen", filename, linenum); | ||
1266 | if (*activep && n == -1) | ||
1267 | options->num_permitted_opens = | ||
1268 | channel_add_adm_permitted_opens(p, port); | ||
1269 | } | ||
1270 | break; | ||
1271 | |||
1272 | case sForceCommand: | ||
1273 | if (cp == NULL) | ||
1274 | fatal("%.200s line %d: Missing argument.", filename, | ||
1275 | linenum); | ||
1276 | len = strspn(cp, WHITESPACE); | ||
1277 | if (*activep && options->adm_forced_command == NULL) | ||
1278 | options->adm_forced_command = xstrdup(cp + len); | ||
1279 | return 0; | ||
1280 | |||
1005 | case sDeprecated: | 1281 | case sDeprecated: |
1006 | logit("%s line %d: Deprecated option %s", | 1282 | logit("%s line %d: Deprecated option %s", |
1007 | filename, linenum, arg); | 1283 | filename, linenum, arg); |
@@ -1058,22 +1334,85 @@ load_server_config(const char *filename, Buffer *conf) | |||
1058 | } | 1334 | } |
1059 | 1335 | ||
1060 | void | 1336 | void |
1061 | parse_server_config(ServerOptions *options, const char *filename, Buffer *conf) | 1337 | parse_server_match_config(ServerOptions *options, const char *user, |
1338 | const char *host, const char *address) | ||
1062 | { | 1339 | { |
1063 | int linenum, bad_options = 0; | 1340 | ServerOptions mo; |
1341 | |||
1342 | initialize_server_options(&mo); | ||
1343 | parse_server_config(&mo, "reprocess config", &cfg, user, host, address); | ||
1344 | copy_set_server_options(options, &mo, 0); | ||
1345 | } | ||
1346 | |||
1347 | /* Helper macros */ | ||
1348 | #define M_CP_INTOPT(n) do {\ | ||
1349 | if (src->n != -1) \ | ||
1350 | dst->n = src->n; \ | ||
1351 | } while (0) | ||
1352 | #define M_CP_STROPT(n) do {\ | ||
1353 | if (src->n != NULL) { \ | ||
1354 | if (dst->n != NULL) \ | ||
1355 | xfree(dst->n); \ | ||
1356 | dst->n = src->n; \ | ||
1357 | } \ | ||
1358 | } while(0) | ||
1359 | |||
1360 | /* | ||
1361 | * Copy any supported values that are set. | ||
1362 | * | ||
1363 | * If the preauth flag is set, we do not bother copying the the string or | ||
1364 | * array values that are not used pre-authentication, because any that we | ||
1365 | * do use must be explictly sent in mm_getpwnamallow(). | ||
1366 | */ | ||
1367 | void | ||
1368 | copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) | ||
1369 | { | ||
1370 | M_CP_INTOPT(password_authentication); | ||
1371 | M_CP_INTOPT(gss_authentication); | ||
1372 | M_CP_INTOPT(rsa_authentication); | ||
1373 | M_CP_INTOPT(pubkey_authentication); | ||
1374 | M_CP_INTOPT(kerberos_authentication); | ||
1375 | M_CP_INTOPT(hostbased_authentication); | ||
1376 | M_CP_INTOPT(kbd_interactive_authentication); | ||
1377 | |||
1378 | M_CP_INTOPT(allow_tcp_forwarding); | ||
1379 | M_CP_INTOPT(gateway_ports); | ||
1380 | M_CP_INTOPT(x11_display_offset); | ||
1381 | M_CP_INTOPT(x11_forwarding); | ||
1382 | M_CP_INTOPT(x11_use_localhost); | ||
1383 | |||
1384 | M_CP_STROPT(banner); | ||
1385 | if (preauth) | ||
1386 | return; | ||
1387 | M_CP_STROPT(adm_forced_command); | ||
1388 | } | ||
1389 | |||
1390 | #undef M_CP_INTOPT | ||
1391 | #undef M_CP_STROPT | ||
1392 | |||
1393 | void | ||
1394 | parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, | ||
1395 | const char *user, const char *host, const char *address) | ||
1396 | { | ||
1397 | int active, linenum, bad_options = 0; | ||
1064 | char *cp, *obuf, *cbuf; | 1398 | char *cp, *obuf, *cbuf; |
1065 | 1399 | ||
1066 | debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); | 1400 | debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); |
1067 | 1401 | ||
1068 | obuf = cbuf = xstrdup(buffer_ptr(conf)); | 1402 | obuf = cbuf = xstrdup(buffer_ptr(conf)); |
1403 | active = user ? 0 : 1; | ||
1069 | linenum = 1; | 1404 | linenum = 1; |
1070 | while ((cp = strsep(&cbuf, "\n")) != NULL) { | 1405 | while ((cp = strsep(&cbuf, "\n")) != NULL) { |
1071 | if (process_server_config_line(options, cp, filename, | 1406 | if (process_server_config_line(options, cp, filename, |
1072 | linenum++) != 0) | 1407 | linenum++, &active, user, host, address) != 0) |
1073 | bad_options++; | 1408 | bad_options++; |
1074 | } | 1409 | } |
1075 | xfree(obuf); | 1410 | xfree(obuf); |
1076 | if (bad_options > 0) | 1411 | if (bad_options > 0) |
1077 | fatal("%s: terminating, %d bad configuration options", | 1412 | fatal("%s: terminating, %d bad configuration options", |
1078 | filename, bad_options); | 1413 | filename, bad_options); |
1414 | |||
1415 | /* challenge-response is implemented via keyboard interactive */ | ||
1416 | if (options->challenge_response_authentication == 1) | ||
1417 | options->kbd_interactive_authentication = 1; | ||
1079 | } | 1418 | } |