Branch data Line data Source code
1 : : /* $OpenBSD: auth2.c,v 1.130 2014/01/29 06:18:35 djm Exp $ */
2 : : /*
3 : : * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 : : *
5 : : * Redistribution and use in source and binary forms, with or without
6 : : * modification, are permitted provided that the following conditions
7 : : * are met:
8 : : * 1. Redistributions of source code must retain the above copyright
9 : : * notice, this list of conditions and the following disclaimer.
10 : : * 2. Redistributions in binary form must reproduce the above copyright
11 : : * notice, this list of conditions and the following disclaimer in the
12 : : * documentation and/or other materials provided with the distribution.
13 : : *
14 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 : : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 : : */
25 : :
26 : : #include "includes.h"
27 : :
28 : : #include <sys/types.h>
29 : : #include <sys/stat.h>
30 : : #include <sys/uio.h>
31 : :
32 : : #include <fcntl.h>
33 : : #include <pwd.h>
34 : : #include <stdarg.h>
35 : : #include <string.h>
36 : : #include <unistd.h>
37 : :
38 : : #include "atomicio.h"
39 : : #include "xmalloc.h"
40 : : #include "ssh2.h"
41 : : #include "packet.h"
42 : : #include "log.h"
43 : : #include "buffer.h"
44 : : #include "servconf.h"
45 : : #include "compat.h"
46 : : #include "key.h"
47 : : #include "hostfile.h"
48 : : #include "auth.h"
49 : : #include "dispatch.h"
50 : : #include "pathnames.h"
51 : : #include "buffer.h"
52 : :
53 : : #ifdef GSSAPI
54 : : #include "ssh-gss.h"
55 : : #endif
56 : : #include "monitor_wrap.h"
57 : :
58 : : /* import */
59 : : extern ServerOptions options;
60 : : extern u_char *session_id2;
61 : : extern u_int session_id2_len;
62 : : extern Buffer loginmsg;
63 : :
64 : : /* methods */
65 : :
66 : : extern Authmethod method_none;
67 : : extern Authmethod method_pubkey;
68 : : extern Authmethod method_passwd;
69 : : extern Authmethod method_kbdint;
70 : : extern Authmethod method_hostbased;
71 : : #ifdef GSSAPI
72 : : extern Authmethod method_gssapi;
73 : : #endif
74 : :
75 : : Authmethod *authmethods[] = {
76 : : &method_none,
77 : : &method_pubkey,
78 : : #ifdef GSSAPI
79 : : &method_gssapi,
80 : : #endif
81 : : &method_passwd,
82 : : &method_kbdint,
83 : : &method_hostbased,
84 : : NULL
85 : : };
86 : :
87 : : /* protocol */
88 : :
89 : : static void input_service_request(int, u_int32_t, void *);
90 : : static void input_userauth_request(int, u_int32_t, void *);
91 : :
92 : : /* helper */
93 : : static Authmethod *authmethod_lookup(Authctxt *, const char *);
94 : : static char *authmethods_get(Authctxt *authctxt);
95 : :
96 : : #define MATCH_NONE 0 /* method or submethod mismatch */
97 : : #define MATCH_METHOD 1 /* method matches (no submethod specified) */
98 : : #define MATCH_BOTH 2 /* method and submethod match */
99 : : #define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */
100 : : static int list_starts_with(const char *, const char *, const char *);
101 : :
102 : : char *
103 : 8 : auth2_read_banner(void)
104 : : {
105 : : struct stat st;
106 : 8 : char *banner = NULL;
107 : : size_t len, n;
108 : : int fd;
109 : :
110 [ + + ]: 8 : if ((fd = open(options.banner, O_RDONLY)) == -1)
111 : : return (NULL);
112 [ - + ]: 7 : if (fstat(fd, &st) == -1) {
113 : 0 : close(fd);
114 : 0 : return (NULL);
115 : : }
116 [ + + ]: 7 : if (st.st_size <= 0 || st.st_size > 1*1024*1024) {
117 : 1 : close(fd);
118 : 1 : return (NULL);
119 : : }
120 : :
121 : 6 : len = (size_t)st.st_size; /* truncate */
122 : 6 : banner = xmalloc(len + 1);
123 : 6 : n = atomicio(read, fd, banner, len);
124 : 6 : close(fd);
125 : :
126 [ - + ]: 6 : if (n != len) {
127 : 0 : free(banner);
128 : 0 : return (NULL);
129 : : }
130 : 6 : banner[n] = '\0';
131 : :
132 : 6 : return (banner);
133 : : }
134 : :
135 : : void
136 : 6 : userauth_send_banner(const char *msg)
137 : : {
138 [ + - ]: 6 : if (datafellows & SSH_BUG_BANNER)
139 : 6 : return;
140 : :
141 : 6 : packet_start(SSH2_MSG_USERAUTH_BANNER);
142 : 6 : packet_put_cstring(msg);
143 : 6 : packet_put_cstring(""); /* language, unused */
144 : 6 : packet_send();
145 : 6 : debug("%s: sent", __func__);
146 : : }
147 : :
148 : : static void
149 : 732 : userauth_banner(void)
150 : : {
151 : 732 : char *banner = NULL;
152 : :
153 [ + + ][ + - ]: 732 : if (options.banner == NULL ||
154 [ + - ]: 8 : strcasecmp(options.banner, "none") == 0 ||
155 : 8 : (datafellows & SSH_BUG_BANNER) != 0)
156 : 732 : return;
157 : :
158 [ + - ][ + + ]: 8 : if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
159 : : goto done;
160 : 6 : userauth_send_banner(banner);
161 : :
162 : : done:
163 : 8 : free(banner);
164 : : }
165 : :
166 : : /*
167 : : * loop until authctxt->success == TRUE
168 : : */
169 : : void
170 : 732 : do_authentication2(Authctxt *authctxt)
171 : : {
172 : 732 : dispatch_init(&dispatch_protocol_error);
173 : 732 : dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
174 : 732 : dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
175 : 732 : }
176 : :
177 : : /*ARGSUSED*/
178 : : static void
179 : 732 : input_service_request(int type, u_int32_t seq, void *ctxt)
180 : : {
181 : 732 : Authctxt *authctxt = ctxt;
182 : : u_int len;
183 : 732 : int acceptit = 0;
184 : 732 : char *service = packet_get_cstring(&len);
185 [ - + ]: 732 : packet_check_eom();
186 : :
187 [ - + ]: 732 : if (authctxt == NULL)
188 : 0 : fatal("input_service_request: no authctxt");
189 : :
190 [ + - ]: 732 : if (strcmp(service, "ssh-userauth") == 0) {
191 [ + - ]: 732 : if (!authctxt->success) {
192 : 732 : acceptit = 1;
193 : : /* now we can handle user-auth requests */
194 : 732 : dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
195 : : }
196 : : }
197 : : /* XXX all other service requests are denied */
198 : :
199 [ + - ]: 732 : if (acceptit) {
200 : 732 : packet_start(SSH2_MSG_SERVICE_ACCEPT);
201 : 732 : packet_put_cstring(service);
202 : 732 : packet_send();
203 : 732 : packet_write_wait();
204 : : } else {
205 : 0 : debug("bad service request %s", service);
206 : 0 : packet_disconnect("bad service request %s", service);
207 : : }
208 : 732 : free(service);
209 : 732 : }
210 : :
211 : : /*ARGSUSED*/
212 : : static void
213 : 2342 : input_userauth_request(int type, u_int32_t seq, void *ctxt)
214 : : {
215 : 2342 : Authctxt *authctxt = ctxt;
216 : 2342 : Authmethod *m = NULL;
217 : 2342 : char *user, *service, *method, *style = NULL;
218 : 2342 : int authenticated = 0;
219 : :
220 [ - + ]: 2342 : if (authctxt == NULL)
221 : 0 : fatal("input_userauth_request: no authctxt");
222 : :
223 : 2342 : user = packet_get_cstring(NULL);
224 : 2342 : service = packet_get_cstring(NULL);
225 : 2342 : method = packet_get_cstring(NULL);
226 : 2342 : debug("userauth-request for user %s service %s method %s", user, service, method);
227 : 2342 : debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
228 : :
229 [ + + ]: 2342 : if ((style = strchr(user, ':')) != NULL)
230 : 3 : *style++ = 0;
231 : :
232 [ + + ]: 2342 : if (authctxt->attempt++ == 0) {
233 : : /* setup auth context */
234 [ + + ]: 732 : authctxt->pw = PRIVSEP(getpwnamallow(user));
235 : 732 : authctxt->user = xstrdup(user);
236 [ + - ][ + - ]: 732 : if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
237 : 732 : authctxt->valid = 1;
238 : 732 : debug2("input_userauth_request: setting up authctxt for %s", user);
239 : : } else {
240 : 0 : logit("input_userauth_request: invalid user %s", user);
241 : 0 : authctxt->pw = fakepw();
242 : : #ifdef SSH_AUDIT_EVENTS
243 : : PRIVSEP(audit_event(SSH_INVALID_USER));
244 : : #endif
245 : : }
246 : : #ifdef USE_PAM
247 : : if (options.use_pam)
248 : : PRIVSEP(start_pam(authctxt));
249 : : #endif
250 [ + + ][ + - ]: 732 : setproctitle("%s%s", authctxt->valid ? user : "unknown",
251 : 732 : use_privsep ? " [net]" : "");
252 : 732 : authctxt->service = xstrdup(service);
253 [ + + ]: 732 : authctxt->style = style ? xstrdup(style) : NULL;
254 [ + + ]: 732 : if (use_privsep)
255 : 658 : mm_inform_authserv(service, style);
256 : 732 : userauth_banner();
257 [ - + ]: 732 : if (auth2_setup_methods_lists(authctxt) != 0)
258 : 0 : packet_disconnect("no authentication methods enabled");
259 [ + - ][ - + ]: 1610 : } else if (strcmp(user, authctxt->user) != 0 ||
260 : 1610 : strcmp(service, authctxt->service) != 0) {
261 : 0 : packet_disconnect("Change of username or service not allowed: "
262 : : "(%s,%s) -> (%s,%s)",
263 : : authctxt->user, authctxt->service, user, service);
264 : : }
265 : : /* reset state */
266 : 2342 : auth2_challenge_stop(authctxt);
267 : :
268 : : #ifdef GSSAPI
269 : : /* XXX move to auth2_gssapi_stop() */
270 : : dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
271 : : dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
272 : : #endif
273 : :
274 : 2342 : authctxt->postponed = 0;
275 : 2342 : authctxt->server_caused_failure = 0;
276 : :
277 : : /* try to authenticate user */
278 : 2342 : m = authmethod_lookup(authctxt, method);
279 [ + - ][ + - ]: 2342 : if (m != NULL && authctxt->failures < options.max_authtries) {
280 : 2342 : debug2("input_userauth_request: try method %s", method);
281 : 2342 : authenticated = m->userauth(authctxt);
282 : : }
283 : 2342 : userauth_finish(authctxt, authenticated, method, NULL);
284 : :
285 : 2342 : free(service);
286 : 2342 : free(user);
287 : 2342 : free(method);
288 : 2342 : }
289 : :
290 : : void
291 : 2342 : userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
292 : : const char *submethod)
293 : : {
294 : : char *methods;
295 : 2342 : int partial = 0;
296 : :
297 [ - + ][ # # ]: 2342 : if (!authctxt->valid && authenticated)
298 : 0 : fatal("INTERNAL ERROR: authenticated invalid user %s",
299 : : authctxt->user);
300 [ + + ][ - + ]: 2342 : if (authenticated && authctxt->postponed)
301 : 0 : fatal("INTERNAL ERROR: authenticated and postponed");
302 : :
303 : : /* Special handling for root */
304 [ + + ]: 2342 : if (authenticated && authctxt->pw->pw_uid == 0 &&
[ - + # # ]
305 : 0 : !auth_root_allowed(method)) {
306 : 0 : authenticated = 0;
307 : : #ifdef SSH_AUDIT_EVENTS
308 : : PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED));
309 : : #endif
310 : : }
311 : :
312 [ + + ][ - + ]: 2342 : if (authenticated && options.num_auth_methods != 0) {
313 [ # # ]: 0 : if (!auth2_update_methods_lists(authctxt, method, submethod)) {
314 : 0 : authenticated = 0;
315 : 0 : partial = 1;
316 : : }
317 : : }
318 : :
319 : : /* Log before sending the reply */
320 : 2342 : auth_log(authctxt, authenticated, partial, method, submethod);
321 : :
322 [ + + ]: 2342 : if (authctxt->postponed)
323 : 2342 : return;
324 : :
325 : : #ifdef USE_PAM
326 : : if (options.use_pam && authenticated) {
327 : : if (!PRIVSEP(do_pam_account())) {
328 : : /* if PAM returned a message, send it to the user */
329 : : if (buffer_len(&loginmsg) > 0) {
330 : : buffer_append(&loginmsg, "\0", 1);
331 : : userauth_send_banner(buffer_ptr(&loginmsg));
332 : : packet_write_wait();
333 : : }
334 : : fatal("Access denied for user %s by PAM account "
335 : : "configuration", authctxt->user);
336 : : }
337 : : }
338 : : #endif
339 : :
340 : : #ifdef _UNICOS
341 : : if (authenticated && cray_access_denied(authctxt->user)) {
342 : : authenticated = 0;
343 : : fatal("Access denied for user %s.", authctxt->user);
344 : : }
345 : : #endif /* _UNICOS */
346 : :
347 [ + + ]: 1610 : if (authenticated == 1) {
348 : : /* turn off userauth */
349 : 732 : dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
350 : 732 : packet_start(SSH2_MSG_USERAUTH_SUCCESS);
351 : 732 : packet_send();
352 : 732 : packet_write_wait();
353 : : /* now we can break out */
354 : 732 : authctxt->success = 1;
355 : : } else {
356 : :
357 : : /* Allow initial try of "none" auth without failure penalty */
358 [ + - ][ + + ]: 878 : if (!authctxt->server_caused_failure &&
359 [ - + ]: 732 : (authctxt->attempt > 1 || strcmp(method, "none") != 0))
360 : 146 : authctxt->failures++;
361 [ - + ]: 878 : if (authctxt->failures >= options.max_authtries) {
362 : : #ifdef SSH_AUDIT_EVENTS
363 : : PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
364 : : #endif
365 : 0 : packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
366 : : }
367 : 878 : methods = authmethods_get(authctxt);
368 : 878 : debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
369 : : partial, methods);
370 : 878 : packet_start(SSH2_MSG_USERAUTH_FAILURE);
371 : 878 : packet_put_cstring(methods);
372 : 878 : packet_put_char(partial);
373 : 878 : packet_send();
374 : 878 : packet_write_wait();
375 : 878 : free(methods);
376 : : }
377 : : }
378 : :
379 : : /*
380 : : * Checks whether method is allowed by at least one AuthenticationMethods
381 : : * methods list. Returns 1 if allowed, or no methods lists configured.
382 : : * 0 otherwise.
383 : : */
384 : : int
385 : 4976 : auth2_method_allowed(Authctxt *authctxt, const char *method,
386 : : const char *submethod)
387 : : {
388 : : u_int i;
389 : :
390 : : /*
391 : : * NB. authctxt->num_auth_methods might be zero as a result of
392 : : * auth2_setup_methods_lists(), so check the configuration.
393 : : */
394 [ - + ]: 4976 : if (options.num_auth_methods == 0)
395 : : return 1;
396 [ # # ]: 0 : for (i = 0; i < authctxt->num_auth_methods; i++) {
397 [ # # ]: 0 : if (list_starts_with(authctxt->auth_methods[i], method,
398 : : submethod) != MATCH_NONE)
399 : : return 1;
400 : : }
401 : : return 0;
402 : : }
403 : :
404 : : static char *
405 : 878 : authmethods_get(Authctxt *authctxt)
406 : : {
407 : : Buffer b;
408 : : char *list;
409 : : u_int i;
410 : :
411 : 878 : buffer_init(&b);
412 [ + + ]: 5268 : for (i = 0; authmethods[i] != NULL; i++) {
413 [ + + ]: 4390 : if (strcmp(authmethods[i]->name, "none") == 0)
414 : 878 : continue;
415 [ + - ][ + + ]: 3512 : if (authmethods[i]->enabled == NULL ||
416 : 3512 : *(authmethods[i]->enabled) == 0)
417 : 878 : continue;
418 [ - + ]: 2634 : if (!auth2_method_allowed(authctxt, authmethods[i]->name,
419 : : NULL))
420 : 0 : continue;
421 [ + + ]: 2634 : if (buffer_len(&b) > 0)
422 : 1756 : buffer_append(&b, ",", 1);
423 : 2634 : buffer_append(&b, authmethods[i]->name,
424 : 2634 : strlen(authmethods[i]->name));
425 : : }
426 : 878 : buffer_append(&b, "\0", 1);
427 : 878 : list = xstrdup(buffer_ptr(&b));
428 : 878 : buffer_free(&b);
429 : 878 : return list;
430 : : }
431 : :
432 : : static Authmethod *
433 : 2342 : authmethod_lookup(Authctxt *authctxt, const char *name)
434 : : {
435 : : int i;
436 : :
437 [ + - ]: 2342 : if (name != NULL)
438 [ + - ]: 3952 : for (i = 0; authmethods[i] != NULL; i++)
439 [ + - ][ + + ]: 3952 : if (authmethods[i]->enabled != NULL &&
440 [ + - ]: 2342 : *(authmethods[i]->enabled) != 0 &&
441 [ - + ]: 2342 : strcmp(name, authmethods[i]->name) == 0 &&
442 : 2342 : auth2_method_allowed(authctxt,
443 : : authmethods[i]->name, NULL))
444 : : return authmethods[i];
445 [ # # ]: 0 : debug2("Unrecognized authentication method name: %s",
446 : : name ? name : "NULL");
447 : 0 : return NULL;
448 : : }
449 : :
450 : : /*
451 : : * Check a comma-separated list of methods for validity. Is need_enable is
452 : : * non-zero, then also require that the methods are enabled.
453 : : * Returns 0 on success or -1 if the methods list is invalid.
454 : : */
455 : : int
456 : 0 : auth2_methods_valid(const char *_methods, int need_enable)
457 : : {
458 : : char *methods, *omethods, *method, *p;
459 : : u_int i, found;
460 : 0 : int ret = -1;
461 : :
462 [ # # ]: 0 : if (*_methods == '\0') {
463 : 0 : error("empty authentication method list");
464 : 0 : return -1;
465 : : }
466 : 0 : omethods = methods = xstrdup(_methods);
467 [ # # ]: 0 : while ((method = strsep(&methods, ",")) != NULL) {
468 [ # # ]: 0 : for (found = i = 0; !found && authmethods[i] != NULL; i++) {
469 [ # # ]: 0 : if ((p = strchr(method, ':')) != NULL)
470 : 0 : *p = '\0';
471 [ # # ]: 0 : if (strcmp(method, authmethods[i]->name) != 0)
472 : 0 : continue;
473 [ # # ]: 0 : if (need_enable) {
474 [ # # ][ # # ]: 0 : if (authmethods[i]->enabled == NULL ||
475 : 0 : *(authmethods[i]->enabled) == 0) {
476 : 0 : error("Disabled method \"%s\" in "
477 : : "AuthenticationMethods list \"%s\"",
478 : : method, _methods);
479 : 0 : goto out;
480 : : }
481 : : }
482 : : found = 1;
483 : : break;
484 : : }
485 [ # # ]: 0 : if (!found) {
486 : 0 : error("Unknown authentication method \"%s\" in list",
487 : : method);
488 : 0 : goto out;
489 : : }
490 : : }
491 : : ret = 0;
492 : : out:
493 : 0 : free(omethods);
494 : 0 : return ret;
495 : : }
496 : :
497 : : /*
498 : : * Prune the AuthenticationMethods supplied in the configuration, removing
499 : : * any methods lists that include disabled methods. Note that this might
500 : : * leave authctxt->num_auth_methods == 0, even when multiple required auth
501 : : * has been requested. For this reason, all tests for whether multiple is
502 : : * enabled should consult options.num_auth_methods directly.
503 : : */
504 : : int
505 : 1390 : auth2_setup_methods_lists(Authctxt *authctxt)
506 : : {
507 : : u_int i;
508 : :
509 [ - + ]: 1390 : if (options.num_auth_methods == 0)
510 : : return 0;
511 : 0 : debug3("%s: checking methods", __func__);
512 : 0 : authctxt->auth_methods = xcalloc(options.num_auth_methods,
513 : : sizeof(*authctxt->auth_methods));
514 : 0 : authctxt->num_auth_methods = 0;
515 [ # # ]: 0 : for (i = 0; i < options.num_auth_methods; i++) {
516 [ # # ]: 0 : if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
517 : 0 : logit("Authentication methods list \"%s\" contains "
518 : : "disabled method, skipping",
519 : : options.auth_methods[i]);
520 : 0 : continue;
521 : : }
522 : 0 : debug("authentication methods list %d: %s",
523 : : authctxt->num_auth_methods, options.auth_methods[i]);
524 : 0 : authctxt->auth_methods[authctxt->num_auth_methods++] =
525 : 0 : xstrdup(options.auth_methods[i]);
526 : : }
527 [ # # ]: 0 : if (authctxt->num_auth_methods == 0) {
528 : 0 : error("No AuthenticationMethods left after eliminating "
529 : : "disabled methods");
530 : 0 : return -1;
531 : : }
532 : : return 0;
533 : : }
534 : :
535 : : static int
536 : 0 : list_starts_with(const char *methods, const char *method,
537 : : const char *submethod)
538 : : {
539 : 0 : size_t l = strlen(method);
540 : : int match;
541 : : const char *p;
542 : :
543 [ # # ]: 0 : if (strncmp(methods, method, l) != 0)
544 : : return MATCH_NONE;
545 : 0 : p = methods + l;
546 : 0 : match = MATCH_METHOD;
547 [ # # ]: 0 : if (*p == ':') {
548 [ # # ]: 0 : if (!submethod)
549 : : return MATCH_PARTIAL;
550 : 0 : l = strlen(submethod);
551 : 0 : p += 1;
552 [ # # ]: 0 : if (strncmp(submethod, p, l))
553 : : return MATCH_NONE;
554 : 0 : p += l;
555 : 0 : match = MATCH_BOTH;
556 : : }
557 [ # # ]: 0 : if (*p != ',' && *p != '\0')
558 : : return MATCH_NONE;
559 : 0 : return match;
560 : : }
561 : :
562 : : /*
563 : : * Remove method from the start of a comma-separated list of methods.
564 : : * Returns 0 if the list of methods did not start with that method or 1
565 : : * if it did.
566 : : */
567 : : static int
568 : 0 : remove_method(char **methods, const char *method, const char *submethod)
569 : : {
570 : 0 : char *omethods = *methods, *p;
571 : 0 : size_t l = strlen(method);
572 : : int match;
573 : :
574 : 0 : match = list_starts_with(omethods, method, submethod);
575 [ # # ]: 0 : if (match != MATCH_METHOD && match != MATCH_BOTH)
576 : : return 0;
577 : 0 : p = omethods + l;
578 [ # # ]: 0 : if (submethod && match == MATCH_BOTH)
579 : 0 : p += 1 + strlen(submethod); /* include colon */
580 [ # # ]: 0 : if (*p == ',')
581 : 0 : p++;
582 : 0 : *methods = xstrdup(p);
583 : 0 : free(omethods);
584 : 0 : return 1;
585 : : }
586 : :
587 : : /*
588 : : * Called after successful authentication. Will remove the successful method
589 : : * from the start of each list in which it occurs. If it was the last method
590 : : * in any list, then authentication is deemed successful.
591 : : * Returns 1 if the method completed any authentication list or 0 otherwise.
592 : : */
593 : : int
594 : 0 : auth2_update_methods_lists(Authctxt *authctxt, const char *method,
595 : : const char *submethod)
596 : : {
597 : 0 : u_int i, found = 0;
598 : :
599 : 0 : debug3("%s: updating methods list after \"%s\"", __func__, method);
600 [ # # ]: 0 : for (i = 0; i < authctxt->num_auth_methods; i++) {
601 [ # # ]: 0 : if (!remove_method(&(authctxt->auth_methods[i]), method,
602 : : submethod))
603 : 0 : continue;
604 : 0 : found = 1;
605 [ # # ]: 0 : if (*authctxt->auth_methods[i] == '\0') {
606 : 0 : debug2("authentication methods list %d complete", i);
607 : 0 : return 1;
608 : : }
609 : 0 : debug3("authentication methods list %d remaining: \"%s\"",
610 : : i, authctxt->auth_methods[i]);
611 : : }
612 : : /* This should not happen, but would be bad if it did */
613 [ # # ]: 0 : if (!found)
614 : 0 : fatal("%s: method not in AuthenticationMethods", __func__);
615 : : return 0;
616 : : }
617 : :
618 : :
|