|
Lines 2-7
Link Here
|
| 2 |
/* |
2 |
/* |
| 3 |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
3 |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
| 4 |
* Copyright (c) 2008 Damien Miller. All rights reserved. |
4 |
* Copyright (c) 2008 Damien Miller. All rights reserved. |
|
|
5 |
* Copyright (c) 2014 Google Inc. All rights reserved. |
| 5 |
* |
6 |
* |
| 6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* Redistribution and use in source and binary forms, with or without |
| 7 |
* modification, are permitted provided that the following conditions |
8 |
* modification, are permitted provided that the following conditions |
|
Lines 30-35
Link Here
|
| 30 |
#include <sys/socket.h> |
31 |
#include <sys/socket.h> |
| 31 |
#include <sys/wait.h> |
32 |
#include <sys/wait.h> |
| 32 |
#include <sys/stat.h> |
33 |
#include <sys/stat.h> |
|
|
34 |
#include <time.h> |
| 33 |
|
35 |
|
| 34 |
#include <errno.h> |
36 |
#include <errno.h> |
| 35 |
#include <fcntl.h> |
37 |
#include <fcntl.h> |
|
Lines 44-49
Link Here
|
| 44 |
#include <vis.h> |
46 |
#include <vis.h> |
| 45 |
#endif |
47 |
#endif |
| 46 |
|
48 |
|
|
|
49 |
#ifdef U2F |
| 50 |
#include <u2f-host.h> |
| 51 |
#endif |
| 52 |
|
| 47 |
#include "openbsd-compat/sys-queue.h" |
53 |
#include "openbsd-compat/sys-queue.h" |
| 48 |
|
54 |
|
| 49 |
#include "xmalloc.h" |
55 |
#include "xmalloc.h" |
|
Lines 71-76
Link Here
|
| 71 |
#include "uidswap.h" |
77 |
#include "uidswap.h" |
| 72 |
#include "hostfile.h" |
78 |
#include "hostfile.h" |
| 73 |
#include "ssherr.h" |
79 |
#include "ssherr.h" |
|
|
80 |
#include "u2f.h" |
| 74 |
|
81 |
|
| 75 |
#ifdef GSSAPI |
82 |
#ifdef GSSAPI |
| 76 |
#include "ssh-gss.h" |
83 |
#include "ssh-gss.h" |
|
Lines 258-263
struct cauthctxt {
Link Here
|
| 258 |
const char *server_user; |
265 |
const char *server_user; |
| 259 |
const char *local_user; |
266 |
const char *local_user; |
| 260 |
const char *host; |
267 |
const char *host; |
|
|
268 |
char *host_port; |
| 261 |
const char *service; |
269 |
const char *service; |
| 262 |
struct cauthmethod *method; |
270 |
struct cauthmethod *method; |
| 263 |
sig_atomic_t success; |
271 |
sig_atomic_t success; |
|
Lines 308-313
int input_gssapi_error(int, u_int32_t, void *);
Link Here
|
| 308 |
int input_gssapi_errtok(int, u_int32_t, void *); |
316 |
int input_gssapi_errtok(int, u_int32_t, void *); |
| 309 |
#endif |
317 |
#endif |
| 310 |
|
318 |
|
|
|
319 |
#ifdef U2F |
| 320 |
int userauth_u2f(Authctxt *authctxt); |
| 321 |
int input_userauth_u2f_authenticate(int type, u_int32_t seq, void *ctxt); |
| 322 |
int input_userauth_u2f_register(int type, u_int32_t seq, void *ctxt); |
| 323 |
int input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt); |
| 324 |
#endif |
| 325 |
|
| 311 |
void userauth(Authctxt *, char *); |
326 |
void userauth(Authctxt *, char *); |
| 312 |
|
327 |
|
| 313 |
static int sign_and_send_pubkey(Authctxt *, Identity *); |
328 |
static int sign_and_send_pubkey(Authctxt *, Identity *); |
|
Lines 320-325
static Authmethod *authmethod_lookup(const char *name);
Link Here
|
| 320 |
static char *authmethods_get(void); |
335 |
static char *authmethods_get(void); |
| 321 |
|
336 |
|
| 322 |
Authmethod authmethods[] = { |
337 |
Authmethod authmethods[] = { |
|
|
338 |
// U2F needs to be the first authentication method, so that we use it once |
| 339 |
// the server allows it. This enables server configurations containing e.g.: |
| 340 |
// AuthenticationMethods password,u2f pubkey,u2f |
| 341 |
#ifdef U2F |
| 342 |
{"u2f", |
| 343 |
userauth_u2f, |
| 344 |
NULL, |
| 345 |
&options.u2f_authentication, |
| 346 |
NULL}, |
| 347 |
#endif |
| 323 |
#ifdef GSSAPI |
348 |
#ifdef GSSAPI |
| 324 |
{"gssapi-with-mic", |
349 |
{"gssapi-with-mic", |
| 325 |
userauth_gssapi, |
350 |
userauth_gssapi, |
|
Lines 357-363
Authmethod authmethods[] = {
Link Here
|
| 357 |
|
382 |
|
| 358 |
void |
383 |
void |
| 359 |
ssh_userauth2(const char *local_user, const char *server_user, char *host, |
384 |
ssh_userauth2(const char *local_user, const char *server_user, char *host, |
| 360 |
Sensitive *sensitive) |
385 |
u_short port, Sensitive *sensitive) |
| 361 |
{ |
386 |
{ |
| 362 |
Authctxt authctxt; |
387 |
Authctxt authctxt; |
| 363 |
int type; |
388 |
int type; |
|
Lines 392-397
ssh_userauth2(const char *local_user, const char *server_user, char *host,
Link Here
|
| 392 |
authctxt.server_user = server_user; |
417 |
authctxt.server_user = server_user; |
| 393 |
authctxt.local_user = local_user; |
418 |
authctxt.local_user = local_user; |
| 394 |
authctxt.host = host; |
419 |
authctxt.host = host; |
|
|
420 |
get_hostfile_hostname_ipaddr(host, NULL, port, &authctxt.host_port, NULL); |
| 395 |
authctxt.service = "ssh-connection"; /* service name */ |
421 |
authctxt.service = "ssh-connection"; /* service name */ |
| 396 |
authctxt.success = 0; |
422 |
authctxt.success = 0; |
| 397 |
authctxt.method = authmethod_lookup("none"); |
423 |
authctxt.method = authmethod_lookup("none"); |
|
Lines 852-857
input_gssapi_error(int type, u_int32_t plen, void *ctxt)
Link Here
|
| 852 |
} |
878 |
} |
| 853 |
#endif /* GSSAPI */ |
879 |
#endif /* GSSAPI */ |
| 854 |
|
880 |
|
|
|
881 |
#ifdef U2F |
| 882 |
int |
| 883 |
userauth_u2f(Authctxt *authctxt) |
| 884 |
{ |
| 885 |
// first step: we dont send anything, but install a custom dispatcher. |
| 886 |
debug("sshconnect2:userauth_u2f"); |
| 887 |
|
| 888 |
// For U2F_MODE_REGISTRATION, this code path will return 0, meaning the |
| 889 |
// authentication method will not be retried. If we did not do that, we |
| 890 |
// would loop endlessly. |
| 891 |
if (authctxt->info_req_seen) { |
| 892 |
dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); |
| 893 |
return 0; |
| 894 |
} |
| 895 |
|
| 896 |
packet_start(SSH2_MSG_USERAUTH_REQUEST); |
| 897 |
packet_put_cstring(authctxt->server_user); |
| 898 |
packet_put_cstring(authctxt->service); |
| 899 |
packet_put_cstring(authctxt->method->name); |
| 900 |
if (options.u2f_mode == NULL || strcasecmp(options.u2f_mode, "authentication") == 0) { |
| 901 |
packet_put_int(U2F_MODE_AUTHENTICATION); |
| 902 |
dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_authenticate); |
| 903 |
} else if (options.u2f_mode != NULL && strcasecmp(options.u2f_mode, "registration") == 0) { |
| 904 |
packet_put_int(U2F_MODE_REGISTRATION); |
| 905 |
dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_register); |
| 906 |
} else { |
| 907 |
fatal("Invalid U2F mode (\"%s\"), expected \"authentication\" or \"registration\".", |
| 908 |
options.u2f_mode); |
| 909 |
} |
| 910 |
packet_send(); |
| 911 |
|
| 912 |
return 1; |
| 913 |
} |
| 914 |
|
| 915 |
static void |
| 916 |
wait_for_u2f_devices(u2fh_devs *devs) |
| 917 |
{ |
| 918 |
time_t looking; |
| 919 |
int attempts = 0; |
| 920 |
u2fh_rc rc; |
| 921 |
|
| 922 |
// The U2F implementation considerations recommend 3 seconds as the time a |
| 923 |
// client implementation should grant for security keys to respond. We wait |
| 924 |
// 3 times that for the user to insert a security key (and it being |
| 925 |
// detected). |
| 926 |
looking = monotime(); |
| 927 |
do { |
| 928 |
if ((rc = u2fh_devs_discover(devs, NULL)) != U2FH_OK && attempts++ == 0) |
| 929 |
error("Please insert and touch your U2F security key."); |
| 930 |
if (rc != U2FH_OK) |
| 931 |
usleep(50); |
| 932 |
} while (rc != U2FH_OK && (monotime() - looking) <= 9); |
| 933 |
if (rc != U2FH_OK) |
| 934 |
fatal("No U2F devices found (%s). Did you plug in your U2F security key?", |
| 935 |
u2fh_strerror(rc)); |
| 936 |
|
| 937 |
if (attempts == 0) |
| 938 |
error("Please touch your U2F security key now."); |
| 939 |
} |
| 940 |
|
| 941 |
int |
| 942 |
input_userauth_u2f_register(int type, u_int32_t seq, void *ctxt) |
| 943 |
{ |
| 944 |
Authctxt *authctxt = ctxt; |
| 945 |
char *challenge, *response; |
| 946 |
u2fh_devs *devs = NULL; |
| 947 |
u2fh_rc rc; |
| 948 |
const char *origin = authctxt->host_port; |
| 949 |
|
| 950 |
if (authctxt == NULL) |
| 951 |
fatal("input_userauth_u2f_register: no authentication context"); |
| 952 |
|
| 953 |
authctxt->info_req_seen = 1; |
| 954 |
|
| 955 |
challenge = packet_get_string(NULL); |
| 956 |
packet_check_eom(); |
| 957 |
|
| 958 |
if ((rc = u2fh_devs_init(&devs)) != U2FH_OK) |
| 959 |
fatal("u2fh_devs_init() failed: %s", u2fh_strerror(rc)); |
| 960 |
|
| 961 |
wait_for_u2f_devices(devs); |
| 962 |
|
| 963 |
if ((rc = u2fh_register(devs, challenge, origin, &response, U2FH_REQUEST_USER_PRESENCE)) != U2FH_OK) |
| 964 |
fatal("u2fh_register() failed: %s", u2fh_strerror(rc)); |
| 965 |
|
| 966 |
u2fh_devs_done(devs); |
| 967 |
|
| 968 |
packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); |
| 969 |
packet_put_cstring(response); |
| 970 |
packet_send(); |
| 971 |
|
| 972 |
free(response); |
| 973 |
dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); |
| 974 |
dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_register_response); |
| 975 |
return 0; |
| 976 |
} |
| 977 |
|
| 978 |
int |
| 979 |
input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt) |
| 980 |
{ |
| 981 |
char *response = packet_get_string(NULL); |
| 982 |
printf("%s\n", response); |
| 983 |
fflush(stdout); |
| 984 |
return 0; |
| 985 |
} |
| 986 |
|
| 987 |
int |
| 988 |
input_userauth_u2f_authenticate(int type, u_int32_t seq, void *ctxt) |
| 989 |
{ |
| 990 |
Authctxt *authctxt = ctxt; |
| 991 |
char *challenge, *response; |
| 992 |
u2fh_devs *devs = NULL; |
| 993 |
u2fh_rc rc; |
| 994 |
const char *origin = authctxt->host_port; |
| 995 |
|
| 996 |
if (authctxt == NULL) |
| 997 |
fatal("input_userauth_u2f_authenticate: no authentication context"); |
| 998 |
|
| 999 |
authctxt->info_req_seen = 1; |
| 1000 |
|
| 1001 |
challenge = packet_get_string(NULL); |
| 1002 |
packet_check_eom(); |
| 1003 |
|
| 1004 |
debug("Starting U2F authentication for origin \"%s\".", origin); |
| 1005 |
|
| 1006 |
if ((rc = u2fh_devs_init(&devs)) != U2FH_OK) |
| 1007 |
fatal("u2fh_devs_init() failed: %s", u2fh_strerror(rc)); |
| 1008 |
|
| 1009 |
wait_for_u2f_devices(devs); |
| 1010 |
|
| 1011 |
// TODO: refactor with input_userauth_u2f_register(), the following line is the only one that is different :) |
| 1012 |
if ((rc = u2fh_authenticate(devs, challenge, origin, &response, U2FH_REQUEST_USER_PRESENCE)) != U2FH_OK) |
| 1013 |
fatal("u2fh_authenticate() failed: %s", u2fh_strerror(rc)); |
| 1014 |
|
| 1015 |
u2fh_devs_done(devs); |
| 1016 |
|
| 1017 |
packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); |
| 1018 |
packet_put_cstring(response); |
| 1019 |
packet_send(); |
| 1020 |
|
| 1021 |
free(response); |
| 1022 |
return 0; |
| 1023 |
|
| 1024 |
// We intentionally do not set SSH2_MSG_USERAUTH_INFO_REQUEST to NULL, |
| 1025 |
// because the server might send us more challenges (in case more than one |
| 1026 |
// U2F security key is in the authorized_keys). |
| 1027 |
} |
| 1028 |
|
| 1029 |
#endif /* U2F */ |
| 1030 |
|
| 855 |
int |
1031 |
int |
| 856 |
userauth_none(Authctxt *authctxt) |
1032 |
userauth_none(Authctxt *authctxt) |
| 857 |
{ |
1033 |
{ |