diff options
Diffstat (limited to 'auth1.c')
-rw-r--r-- | auth1.c | 328 |
1 files changed, 195 insertions, 133 deletions
@@ -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.62 2005/07/16 01:35:24 djm Exp $"); |
14 | 14 | ||
15 | #include "xmalloc.h" | 15 | #include "xmalloc.h" |
16 | #include "rsa.h" | 16 | #include "rsa.h" |
@@ -31,28 +31,182 @@ 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 keybits, 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 | keybits = BN_num_bits(client_host_key->rsa->n); | ||
162 | if (keybits < 0 || bits != (u_int)keybits) { | ||
163 | verbose("Warning: keysize mismatch for client_host_key: " | ||
164 | "actual %d, announced %d", | ||
165 | BN_num_bits(client_host_key->rsa->n), bits); | ||
53 | } | 166 | } |
54 | snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); | 167 | packet_check_eom(); |
55 | return buf; | 168 | |
169 | authenticated = auth_rhosts_rsa(authctxt, client_user, | ||
170 | client_host_key); | ||
171 | key_free(client_host_key); | ||
172 | |||
173 | snprintf(info, infolen, " ruser %.100s", client_user); | ||
174 | |||
175 | return (authenticated); | ||
176 | } | ||
177 | |||
178 | static int | ||
179 | auth1_process_tis_challenge(Authctxt *authctxt, char *info, size_t infolen) | ||
180 | { | ||
181 | char *challenge; | ||
182 | |||
183 | if ((challenge = get_challenge(authctxt)) == NULL) | ||
184 | return (0); | ||
185 | |||
186 | debug("sending challenge '%s'", challenge); | ||
187 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
188 | packet_put_cstring(challenge); | ||
189 | xfree(challenge); | ||
190 | packet_send(); | ||
191 | packet_write_wait(); | ||
192 | |||
193 | return (-1); | ||
194 | } | ||
195 | |||
196 | static int | ||
197 | auth1_process_tis_response(Authctxt *authctxt, char *info, size_t infolen) | ||
198 | { | ||
199 | int authenticated = 0; | ||
200 | char *response; | ||
201 | u_int dlen; | ||
202 | |||
203 | response = packet_get_string(&dlen); | ||
204 | packet_check_eom(); | ||
205 | authenticated = verify_response(authctxt, response); | ||
206 | memset(response, 'r', dlen); | ||
207 | xfree(response); | ||
208 | |||
209 | return (authenticated); | ||
56 | } | 210 | } |
57 | 211 | ||
58 | /* | 212 | /* |
@@ -63,14 +217,9 @@ static void | |||
63 | do_authloop(Authctxt *authctxt) | 217 | do_authloop(Authctxt *authctxt) |
64 | { | 218 | { |
65 | int authenticated = 0; | 219 | int authenticated = 0; |
66 | u_int bits; | ||
67 | Key *client_host_key; | ||
68 | BIGNUM *n; | ||
69 | char *client_user, *password; | ||
70 | char info[1024]; | 220 | char info[1024]; |
71 | u_int dlen; | 221 | int prev = 0, type = 0; |
72 | u_int ulen; | 222 | const struct AuthMethod1 *meth; |
73 | int prev, type = 0; | ||
74 | 223 | ||
75 | debug("Attempting authentication for %s%.100s.", | 224 | debug("Attempting authentication for %s%.100s.", |
76 | authctxt->valid ? "" : "invalid user ", authctxt->user); | 225 | authctxt->valid ? "" : "invalid user ", authctxt->user); |
@@ -95,8 +244,6 @@ do_authloop(Authctxt *authctxt) | |||
95 | packet_send(); | 244 | packet_send(); |
96 | packet_write_wait(); | 245 | packet_write_wait(); |
97 | 246 | ||
98 | client_user = NULL; | ||
99 | |||
100 | for (;;) { | 247 | for (;;) { |
101 | /* default to fail */ | 248 | /* default to fail */ |
102 | authenticated = 0; | 249 | authenticated = 0; |
@@ -118,107 +265,21 @@ do_authloop(Authctxt *authctxt) | |||
118 | type != SSH_CMSG_AUTH_TIS_RESPONSE) | 265 | type != SSH_CMSG_AUTH_TIS_RESPONSE) |
119 | abandon_challenge_response(authctxt); | 266 | abandon_challenge_response(authctxt); |
120 | 267 | ||
121 | /* Process the packet. */ | 268 | if ((meth = lookup_authmethod1(type)) == NULL) { |
122 | switch (type) { | 269 | logit("Unknown message during authentication: " |
123 | case SSH_CMSG_AUTH_RHOSTS_RSA: | 270 | "type %d", type); |
124 | if (!options.rhosts_rsa_authentication) { | 271 | goto skip; |
125 | verbose("Rhosts with RSA authentication disabled."); | 272 | } |
126 | break; | 273 | |
127 | } | 274 | if (!*(meth->enabled)) { |
128 | /* | 275 | verbose("%s authentication disabled.", meth->name); |
129 | * Get client user name. Note that we just have to | 276 | 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 | } | 277 | } |
278 | |||
279 | authenticated = meth->method(authctxt, info, sizeof(info)); | ||
280 | if (authenticated == -1) | ||
281 | continue; /* "postponed" */ | ||
282 | |||
222 | #ifdef BSD_AUTH | 283 | #ifdef BSD_AUTH |
223 | if (authctxt->as) { | 284 | if (authctxt->as) { |
224 | auth_close(authctxt->as); | 285 | auth_close(authctxt->as); |
@@ -238,7 +299,7 @@ do_authloop(Authctxt *authctxt) | |||
238 | 299 | ||
239 | #ifdef HAVE_CYGWIN | 300 | #ifdef HAVE_CYGWIN |
240 | if (authenticated && | 301 | if (authenticated && |
241 | !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD, | 302 | !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD, |
242 | authctxt->pw)) { | 303 | authctxt->pw)) { |
243 | packet_disconnect("Authentication rejected for uid %d.", | 304 | packet_disconnect("Authentication rejected for uid %d.", |
244 | authctxt->pw == NULL ? -1 : authctxt->pw->pw_uid); | 305 | authctxt->pw == NULL ? -1 : authctxt->pw->pw_uid); |
@@ -247,8 +308,8 @@ do_authloop(Authctxt *authctxt) | |||
247 | #else | 308 | #else |
248 | /* Special handling for root */ | 309 | /* Special handling for root */ |
249 | if (authenticated && authctxt->pw->pw_uid == 0 && | 310 | if (authenticated && authctxt->pw->pw_uid == 0 && |
250 | !auth_root_allowed(get_authname(type))) { | 311 | !auth_root_allowed(meth->name)) { |
251 | authenticated = 0; | 312 | authenticated = 0; |
252 | # ifdef SSH_AUDIT_EVENTS | 313 | # ifdef SSH_AUDIT_EVENTS |
253 | PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); | 314 | PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); |
254 | # endif | 315 | # endif |
@@ -262,7 +323,7 @@ do_authloop(Authctxt *authctxt) | |||
262 | size_t len; | 323 | size_t len; |
263 | 324 | ||
264 | error("Access denied for user %s by PAM account " | 325 | error("Access denied for user %s by PAM account " |
265 | "configuration", authctxt->user); | 326 | "configuration", authctxt->user); |
266 | len = buffer_len(&loginmsg); | 327 | len = buffer_len(&loginmsg); |
267 | buffer_append(&loginmsg, "\0", 1); | 328 | buffer_append(&loginmsg, "\0", 1); |
268 | msg = buffer_ptr(&loginmsg); | 329 | msg = buffer_ptr(&loginmsg); |
@@ -276,6 +337,7 @@ do_authloop(Authctxt *authctxt) | |||
276 | } | 337 | } |
277 | #endif | 338 | #endif |
278 | 339 | ||
340 | skip: | ||
279 | /* Log before sending the reply */ | 341 | /* Log before sending the reply */ |
280 | auth_log(authctxt, authenticated, get_authname(type), info); | 342 | auth_log(authctxt, authenticated, get_authname(type), info); |
281 | 343 | ||
@@ -341,7 +403,7 @@ do_authentication(Authctxt *authctxt) | |||
341 | 403 | ||
342 | /* | 404 | /* |
343 | * If we are not running as root, the user must have the same uid as | 405 | * If we are not running as root, the user must have the same uid as |
344 | * the server. (Unless you are running Windows) | 406 | * the server. |
345 | */ | 407 | */ |
346 | #ifndef HAVE_CYGWIN | 408 | #ifndef HAVE_CYGWIN |
347 | if (!use_privsep && getuid() != 0 && authctxt->pw && | 409 | if (!use_privsep && getuid() != 0 && authctxt->pw && |