diff options
author | djm@openbsd.org <djm@openbsd.org> | 2016-03-07 19:02:43 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2016-03-08 06:20:35 +1100 |
commit | 95767262caa6692eff1e1565be1f5cb297949a89 (patch) | |
tree | 1055360a328d0998dabb966f2e1002389f8c6c41 /auth.c | |
parent | af0bb38ffd1f2c4f9f43b0029be2efe922815255 (diff) |
upstream commit
refactor canohost.c: move functions that cache results closer
to the places that use them (authn and session code). After this, no state is
cached in canohost.c
feedback and ok markus@
Upstream-ID: 5f2e4df88d4803fc8ec59ec53629105e23ce625e
Diffstat (limited to 'auth.c')
-rw-r--r-- | auth.c | 145 |
1 files changed, 135 insertions, 10 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth.c,v 1.113 2015/08/21 03:42:19 djm Exp $ */ | 1 | /* $OpenBSD: auth.c,v 1.114 2016/03/07 19:02:43 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | #include <sys/types.h> | 28 | #include <sys/types.h> |
29 | #include <sys/stat.h> | 29 | #include <sys/stat.h> |
30 | #include <sys/socket.h> | ||
30 | 31 | ||
31 | #include <netinet/in.h> | 32 | #include <netinet/in.h> |
32 | 33 | ||
@@ -50,6 +51,7 @@ | |||
50 | #include <string.h> | 51 | #include <string.h> |
51 | #include <unistd.h> | 52 | #include <unistd.h> |
52 | #include <limits.h> | 53 | #include <limits.h> |
54 | #include <netdb.h> | ||
53 | 55 | ||
54 | #include "xmalloc.h" | 56 | #include "xmalloc.h" |
55 | #include "match.h" | 57 | #include "match.h" |
@@ -97,6 +99,7 @@ int auth_debug_init; | |||
97 | int | 99 | int |
98 | allowed_user(struct passwd * pw) | 100 | allowed_user(struct passwd * pw) |
99 | { | 101 | { |
102 | struct ssh *ssh = active_state; /* XXX */ | ||
100 | struct stat st; | 103 | struct stat st; |
101 | const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; | 104 | const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; |
102 | u_int i; | 105 | u_int i; |
@@ -182,8 +185,8 @@ allowed_user(struct passwd * pw) | |||
182 | 185 | ||
183 | if (options.num_deny_users > 0 || options.num_allow_users > 0 || | 186 | if (options.num_deny_users > 0 || options.num_allow_users > 0 || |
184 | options.num_deny_groups > 0 || options.num_allow_groups > 0) { | 187 | options.num_deny_groups > 0 || options.num_allow_groups > 0) { |
185 | hostname = get_canonical_hostname(options.use_dns); | 188 | hostname = auth_get_canonical_hostname(ssh, options.use_dns); |
186 | ipaddr = get_remote_ipaddr(); | 189 | ipaddr = ssh_remote_ipaddr(ssh); |
187 | } | 190 | } |
188 | 191 | ||
189 | /* Return false if user is listed in DenyUsers */ | 192 | /* Return false if user is listed in DenyUsers */ |
@@ -274,6 +277,7 @@ void | |||
274 | auth_log(Authctxt *authctxt, int authenticated, int partial, | 277 | auth_log(Authctxt *authctxt, int authenticated, int partial, |
275 | const char *method, const char *submethod) | 278 | const char *method, const char *submethod) |
276 | { | 279 | { |
280 | struct ssh *ssh = active_state; /* XXX */ | ||
277 | void (*authlog) (const char *fmt,...) = verbose; | 281 | void (*authlog) (const char *fmt,...) = verbose; |
278 | char *authmsg; | 282 | char *authmsg; |
279 | 283 | ||
@@ -300,8 +304,8 @@ auth_log(Authctxt *authctxt, int authenticated, int partial, | |||
300 | submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, | 304 | submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, |
301 | authctxt->valid ? "" : "invalid user ", | 305 | authctxt->valid ? "" : "invalid user ", |
302 | authctxt->user, | 306 | authctxt->user, |
303 | get_remote_ipaddr(), | 307 | ssh_remote_ipaddr(ssh), |
304 | get_remote_port(), | 308 | ssh_remote_port(ssh), |
305 | compat20 ? "ssh2" : "ssh1", | 309 | compat20 ? "ssh2" : "ssh1", |
306 | authctxt->info != NULL ? ": " : "", | 310 | authctxt->info != NULL ? ": " : "", |
307 | authctxt->info != NULL ? authctxt->info : ""); | 311 | authctxt->info != NULL ? authctxt->info : ""); |
@@ -331,12 +335,14 @@ auth_log(Authctxt *authctxt, int authenticated, int partial, | |||
331 | void | 335 | void |
332 | auth_maxtries_exceeded(Authctxt *authctxt) | 336 | auth_maxtries_exceeded(Authctxt *authctxt) |
333 | { | 337 | { |
338 | struct ssh *ssh = active_state; /* XXX */ | ||
339 | |||
334 | error("maximum authentication attempts exceeded for " | 340 | error("maximum authentication attempts exceeded for " |
335 | "%s%.100s from %.200s port %d %s", | 341 | "%s%.100s from %.200s port %d %s", |
336 | authctxt->valid ? "" : "invalid user ", | 342 | authctxt->valid ? "" : "invalid user ", |
337 | authctxt->user, | 343 | authctxt->user, |
338 | get_remote_ipaddr(), | 344 | ssh_remote_ipaddr(ssh), |
339 | get_remote_port(), | 345 | ssh_remote_port(ssh), |
340 | compat20 ? "ssh2" : "ssh1"); | 346 | compat20 ? "ssh2" : "ssh1"); |
341 | packet_disconnect("Too many authentication failures"); | 347 | packet_disconnect("Too many authentication failures"); |
342 | /* NOTREACHED */ | 348 | /* NOTREACHED */ |
@@ -348,6 +354,8 @@ auth_maxtries_exceeded(Authctxt *authctxt) | |||
348 | int | 354 | int |
349 | auth_root_allowed(const char *method) | 355 | auth_root_allowed(const char *method) |
350 | { | 356 | { |
357 | struct ssh *ssh = active_state; /* XXX */ | ||
358 | |||
351 | switch (options.permit_root_login) { | 359 | switch (options.permit_root_login) { |
352 | case PERMIT_YES: | 360 | case PERMIT_YES: |
353 | return 1; | 361 | return 1; |
@@ -364,7 +372,8 @@ auth_root_allowed(const char *method) | |||
364 | } | 372 | } |
365 | break; | 373 | break; |
366 | } | 374 | } |
367 | logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); | 375 | logit("ROOT LOGIN REFUSED FROM %.200s port %d", |
376 | ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); | ||
368 | return 0; | 377 | return 0; |
369 | } | 378 | } |
370 | 379 | ||
@@ -604,6 +613,7 @@ auth_openprincipals(const char *file, struct passwd *pw, int strict_modes) | |||
604 | struct passwd * | 613 | struct passwd * |
605 | getpwnamallow(const char *user) | 614 | getpwnamallow(const char *user) |
606 | { | 615 | { |
616 | struct ssh *ssh = active_state; /* XXX */ | ||
607 | #ifdef HAVE_LOGIN_CAP | 617 | #ifdef HAVE_LOGIN_CAP |
608 | extern login_cap_t *lc; | 618 | extern login_cap_t *lc; |
609 | #ifdef BSD_AUTH | 619 | #ifdef BSD_AUTH |
@@ -639,8 +649,8 @@ getpwnamallow(const char *user) | |||
639 | } | 649 | } |
640 | #endif | 650 | #endif |
641 | if (pw == NULL) { | 651 | if (pw == NULL) { |
642 | logit("Invalid user %.100s from %.100s", | 652 | logit("Invalid user %.100s from %.100s port %d", |
643 | user, get_remote_ipaddr()); | 653 | user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
644 | #ifdef CUSTOM_FAILED_LOGIN | 654 | #ifdef CUSTOM_FAILED_LOGIN |
645 | record_failed_login(user, | 655 | record_failed_login(user, |
646 | get_canonical_hostname(options.use_dns), "ssh"); | 656 | get_canonical_hostname(options.use_dns), "ssh"); |
@@ -773,3 +783,118 @@ fakepw(void) | |||
773 | 783 | ||
774 | return (&fake); | 784 | return (&fake); |
775 | } | 785 | } |
786 | |||
787 | /* | ||
788 | * Returns the remote DNS hostname as a string. The returned string must not | ||
789 | * be freed. NB. this will usually trigger a DNS query the first time it is | ||
790 | * called. | ||
791 | * This function does additional checks on the hostname to mitigate some | ||
792 | * attacks on legacy rhosts-style authentication. | ||
793 | * XXX is RhostsRSAAuthentication vulnerable to these? | ||
794 | * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) | ||
795 | */ | ||
796 | |||
797 | static char * | ||
798 | remote_hostname(struct ssh *ssh) | ||
799 | { | ||
800 | struct sockaddr_storage from; | ||
801 | socklen_t fromlen; | ||
802 | struct addrinfo hints, *ai, *aitop; | ||
803 | char name[NI_MAXHOST], ntop2[NI_MAXHOST]; | ||
804 | const char *ntop = ssh_remote_ipaddr(ssh); | ||
805 | |||
806 | /* Get IP address of client. */ | ||
807 | fromlen = sizeof(from); | ||
808 | memset(&from, 0, sizeof(from)); | ||
809 | if (getpeername(ssh_packet_get_connection_in(ssh), | ||
810 | (struct sockaddr *)&from, &fromlen) < 0) { | ||
811 | debug("getpeername failed: %.100s", strerror(errno)); | ||
812 | return strdup(ntop); | ||
813 | } | ||
814 | |||
815 | ipv64_normalise_mapped(&from, &fromlen); | ||
816 | if (from.ss_family == AF_INET6) | ||
817 | fromlen = sizeof(struct sockaddr_in6); | ||
818 | |||
819 | debug3("Trying to reverse map address %.100s.", ntop); | ||
820 | /* Map the IP address to a host name. */ | ||
821 | if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), | ||
822 | NULL, 0, NI_NAMEREQD) != 0) { | ||
823 | /* Host name not found. Use ip address. */ | ||
824 | return strdup(ntop); | ||
825 | } | ||
826 | |||
827 | /* | ||
828 | * if reverse lookup result looks like a numeric hostname, | ||
829 | * someone is trying to trick us by PTR record like following: | ||
830 | * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 | ||
831 | */ | ||
832 | memset(&hints, 0, sizeof(hints)); | ||
833 | hints.ai_socktype = SOCK_DGRAM; /*dummy*/ | ||
834 | hints.ai_flags = AI_NUMERICHOST; | ||
835 | if (getaddrinfo(name, NULL, &hints, &ai) == 0) { | ||
836 | logit("Nasty PTR record \"%s\" is set up for %s, ignoring", | ||
837 | name, ntop); | ||
838 | freeaddrinfo(ai); | ||
839 | return strdup(ntop); | ||
840 | } | ||
841 | |||
842 | /* Names are stored in lowercase. */ | ||
843 | lowercase(name); | ||
844 | |||
845 | /* | ||
846 | * Map it back to an IP address and check that the given | ||
847 | * address actually is an address of this host. This is | ||
848 | * necessary because anyone with access to a name server can | ||
849 | * define arbitrary names for an IP address. Mapping from | ||
850 | * name to IP address can be trusted better (but can still be | ||
851 | * fooled if the intruder has access to the name server of | ||
852 | * the domain). | ||
853 | */ | ||
854 | memset(&hints, 0, sizeof(hints)); | ||
855 | hints.ai_family = from.ss_family; | ||
856 | hints.ai_socktype = SOCK_STREAM; | ||
857 | if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { | ||
858 | logit("reverse mapping checking getaddrinfo for %.700s " | ||
859 | "[%s] failed - POSSIBLE BREAK-IN ATTEMPT!", name, ntop); | ||
860 | return strdup(ntop); | ||
861 | } | ||
862 | /* Look for the address from the list of addresses. */ | ||
863 | for (ai = aitop; ai; ai = ai->ai_next) { | ||
864 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, | ||
865 | sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && | ||
866 | (strcmp(ntop, ntop2) == 0)) | ||
867 | break; | ||
868 | } | ||
869 | freeaddrinfo(aitop); | ||
870 | /* If we reached the end of the list, the address was not there. */ | ||
871 | if (ai == NULL) { | ||
872 | /* Address not found for the host name. */ | ||
873 | logit("Address %.100s maps to %.600s, but this does not " | ||
874 | "map back to the address - POSSIBLE BREAK-IN ATTEMPT!", | ||
875 | ntop, name); | ||
876 | return strdup(ntop); | ||
877 | } | ||
878 | return strdup(name); | ||
879 | } | ||
880 | |||
881 | /* | ||
882 | * Return the canonical name of the host in the other side of the current | ||
883 | * connection. The host name is cached, so it is efficient to call this | ||
884 | * several times. | ||
885 | */ | ||
886 | |||
887 | const char * | ||
888 | auth_get_canonical_hostname(struct ssh *ssh, int use_dns) | ||
889 | { | ||
890 | static char *dnsname; | ||
891 | |||
892 | if (!use_dns) | ||
893 | return ssh_remote_ipaddr(ssh); | ||
894 | else if (dnsname != NULL) | ||
895 | return dnsname; | ||
896 | else { | ||
897 | dnsname = remote_hostname(ssh); | ||
898 | return dnsname; | ||
899 | } | ||
900 | } | ||