diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | auth1.c | 323 |
2 files changed, 200 insertions, 132 deletions
@@ -1,3 +1,10 @@ | |||
1 | 20050618 | ||
2 | - (djm) OpenBSD CVS Sync | ||
3 | - djm@cvs.openbsd.org 2005/05/20 12:57:01; | ||
4 | [auth1.c] split protocol 1 auth methods into separate functions, makes | ||
5 | authloop much more readable; fixes and ok markus@ (portable ok & | ||
6 | polish dtucker@) | ||
7 | |||
1 | 20050617 | 8 | 20050617 |
2 | - (djm) OpenBSD CVS Sync | 9 | - (djm) OpenBSD CVS Sync |
3 | - djm@cvs.openbsd.org 2005/06/16 03:38:36 | 10 | - djm@cvs.openbsd.org 2005/06/16 03:38:36 |
@@ -2738,4 +2745,4 @@ | |||
2738 | - (djm) Trim deprecated options from INSTALL. Mention UsePAM | 2745 | - (djm) Trim deprecated options from INSTALL. Mention UsePAM |
2739 | - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu | 2746 | - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu |
2740 | 2747 | ||
2741 | $Id: ChangeLog,v 1.3824 2005/06/17 11:15:20 dtucker Exp $ | 2748 | $Id: ChangeLog,v 1.3825 2005/06/18 21:31:37 djm Exp $ |
@@ -10,7 +10,7 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include "includes.h" | 12 | #include "includes.h" |
13 | RCSID("$OpenBSD: auth1.c,v 1.59 2004/07/28 09:40:29 markus Exp $"); | 13 | RCSID("$OpenBSD: auth1.c,v 1.60 2005/05/20 12:57:01 djm Exp $"); |
14 | 14 | ||
15 | #include "xmalloc.h" | 15 | #include "xmalloc.h" |
16 | #include "rsa.h" | 16 | #include "rsa.h" |
@@ -31,28 +31,181 @@ RCSID("$OpenBSD: auth1.c,v 1.59 2004/07/28 09:40:29 markus Exp $"); | |||
31 | extern ServerOptions options; | 31 | extern ServerOptions options; |
32 | extern Buffer loginmsg; | 32 | extern Buffer loginmsg; |
33 | 33 | ||
34 | /* | 34 | static int auth1_process_password(Authctxt *, char *, size_t); |
35 | * convert ssh auth msg type into description | 35 | static int auth1_process_rsa(Authctxt *, char *, size_t); |
36 | */ | 36 | static int auth1_process_rhosts_rsa(Authctxt *, char *, size_t); |
37 | static int auth1_process_tis_challenge(Authctxt *, char *, size_t); | ||
38 | static int auth1_process_tis_response(Authctxt *, char *, size_t); | ||
39 | |||
40 | static char *client_user = NULL; /* Used to fill in remote user for PAM */ | ||
41 | |||
42 | struct AuthMethod1 { | ||
43 | int type; | ||
44 | char *name; | ||
45 | int *enabled; | ||
46 | int (*method)(Authctxt *, char *, size_t); | ||
47 | }; | ||
48 | |||
49 | const struct AuthMethod1 auth1_methods[] = { | ||
50 | { | ||
51 | SSH_CMSG_AUTH_PASSWORD, "password", | ||
52 | &options.password_authentication, auth1_process_password | ||
53 | }, | ||
54 | { | ||
55 | SSH_CMSG_AUTH_RSA, "rsa", | ||
56 | &options.rsa_authentication, auth1_process_rsa | ||
57 | }, | ||
58 | { | ||
59 | SSH_CMSG_AUTH_RHOSTS_RSA, "rhosts-rsa", | ||
60 | &options.rhosts_rsa_authentication, auth1_process_rhosts_rsa | ||
61 | }, | ||
62 | { | ||
63 | SSH_CMSG_AUTH_TIS, "challenge-response", | ||
64 | &options.challenge_response_authentication, | ||
65 | auth1_process_tis_challenge | ||
66 | }, | ||
67 | { | ||
68 | SSH_CMSG_AUTH_TIS_RESPONSE, "challenge-response", | ||
69 | &options.challenge_response_authentication, | ||
70 | auth1_process_tis_response | ||
71 | }, | ||
72 | { -1, NULL, NULL, NULL} | ||
73 | }; | ||
74 | |||
75 | static const struct AuthMethod1 | ||
76 | *lookup_authmethod1(int type) | ||
77 | { | ||
78 | int i; | ||
79 | |||
80 | for(i = 0; auth1_methods[i].name != NULL; i++) | ||
81 | if (auth1_methods[i].type == type) | ||
82 | return (&(auth1_methods[i])); | ||
83 | |||
84 | return (NULL); | ||
85 | } | ||
86 | |||
37 | static char * | 87 | static char * |
38 | get_authname(int type) | 88 | get_authname(int type) |
39 | { | 89 | { |
40 | static char buf[1024]; | 90 | const struct AuthMethod1 *a; |
41 | switch (type) { | 91 | static char buf[64]; |
42 | case SSH_CMSG_AUTH_PASSWORD: | 92 | |
43 | return "password"; | 93 | if ((a = lookup_authmethod1(type)) != NULL) |
44 | case SSH_CMSG_AUTH_RSA: | 94 | return (a->name); |
45 | return "rsa"; | 95 | snprintf(buf, sizeof(buf), "bad-auth-msg-%d", type); |
46 | case SSH_CMSG_AUTH_RHOSTS_RSA: | 96 | return (buf); |
47 | return "rhosts-rsa"; | 97 | } |
48 | case SSH_CMSG_AUTH_RHOSTS: | 98 | |
49 | return "rhosts"; | 99 | static int |
50 | case SSH_CMSG_AUTH_TIS: | 100 | auth1_process_password(Authctxt *authctxt, char *info, size_t infolen) |
51 | case SSH_CMSG_AUTH_TIS_RESPONSE: | 101 | { |
52 | return "challenge-response"; | 102 | int authenticated = 0; |
103 | char *password; | ||
104 | u_int dlen; | ||
105 | |||
106 | /* | ||
107 | * Read user password. It is in plain text, but was | ||
108 | * transmitted over the encrypted channel so it is | ||
109 | * not visible to an outside observer. | ||
110 | */ | ||
111 | password = packet_get_string(&dlen); | ||
112 | packet_check_eom(); | ||
113 | |||
114 | /* Try authentication with the password. */ | ||
115 | authenticated = PRIVSEP(auth_password(authctxt, password)); | ||
116 | |||
117 | memset(password, 0, dlen); | ||
118 | xfree(password); | ||
119 | |||
120 | return (authenticated); | ||
121 | } | ||
122 | |||
123 | static int | ||
124 | auth1_process_rsa(Authctxt *authctxt, char *info, size_t infolen) | ||
125 | { | ||
126 | int authenticated = 0; | ||
127 | BIGNUM *n; | ||
128 | |||
129 | /* RSA authentication requested. */ | ||
130 | if ((n = BN_new()) == NULL) | ||
131 | fatal("do_authloop: BN_new failed"); | ||
132 | packet_get_bignum(n); | ||
133 | packet_check_eom(); | ||
134 | authenticated = auth_rsa(authctxt, n); | ||
135 | BN_clear_free(n); | ||
136 | |||
137 | return (authenticated); | ||
138 | } | ||
139 | |||
140 | static int | ||
141 | auth1_process_rhosts_rsa(Authctxt *authctxt, char *info, size_t infolen) | ||
142 | { | ||
143 | int authenticated = 0; | ||
144 | u_int bits; | ||
145 | Key *client_host_key; | ||
146 | u_int ulen; | ||
147 | |||
148 | /* | ||
149 | * Get client user name. Note that we just have to | ||
150 | * trust the client; root on the client machine can | ||
151 | * claim to be any user. | ||
152 | */ | ||
153 | client_user = packet_get_string(&ulen); | ||
154 | |||
155 | /* Get the client host key. */ | ||
156 | client_host_key = key_new(KEY_RSA1); | ||
157 | bits = packet_get_int(); | ||
158 | packet_get_bignum(client_host_key->rsa->e); | ||
159 | packet_get_bignum(client_host_key->rsa->n); | ||
160 | |||
161 | if (bits != BN_num_bits(client_host_key->rsa->n)) { | ||
162 | verbose("Warning: keysize mismatch for client_host_key: " | ||
163 | "actual %d, announced %d", | ||
164 | BN_num_bits(client_host_key->rsa->n), bits); | ||
53 | } | 165 | } |
54 | snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); | 166 | packet_check_eom(); |
55 | return buf; | 167 | |
168 | authenticated = auth_rhosts_rsa(authctxt, client_user, | ||
169 | client_host_key); | ||
170 | key_free(client_host_key); | ||
171 | |||
172 | snprintf(info, infolen, " ruser %.100s", client_user); | ||
173 | |||
174 | return (authenticated); | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | auth1_process_tis_challenge(Authctxt *authctxt, char *info, size_t infolen) | ||
179 | { | ||
180 | char *challenge; | ||
181 | |||
182 | if ((challenge = get_challenge(authctxt)) == NULL) | ||
183 | return (0); | ||
184 | |||
185 | debug("sending challenge '%s'", challenge); | ||
186 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
187 | packet_put_cstring(challenge); | ||
188 | xfree(challenge); | ||
189 | packet_send(); | ||
190 | packet_write_wait(); | ||
191 | |||
192 | return (-1); | ||
193 | } | ||
194 | |||
195 | static int | ||
196 | auth1_process_tis_response(Authctxt *authctxt, char *info, size_t infolen) | ||
197 | { | ||
198 | int authenticated = 0; | ||
199 | char *response; | ||
200 | u_int dlen; | ||
201 | |||
202 | response = packet_get_string(&dlen); | ||
203 | packet_check_eom(); | ||
204 | authenticated = verify_response(authctxt, response); | ||
205 | memset(response, 'r', dlen); | ||
206 | xfree(response); | ||
207 | |||
208 | return (authenticated); | ||
56 | } | 209 | } |
57 | 210 | ||
58 | /* | 211 | /* |
@@ -63,14 +216,9 @@ static void | |||
63 | do_authloop(Authctxt *authctxt) | 216 | do_authloop(Authctxt *authctxt) |
64 | { | 217 | { |
65 | int authenticated = 0; | 218 | int authenticated = 0; |
66 | u_int bits; | ||
67 | Key *client_host_key; | ||
68 | BIGNUM *n; | ||
69 | char *client_user, *password; | ||
70 | char info[1024]; | 219 | char info[1024]; |
71 | u_int dlen; | 220 | int prev = 0, type = 0; |
72 | u_int ulen; | 221 | const struct AuthMethod1 *meth; |
73 | int prev, type = 0; | ||
74 | 222 | ||
75 | debug("Attempting authentication for %s%.100s.", | 223 | debug("Attempting authentication for %s%.100s.", |
76 | authctxt->valid ? "" : "invalid user ", authctxt->user); | 224 | authctxt->valid ? "" : "invalid user ", authctxt->user); |
@@ -95,8 +243,6 @@ do_authloop(Authctxt *authctxt) | |||
95 | packet_send(); | 243 | packet_send(); |
96 | packet_write_wait(); | 244 | packet_write_wait(); |
97 | 245 | ||
98 | client_user = NULL; | ||
99 | |||
100 | for (;;) { | 246 | for (;;) { |
101 | /* default to fail */ | 247 | /* default to fail */ |
102 | authenticated = 0; | 248 | authenticated = 0; |
@@ -118,107 +264,21 @@ do_authloop(Authctxt *authctxt) | |||
118 | type != SSH_CMSG_AUTH_TIS_RESPONSE) | 264 | type != SSH_CMSG_AUTH_TIS_RESPONSE) |
119 | abandon_challenge_response(authctxt); | 265 | abandon_challenge_response(authctxt); |
120 | 266 | ||
121 | /* Process the packet. */ | 267 | if ((meth = lookup_authmethod1(type)) == NULL) { |
122 | switch (type) { | 268 | logit("Unknown message during authentication: " |
123 | case SSH_CMSG_AUTH_RHOSTS_RSA: | 269 | "type %d", type); |
124 | if (!options.rhosts_rsa_authentication) { | 270 | goto skip; |
125 | verbose("Rhosts with RSA authentication disabled."); | 271 | } |
126 | break; | 272 | |
127 | } | 273 | if (!*(meth->enabled)) { |
128 | /* | 274 | verbose("%s authentication disabled.", meth->name); |
129 | * Get client user name. Note that we just have to | 275 | goto skip; |
130 | * trust the client; root on the client machine can | ||
131 | * claim to be any user. | ||
132 | */ | ||
133 | client_user = packet_get_string(&ulen); | ||
134 | |||
135 | /* Get the client host key. */ | ||
136 | client_host_key = key_new(KEY_RSA1); | ||
137 | bits = packet_get_int(); | ||
138 | packet_get_bignum(client_host_key->rsa->e); | ||
139 | packet_get_bignum(client_host_key->rsa->n); | ||
140 | |||
141 | if (bits != BN_num_bits(client_host_key->rsa->n)) | ||
142 | verbose("Warning: keysize mismatch for client_host_key: " | ||
143 | "actual %d, announced %d", | ||
144 | BN_num_bits(client_host_key->rsa->n), bits); | ||
145 | packet_check_eom(); | ||
146 | |||
147 | authenticated = auth_rhosts_rsa(authctxt, client_user, | ||
148 | client_host_key); | ||
149 | key_free(client_host_key); | ||
150 | |||
151 | snprintf(info, sizeof info, " ruser %.100s", client_user); | ||
152 | break; | ||
153 | |||
154 | case SSH_CMSG_AUTH_RSA: | ||
155 | if (!options.rsa_authentication) { | ||
156 | verbose("RSA authentication disabled."); | ||
157 | break; | ||
158 | } | ||
159 | /* RSA authentication requested. */ | ||
160 | if ((n = BN_new()) == NULL) | ||
161 | fatal("do_authloop: BN_new failed"); | ||
162 | packet_get_bignum(n); | ||
163 | packet_check_eom(); | ||
164 | authenticated = auth_rsa(authctxt, n); | ||
165 | BN_clear_free(n); | ||
166 | break; | ||
167 | |||
168 | case SSH_CMSG_AUTH_PASSWORD: | ||
169 | if (!options.password_authentication) { | ||
170 | verbose("Password authentication disabled."); | ||
171 | break; | ||
172 | } | ||
173 | /* | ||
174 | * Read user password. It is in plain text, but was | ||
175 | * transmitted over the encrypted channel so it is | ||
176 | * not visible to an outside observer. | ||
177 | */ | ||
178 | password = packet_get_string(&dlen); | ||
179 | packet_check_eom(); | ||
180 | |||
181 | /* Try authentication with the password. */ | ||
182 | authenticated = PRIVSEP(auth_password(authctxt, password)); | ||
183 | |||
184 | memset(password, 0, strlen(password)); | ||
185 | xfree(password); | ||
186 | break; | ||
187 | |||
188 | case SSH_CMSG_AUTH_TIS: | ||
189 | debug("rcvd SSH_CMSG_AUTH_TIS"); | ||
190 | if (options.challenge_response_authentication == 1) { | ||
191 | char *challenge = get_challenge(authctxt); | ||
192 | if (challenge != NULL) { | ||
193 | debug("sending challenge '%s'", challenge); | ||
194 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
195 | packet_put_cstring(challenge); | ||
196 | xfree(challenge); | ||
197 | packet_send(); | ||
198 | packet_write_wait(); | ||
199 | continue; | ||
200 | } | ||
201 | } | ||
202 | break; | ||
203 | case SSH_CMSG_AUTH_TIS_RESPONSE: | ||
204 | debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); | ||
205 | if (options.challenge_response_authentication == 1) { | ||
206 | char *response = packet_get_string(&dlen); | ||
207 | packet_check_eom(); | ||
208 | authenticated = verify_response(authctxt, response); | ||
209 | memset(response, 'r', dlen); | ||
210 | xfree(response); | ||
211 | } | ||
212 | break; | ||
213 | |||
214 | default: | ||
215 | /* | ||
216 | * Any unknown messages will be ignored (and failure | ||
217 | * returned) during authentication. | ||
218 | */ | ||
219 | logit("Unknown message during authentication: type %d", type); | ||
220 | break; | ||
221 | } | 276 | } |
277 | |||
278 | authenticated = meth->method(authctxt, info, sizeof(info)); | ||
279 | if (authenticated == -1) | ||
280 | continue; /* "postponed" */ | ||
281 | |||
222 | #ifdef BSD_AUTH | 282 | #ifdef BSD_AUTH |
223 | if (authctxt->as) { | 283 | if (authctxt->as) { |
224 | auth_close(authctxt->as); | 284 | auth_close(authctxt->as); |
@@ -247,8 +307,8 @@ do_authloop(Authctxt *authctxt) | |||
247 | #else | 307 | #else |
248 | /* Special handling for root */ | 308 | /* Special handling for root */ |
249 | if (authenticated && authctxt->pw->pw_uid == 0 && | 309 | if (authenticated && authctxt->pw->pw_uid == 0 && |
250 | !auth_root_allowed(get_authname(type))) { | 310 | !auth_root_allowed(meth->name)) { |
251 | authenticated = 0; | 311 | authenticated = 0; |
252 | # ifdef SSH_AUDIT_EVENTS | 312 | # ifdef SSH_AUDIT_EVENTS |
253 | PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); | 313 | PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); |
254 | # endif | 314 | # endif |
@@ -276,6 +336,7 @@ do_authloop(Authctxt *authctxt) | |||
276 | } | 336 | } |
277 | #endif | 337 | #endif |
278 | 338 | ||
339 | skip: | ||
279 | /* Log before sending the reply */ | 340 | /* Log before sending the reply */ |
280 | auth_log(authctxt, authenticated, get_authname(type), info); | 341 | auth_log(authctxt, authenticated, get_authname(type), info); |
281 | 342 | ||
@@ -341,7 +402,7 @@ do_authentication(Authctxt *authctxt) | |||
341 | 402 | ||
342 | /* | 403 | /* |
343 | * If we are not running as root, the user must have the same uid as | 404 | * If we are not running as root, the user must have the same uid as |
344 | * the server. (Unless you are running Windows) | 405 | * the server. |
345 | */ | 406 | */ |
346 | #ifndef HAVE_CYGWIN | 407 | #ifndef HAVE_CYGWIN |
347 | if (!use_privsep && getuid() != 0 && authctxt->pw && | 408 | if (!use_privsep && getuid() != 0 && authctxt->pw && |