1 | /* |
---|
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
---|
3 | * |
---|
4 | * Redistribution and use in source and binary forms, with or without |
---|
5 | * modification, are permitted provided that the following conditions |
---|
6 | * are met: |
---|
7 | * 1. Redistributions of source code must retain the above copyright |
---|
8 | * notice, this list of conditions and the following disclaimer. |
---|
9 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
10 | * notice, this list of conditions and the following disclaimer in the |
---|
11 | * documentation and/or other materials provided with the distribution. |
---|
12 | * |
---|
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
---|
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
---|
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
---|
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
---|
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
---|
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
---|
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
---|
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
---|
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
---|
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
23 | */ |
---|
24 | |
---|
25 | #include "includes.h" |
---|
26 | RCSID("$OpenBSD: auth2.c,v 1.95 2002/08/22 21:33:58 markus Exp $"); |
---|
27 | |
---|
28 | #include "ssh2.h" |
---|
29 | #include "xmalloc.h" |
---|
30 | #include "packet.h" |
---|
31 | #include "log.h" |
---|
32 | #include "servconf.h" |
---|
33 | #include "compat.h" |
---|
34 | #include "auth.h" |
---|
35 | #include "dispatch.h" |
---|
36 | #include "pathnames.h" |
---|
37 | #include "monitor_wrap.h" |
---|
38 | |
---|
39 | #include <al.h> |
---|
40 | extern char *session_username; |
---|
41 | extern int is_local_acct; |
---|
42 | |
---|
43 | #ifdef GSSAPI |
---|
44 | #include "ssh-gss.h" |
---|
45 | #endif |
---|
46 | |
---|
47 | /* import */ |
---|
48 | extern ServerOptions options; |
---|
49 | extern u_char *session_id2; |
---|
50 | extern int session_id2_len; |
---|
51 | extern int debug_flag; |
---|
52 | |
---|
53 | #ifdef WITH_AIXAUTHENTICATE |
---|
54 | extern char *aixloginmsg; |
---|
55 | #endif |
---|
56 | |
---|
57 | Authctxt *x_authctxt = NULL; |
---|
58 | |
---|
59 | /* methods */ |
---|
60 | |
---|
61 | extern Authmethod method_none; |
---|
62 | extern Authmethod method_pubkey; |
---|
63 | extern Authmethod method_passwd; |
---|
64 | extern Authmethod method_kbdint; |
---|
65 | extern Authmethod method_hostbased; |
---|
66 | extern Authmethod method_external; |
---|
67 | extern Authmethod method_gssapi; |
---|
68 | |
---|
69 | Authmethod *authmethods[] = { |
---|
70 | &method_none, |
---|
71 | #ifdef GSSAPI |
---|
72 | &method_external, |
---|
73 | &method_gssapi, |
---|
74 | #endif |
---|
75 | &method_pubkey, |
---|
76 | &method_passwd, |
---|
77 | &method_kbdint, |
---|
78 | &method_hostbased, |
---|
79 | NULL |
---|
80 | }; |
---|
81 | |
---|
82 | /* protocol */ |
---|
83 | |
---|
84 | static void input_service_request(int, u_int32_t, void *); |
---|
85 | static void input_userauth_request(int, u_int32_t, void *); |
---|
86 | |
---|
87 | /* helper */ |
---|
88 | static Authmethod *authmethod_lookup(const char *); |
---|
89 | static char *authmethods_get(void); |
---|
90 | int user_key_allowed(struct passwd *, Key *); |
---|
91 | int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); |
---|
92 | |
---|
93 | /* |
---|
94 | * loop until authctxt->success == TRUE |
---|
95 | */ |
---|
96 | |
---|
97 | Authctxt * |
---|
98 | do_authentication2(void) |
---|
99 | { |
---|
100 | Authctxt *authctxt = authctxt_new(); |
---|
101 | |
---|
102 | x_authctxt = authctxt; /*XXX*/ |
---|
103 | |
---|
104 | /* challenge-response is implemented via keyboard interactive */ |
---|
105 | if (options.challenge_response_authentication) |
---|
106 | options.kbd_interactive_authentication = 1; |
---|
107 | if (options.pam_authentication_via_kbd_int) |
---|
108 | options.kbd_interactive_authentication = 1; |
---|
109 | if (use_privsep) |
---|
110 | options.pam_authentication_via_kbd_int = 0; |
---|
111 | |
---|
112 | dispatch_init(&dispatch_protocol_error); |
---|
113 | dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); |
---|
114 | dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); |
---|
115 | |
---|
116 | return (authctxt); |
---|
117 | } |
---|
118 | |
---|
119 | static void |
---|
120 | input_service_request(int type, u_int32_t seq, void *ctxt) |
---|
121 | { |
---|
122 | Authctxt *authctxt = ctxt; |
---|
123 | u_int len; |
---|
124 | int acceptit = 0; |
---|
125 | char *service = packet_get_string(&len); |
---|
126 | packet_check_eom(); |
---|
127 | |
---|
128 | if (authctxt == NULL) |
---|
129 | fatal("input_service_request: no authctxt"); |
---|
130 | |
---|
131 | if (strcmp(service, "ssh-userauth") == 0) { |
---|
132 | if (!authctxt->success) { |
---|
133 | acceptit = 1; |
---|
134 | /* now we can handle user-auth requests */ |
---|
135 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); |
---|
136 | } |
---|
137 | } |
---|
138 | /* XXX all other service requests are denied */ |
---|
139 | |
---|
140 | if (acceptit) { |
---|
141 | packet_start(SSH2_MSG_SERVICE_ACCEPT); |
---|
142 | packet_put_cstring(service); |
---|
143 | packet_send(); |
---|
144 | packet_write_wait(); |
---|
145 | } else { |
---|
146 | debug("bad service request %s", service); |
---|
147 | packet_disconnect("bad service request %s", service); |
---|
148 | } |
---|
149 | xfree(service); |
---|
150 | } |
---|
151 | |
---|
152 | static void |
---|
153 | input_userauth_request(int type, u_int32_t seq, void *ctxt) |
---|
154 | { |
---|
155 | Authctxt *authctxt = ctxt; |
---|
156 | Authmethod *m = NULL; |
---|
157 | char *user, *service, *method, *style = NULL; |
---|
158 | int authenticated = 0, status; |
---|
159 | char *filetext, *errmem; |
---|
160 | const char *err; |
---|
161 | |
---|
162 | if (authctxt == NULL) |
---|
163 | fatal("input_userauth_request: no authctxt"); |
---|
164 | |
---|
165 | user = packet_get_string(NULL); |
---|
166 | service = packet_get_string(NULL); |
---|
167 | method = packet_get_string(NULL); |
---|
168 | debug("userauth-request for user %s service %s method %s", user, service, method); |
---|
169 | debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); |
---|
170 | |
---|
171 | if ((style = strchr(user, ':')) != NULL) |
---|
172 | *style++ = 0; |
---|
173 | |
---|
174 | if (authctxt->attempt++ == 0) { |
---|
175 | /* setup auth context */ |
---|
176 | struct passwd *pw = NULL; |
---|
177 | |
---|
178 | status = al_login_allowed(user, 1, &is_local_acct, &filetext); |
---|
179 | if (status != AL_SUCCESS) |
---|
180 | { |
---|
181 | char *buf; |
---|
182 | |
---|
183 | err = al_strerror(status, &errmem); |
---|
184 | if (filetext && *filetext) |
---|
185 | { |
---|
186 | buf = xmalloc(40 + strlen(err) + strlen(filetext)); |
---|
187 | sprintf(buf, "You are not allowed to log in here: " |
---|
188 | "%s\n%s", err, filetext); |
---|
189 | } |
---|
190 | else |
---|
191 | { |
---|
192 | buf = xmalloc(40 + strlen(err)); |
---|
193 | sprintf(buf, "You are not allowed to log in here: " |
---|
194 | "%s\n", err); |
---|
195 | } |
---|
196 | log("Login denied: attempted login as %s from %s: %s", |
---|
197 | user, get_canonical_hostname(1), err); |
---|
198 | packet_disconnect(buf); |
---|
199 | } |
---|
200 | if (!is_local_acct) |
---|
201 | { |
---|
202 | status = al_acct_create(user, getpid(), 0, 0, NULL); |
---|
203 | if (status != AL_SUCCESS && debug_flag) |
---|
204 | { |
---|
205 | err = al_strerror(status, &errmem); |
---|
206 | debug("al_acct_create failed for user %s: %s", user, |
---|
207 | err); |
---|
208 | al_free_errmem(errmem); |
---|
209 | } |
---|
210 | session_username = xstrdup(user); |
---|
211 | atexit(session_cleanup); |
---|
212 | } |
---|
213 | |
---|
214 | authctxt->pw = PRIVSEP(getpwnamallow(user)); |
---|
215 | if (authctxt->pw && strcmp(service, "ssh-connection")==0) { |
---|
216 | authctxt->valid = 1; |
---|
217 | debug2("input_userauth_request: setting up authctxt for %s", user); |
---|
218 | #ifdef USE_PAM |
---|
219 | PRIVSEP(start_pam(authctxt->pw->pw_name)); |
---|
220 | #endif |
---|
221 | } else { |
---|
222 | log("input_userauth_request: illegal user %s", user); |
---|
223 | #ifdef USE_PAM |
---|
224 | PRIVSEP(start_pam("NOUSER")); |
---|
225 | #endif |
---|
226 | } |
---|
227 | setproctitle("%s%s", authctxt->pw ? user : "unknown", |
---|
228 | use_privsep ? " [net]" : ""); |
---|
229 | authctxt->user = xstrdup(user); |
---|
230 | authctxt->service = xstrdup(service); |
---|
231 | authctxt->style = style ? xstrdup(style) : NULL; |
---|
232 | if (use_privsep) |
---|
233 | mm_inform_authserv(service, style); |
---|
234 | } else if (strcmp(user, authctxt->user) != 0 || |
---|
235 | strcmp(service, authctxt->service) != 0) { |
---|
236 | packet_disconnect("Change of username or service not allowed: " |
---|
237 | "(%s,%s) -> (%s,%s)", |
---|
238 | authctxt->user, authctxt->service, user, service); |
---|
239 | } |
---|
240 | /* reset state */ |
---|
241 | auth2_challenge_stop(authctxt); |
---|
242 | |
---|
243 | #ifdef GSSAPI |
---|
244 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); |
---|
245 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); |
---|
246 | #endif |
---|
247 | |
---|
248 | authctxt->postponed = 0; |
---|
249 | |
---|
250 | /* try to authenticate user */ |
---|
251 | m = authmethod_lookup(method); |
---|
252 | if (m != NULL) { |
---|
253 | debug2("input_userauth_request: try method %s", method); |
---|
254 | authenticated = m->userauth(authctxt); |
---|
255 | } |
---|
256 | userauth_finish(authctxt, authenticated, method); |
---|
257 | |
---|
258 | xfree(service); |
---|
259 | xfree(user); |
---|
260 | xfree(method); |
---|
261 | } |
---|
262 | |
---|
263 | void |
---|
264 | userauth_finish(Authctxt *authctxt, int authenticated, char *method) |
---|
265 | { |
---|
266 | char *methods; |
---|
267 | |
---|
268 | if (!authctxt->valid && authenticated) |
---|
269 | fatal("INTERNAL ERROR: authenticated invalid user %s", |
---|
270 | authctxt->user); |
---|
271 | |
---|
272 | /* Special handling for root */ |
---|
273 | if (!use_privsep && |
---|
274 | authenticated && authctxt->pw->pw_uid == 0 && |
---|
275 | !auth_root_allowed(method)) |
---|
276 | authenticated = 0; |
---|
277 | |
---|
278 | #ifdef USE_PAM |
---|
279 | if (!use_privsep && authenticated && authctxt->user && |
---|
280 | !do_pam_account(authctxt->user, NULL)) |
---|
281 | authenticated = 0; |
---|
282 | #endif /* USE_PAM */ |
---|
283 | |
---|
284 | #ifdef _UNICOS |
---|
285 | if (authenticated && cray_access_denied(authctxt->user)) { |
---|
286 | authenticated = 0; |
---|
287 | fatal("Access denied for user %s.",authctxt->user); |
---|
288 | } |
---|
289 | #endif /* _UNICOS */ |
---|
290 | |
---|
291 | /* Log before sending the reply */ |
---|
292 | auth_log(authctxt, authenticated, method, " ssh2"); |
---|
293 | |
---|
294 | if (authctxt->postponed) |
---|
295 | return; |
---|
296 | |
---|
297 | /* XXX todo: check if multiple auth methods are needed */ |
---|
298 | if (authenticated == 1) { |
---|
299 | /* turn off userauth */ |
---|
300 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); |
---|
301 | packet_start(SSH2_MSG_USERAUTH_SUCCESS); |
---|
302 | packet_send(); |
---|
303 | packet_write_wait(); |
---|
304 | /* now we can break out */ |
---|
305 | authctxt->success = 1; |
---|
306 | } else { |
---|
307 | if (authctxt->failures++ > AUTH_FAIL_MAX) { |
---|
308 | packet_disconnect(AUTH_FAIL_MSG, authctxt->user); |
---|
309 | } |
---|
310 | #ifdef _UNICOS |
---|
311 | if (strcmp(method, "password") == 0) |
---|
312 | cray_login_failure(authctxt->user, IA_UDBERR); |
---|
313 | #endif /* _UNICOS */ |
---|
314 | methods = authmethods_get(); |
---|
315 | packet_start(SSH2_MSG_USERAUTH_FAILURE); |
---|
316 | packet_put_cstring(methods); |
---|
317 | packet_put_char(0); /* XXX partial success, unused */ |
---|
318 | packet_send(); |
---|
319 | packet_write_wait(); |
---|
320 | xfree(methods); |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | /* get current user */ |
---|
325 | |
---|
326 | struct passwd* |
---|
327 | auth_get_user(void) |
---|
328 | { |
---|
329 | return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL; |
---|
330 | } |
---|
331 | |
---|
332 | #define DELIM "," |
---|
333 | |
---|
334 | static char * |
---|
335 | authmethods_get(void) |
---|
336 | { |
---|
337 | Buffer b; |
---|
338 | char *list; |
---|
339 | int i; |
---|
340 | |
---|
341 | buffer_init(&b); |
---|
342 | for (i = 0; authmethods[i] != NULL; i++) { |
---|
343 | if (strcmp(authmethods[i]->name, "none") == 0) |
---|
344 | continue; |
---|
345 | if (authmethods[i]->enabled != NULL && |
---|
346 | *(authmethods[i]->enabled) != 0) { |
---|
347 | if (buffer_len(&b) > 0) |
---|
348 | buffer_append(&b, ",", 1); |
---|
349 | buffer_append(&b, authmethods[i]->name, |
---|
350 | strlen(authmethods[i]->name)); |
---|
351 | } |
---|
352 | } |
---|
353 | buffer_append(&b, "\0", 1); |
---|
354 | list = xstrdup(buffer_ptr(&b)); |
---|
355 | buffer_free(&b); |
---|
356 | return list; |
---|
357 | } |
---|
358 | |
---|
359 | static Authmethod * |
---|
360 | authmethod_lookup(const char *name) |
---|
361 | { |
---|
362 | int i; |
---|
363 | |
---|
364 | if (name != NULL) |
---|
365 | for (i = 0; authmethods[i] != NULL; i++) |
---|
366 | if (authmethods[i]->enabled != NULL && |
---|
367 | *(authmethods[i]->enabled) != 0 && |
---|
368 | strcmp(name, authmethods[i]->name) == 0) |
---|
369 | return authmethods[i]; |
---|
370 | debug2("Unrecognized authentication method name: %s", |
---|
371 | name ? name : "NULL"); |
---|
372 | return NULL; |
---|
373 | } |
---|