|
Lines 92-99
static void input_service_request(int, u_int32_t, void *);
Link Here
|
| 92 |
static void input_userauth_request(int, u_int32_t, void *); |
92 |
static void input_userauth_request(int, u_int32_t, void *); |
| 93 |
|
93 |
|
| 94 |
/* helper */ |
94 |
/* helper */ |
| 95 |
static Authmethod *authmethod_lookup(const char *); |
95 |
static Authmethod *authmethod_lookup(Authctxt *, const char *); |
| 96 |
static char *authmethods_get(void); |
96 |
static char *authmethods_get(Authctxt *authctxt); |
|
|
97 |
static int method_allowed(Authctxt *, const char *); |
| 98 |
static int list_starts_with(const char *, const char *); |
| 97 |
|
99 |
|
| 98 |
char * |
100 |
char * |
| 99 |
auth2_read_banner(void) |
101 |
auth2_read_banner(void) |
|
Lines 235-240
input_userauth_request(int type, u_int32_t seq, void *ctxt)
Link Here
|
| 235 |
if (use_privsep) |
237 |
if (use_privsep) |
| 236 |
mm_inform_authserv(service, style); |
238 |
mm_inform_authserv(service, style); |
| 237 |
userauth_banner(); |
239 |
userauth_banner(); |
|
|
240 |
if (auth2_setup_methods_lists(authctxt) != 0) |
| 241 |
packet_disconnect("no authentication methods enabled"); |
| 238 |
} else if (strcmp(user, authctxt->user) != 0 || |
242 |
} else if (strcmp(user, authctxt->user) != 0 || |
| 239 |
strcmp(service, authctxt->service) != 0) { |
243 |
strcmp(service, authctxt->service) != 0) { |
| 240 |
packet_disconnect("Change of username or service not allowed: " |
244 |
packet_disconnect("Change of username or service not allowed: " |
|
Lines 257-263
input_userauth_request(int type, u_int32_t seq, void *ctxt)
Link Here
|
| 257 |
authctxt->server_caused_failure = 0; |
261 |
authctxt->server_caused_failure = 0; |
| 258 |
|
262 |
|
| 259 |
/* try to authenticate user */ |
263 |
/* try to authenticate user */ |
| 260 |
m = authmethod_lookup(method); |
264 |
m = authmethod_lookup(authctxt, method); |
| 261 |
if (m != NULL && authctxt->failures < options.max_authtries) { |
265 |
if (m != NULL && authctxt->failures < options.max_authtries) { |
| 262 |
debug2("input_userauth_request: try method %s", method); |
266 |
debug2("input_userauth_request: try method %s", method); |
| 263 |
authenticated = m->userauth(authctxt); |
267 |
authenticated = m->userauth(authctxt); |
|
Lines 273-278
void
Link Here
|
| 273 |
userauth_finish(Authctxt *authctxt, int authenticated, char *method) |
277 |
userauth_finish(Authctxt *authctxt, int authenticated, char *method) |
| 274 |
{ |
278 |
{ |
| 275 |
char *methods; |
279 |
char *methods; |
|
|
280 |
int partial = 0; |
| 276 |
|
281 |
|
| 277 |
if (!authctxt->valid && authenticated) |
282 |
if (!authctxt->valid && authenticated) |
| 278 |
fatal("INTERNAL ERROR: authenticated invalid user %s", |
283 |
fatal("INTERNAL ERROR: authenticated invalid user %s", |
|
Lines 289-295
userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Link Here
|
| 289 |
if (authctxt->postponed) |
294 |
if (authctxt->postponed) |
| 290 |
return; |
295 |
return; |
| 291 |
|
296 |
|
| 292 |
/* XXX todo: check if multiple auth methods are needed */ |
297 |
if (authenticated && options.num_auth_methods != 0) { |
|
|
298 |
if (!auth2_update_methods_lists(authctxt, method)) { |
| 299 |
authenticated = 0; |
| 300 |
partial = 1; |
| 301 |
} |
| 302 |
} |
| 303 |
|
| 293 |
if (authenticated == 1) { |
304 |
if (authenticated == 1) { |
| 294 |
/* turn off userauth */ |
305 |
/* turn off userauth */ |
| 295 |
dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); |
306 |
dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); |
|
Lines 305-338
userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Link Here
|
| 305 |
authctxt->failures++; |
316 |
authctxt->failures++; |
| 306 |
if (authctxt->failures >= options.max_authtries) |
317 |
if (authctxt->failures >= options.max_authtries) |
| 307 |
packet_disconnect(AUTH_FAIL_MSG, authctxt->user); |
318 |
packet_disconnect(AUTH_FAIL_MSG, authctxt->user); |
| 308 |
methods = authmethods_get(); |
319 |
methods = authmethods_get(authctxt); |
|
|
320 |
debug3("%s: failure partial=%d next methods=\"%s\"", __func__, |
| 321 |
partial, methods); |
| 309 |
packet_start(SSH2_MSG_USERAUTH_FAILURE); |
322 |
packet_start(SSH2_MSG_USERAUTH_FAILURE); |
| 310 |
packet_put_cstring(methods); |
323 |
packet_put_cstring(methods); |
| 311 |
packet_put_char(0); /* XXX partial success, unused */ |
324 |
packet_put_char(partial); |
| 312 |
packet_send(); |
325 |
packet_send(); |
| 313 |
packet_write_wait(); |
326 |
packet_write_wait(); |
| 314 |
xfree(methods); |
327 |
xfree(methods); |
| 315 |
} |
328 |
} |
| 316 |
} |
329 |
} |
| 317 |
|
330 |
|
|
|
331 |
/* |
| 332 |
* Checks whether method is allowed by at least one AuthenticationMethods |
| 333 |
* methods list. Returns 1 if allowed, or no methods lists configured. |
| 334 |
* 0 otherwise. |
| 335 |
*/ |
| 336 |
static int |
| 337 |
method_allowed(Authctxt *authctxt, const char *method) |
| 338 |
{ |
| 339 |
u_int i; |
| 340 |
|
| 341 |
if (options.num_auth_methods == 0) |
| 342 |
return 1; |
| 343 |
for (i = 0; i < authctxt->num_auth_methods; i++) { |
| 344 |
if (list_starts_with(authctxt->auth_methods[i], method)) |
| 345 |
return 1; |
| 346 |
} |
| 347 |
return 0; |
| 348 |
} |
| 349 |
|
| 318 |
static char * |
350 |
static char * |
| 319 |
authmethods_get(void) |
351 |
authmethods_get(Authctxt *authctxt) |
| 320 |
{ |
352 |
{ |
| 321 |
Buffer b; |
353 |
Buffer b; |
| 322 |
char *list; |
354 |
char *list; |
| 323 |
int i; |
355 |
u_int i; |
| 324 |
|
356 |
|
| 325 |
buffer_init(&b); |
357 |
buffer_init(&b); |
| 326 |
for (i = 0; authmethods[i] != NULL; i++) { |
358 |
for (i = 0; authmethods[i] != NULL; i++) { |
| 327 |
if (strcmp(authmethods[i]->name, "none") == 0) |
359 |
if (strcmp(authmethods[i]->name, "none") == 0) |
| 328 |
continue; |
360 |
continue; |
| 329 |
if (authmethods[i]->enabled != NULL && |
361 |
if (authmethods[i]->enabled == NULL || |
| 330 |
*(authmethods[i]->enabled) != 0) { |
362 |
*(authmethods[i]->enabled) == 0) |
| 331 |
if (buffer_len(&b) > 0) |
363 |
continue; |
| 332 |
buffer_append(&b, ",", 1); |
364 |
if (!method_allowed(authctxt, authmethods[i]->name)) |
| 333 |
buffer_append(&b, authmethods[i]->name, |
365 |
continue; |
| 334 |
strlen(authmethods[i]->name)); |
366 |
if (buffer_len(&b) > 0) |
| 335 |
} |
367 |
buffer_append(&b, ",", 1); |
|
|
368 |
buffer_append(&b, authmethods[i]->name, |
| 369 |
strlen(authmethods[i]->name)); |
| 336 |
} |
370 |
} |
| 337 |
buffer_append(&b, "\0", 1); |
371 |
buffer_append(&b, "\0", 1); |
| 338 |
list = xstrdup(buffer_ptr(&b)); |
372 |
list = xstrdup(buffer_ptr(&b)); |
|
Lines 341-347
authmethods_get(void)
Link Here
|
| 341 |
} |
375 |
} |
| 342 |
|
376 |
|
| 343 |
static Authmethod * |
377 |
static Authmethod * |
| 344 |
authmethod_lookup(const char *name) |
378 |
authmethod_lookup(Authctxt *authctxt, const char *name) |
| 345 |
{ |
379 |
{ |
| 346 |
int i; |
380 |
int i; |
| 347 |
|
381 |
|
|
Lines 349-358
authmethod_lookup(const char *name)
Link Here
|
| 349 |
for (i = 0; authmethods[i] != NULL; i++) |
383 |
for (i = 0; authmethods[i] != NULL; i++) |
| 350 |
if (authmethods[i]->enabled != NULL && |
384 |
if (authmethods[i]->enabled != NULL && |
| 351 |
*(authmethods[i]->enabled) != 0 && |
385 |
*(authmethods[i]->enabled) != 0 && |
| 352 |
strcmp(name, authmethods[i]->name) == 0) |
386 |
strcmp(name, authmethods[i]->name) == 0 && |
|
|
387 |
method_allowed(authctxt, authmethods[i]->name)) |
| 353 |
return authmethods[i]; |
388 |
return authmethods[i]; |
| 354 |
debug2("Unrecognized authentication method name: %s", |
389 |
debug2("Unrecognized authentication method name: %s", |
| 355 |
name ? name : "NULL"); |
390 |
name ? name : "NULL"); |
| 356 |
return NULL; |
391 |
return NULL; |
| 357 |
} |
392 |
} |
| 358 |
|
393 |
|
|
|
394 |
/* |
| 395 |
* Check a comma-separated list of methods for validity. Is need_enable is |
| 396 |
* non-zero, then also require that the methods are enabled. |
| 397 |
* Returns 0 on success or -1 if the methods list is invalid. |
| 398 |
*/ |
| 399 |
int |
| 400 |
auth2_methods_valid(const char *_methods, int need_enable) |
| 401 |
{ |
| 402 |
char *methods, *omethods, *method; |
| 403 |
u_int i, found; |
| 404 |
int ret = -1; |
| 405 |
|
| 406 |
if (*_methods == '\0') { |
| 407 |
error("empty authentication method list"); |
| 408 |
return -1; |
| 409 |
} |
| 410 |
omethods = methods = xstrdup(_methods); |
| 411 |
while ((method = strsep(&methods, ",")) != NULL) { |
| 412 |
for (found = i = 0; !found && authmethods[i] != NULL; i++) { |
| 413 |
if (strcmp(method, authmethods[i]->name) != 0) |
| 414 |
continue; |
| 415 |
if (need_enable) { |
| 416 |
if (authmethods[i]->enabled == NULL || |
| 417 |
*(authmethods[i]->enabled) == 0) { |
| 418 |
error("Disabled method \"%s\" in " |
| 419 |
"AuthenticationMethods list \"%s\"", |
| 420 |
method, _methods); |
| 421 |
goto out; |
| 422 |
} |
| 423 |
} |
| 424 |
found = 1; |
| 425 |
break; |
| 426 |
} |
| 427 |
if (!found) { |
| 428 |
error("Unknown authentication method \"%s\" in list", |
| 429 |
method); |
| 430 |
goto out; |
| 431 |
} |
| 432 |
} |
| 433 |
ret = 0; |
| 434 |
out: |
| 435 |
free(omethods); |
| 436 |
return ret; |
| 437 |
} |
| 438 |
|
| 439 |
/* |
| 440 |
* Prune the AuthenticationMethods supplied in the configuration, removing |
| 441 |
* any methods lists that include disabled methods. Note that this might |
| 442 |
* leave authctxt->num_auth_methods == 0, even when multiple required auth |
| 443 |
* has been requested. For this reason, all tests for whether multiple is |
| 444 |
* enabled should consult options.num_auth_methods directly. |
| 445 |
*/ |
| 446 |
int |
| 447 |
auth2_setup_methods_lists(Authctxt *authctxt) |
| 448 |
{ |
| 449 |
u_int i; |
| 450 |
|
| 451 |
if (options.num_auth_methods == 0) |
| 452 |
return 0; |
| 453 |
debug3("%s: checking methods", __func__); |
| 454 |
authctxt->auth_methods = xcalloc(options.num_auth_methods, |
| 455 |
sizeof(*authctxt->auth_methods)); |
| 456 |
authctxt->num_auth_methods = 0; |
| 457 |
for (i = 0; i < options.num_auth_methods; i++) { |
| 458 |
if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { |
| 459 |
logit("Authentication methods list \"%s\" contains " |
| 460 |
"disabled method, skipping", |
| 461 |
options.auth_methods[i]); |
| 462 |
continue; |
| 463 |
} |
| 464 |
debug("authentication methods list %d: %s", |
| 465 |
authctxt->num_auth_methods, options.auth_methods[i]); |
| 466 |
authctxt->auth_methods[authctxt->num_auth_methods++] = |
| 467 |
xstrdup(options.auth_methods[i]); |
| 468 |
} |
| 469 |
if (authctxt->num_auth_methods == 0) { |
| 470 |
error("No AuthenticationMethods left after eliminating " |
| 471 |
"disabled methods"); |
| 472 |
return -1; |
| 473 |
} |
| 474 |
return 0; |
| 475 |
} |
| 476 |
|
| 477 |
static int |
| 478 |
list_starts_with(const char *methods, const char *method) |
| 479 |
{ |
| 480 |
size_t l = strlen(method); |
| 481 |
|
| 482 |
if (strncmp(methods, method, l) != 0) |
| 483 |
return 0; |
| 484 |
if (methods[l] != ',' && methods[l] != '\0') |
| 485 |
return 0; |
| 486 |
return 1; |
| 487 |
} |
| 488 |
|
| 489 |
/* |
| 490 |
* Remove method from the start of a comma-separated list of methods. |
| 491 |
* Returns 0 if the list of methods did not start with that method or 1 |
| 492 |
* if it did. |
| 493 |
*/ |
| 494 |
static int |
| 495 |
remove_method(char **methods, const char *method) |
| 496 |
{ |
| 497 |
char *omethods = *methods; |
| 498 |
size_t l = strlen(method); |
| 499 |
|
| 500 |
if (!list_starts_with(omethods, method)) |
| 501 |
return 0; |
| 502 |
*methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0)); |
| 503 |
free(omethods); |
| 504 |
return 1; |
| 505 |
} |
| 506 |
|
| 507 |
/* |
| 508 |
* Called after successful authentication. Will remove the successful method |
| 509 |
* from the start of each list in which it occurs. If it was the last method |
| 510 |
* in any list, then authentication is deemed successful. |
| 511 |
* Returns 1 if the method completed any authentication list or 0 otherwise. |
| 512 |
*/ |
| 513 |
int |
| 514 |
auth2_update_methods_lists(Authctxt *authctxt, const char *method) |
| 515 |
{ |
| 516 |
u_int i, found = 0; |
| 517 |
|
| 518 |
debug3("%s: updating methods list after \"%s\"", __func__, method); |
| 519 |
for (i = 0; i < authctxt->num_auth_methods; i++) { |
| 520 |
if (!remove_method(&(authctxt->auth_methods[i]), method)) |
| 521 |
continue; |
| 522 |
found = 1; |
| 523 |
if (*authctxt->auth_methods[i] == '\0') { |
| 524 |
debug2("authentication methods list %d complete", i); |
| 525 |
return 1; |
| 526 |
} |
| 527 |
debug3("authentication methods list %d remaining: \"%s\"", |
| 528 |
i, authctxt->auth_methods[i]); |
| 529 |
} |
| 530 |
/* This should not happen, but would be bad if it did */ |
| 531 |
if (!found) |
| 532 |
fatal("%s: method not in AuthenticationMethods", __func__); |
| 533 |
return 0; |
| 534 |
} |
| 535 |
|
| 536 |
|