10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * Copyright (c) 2001 Markus Friedl. All rights reserved.
30Sstevel@tonic-gate * Copyright (c) 2001 Per Allansson. All rights reserved.
40Sstevel@tonic-gate *
50Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
60Sstevel@tonic-gate * modification, are permitted provided that the following conditions
70Sstevel@tonic-gate * are met:
80Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
90Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
100Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
110Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
120Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
130Sstevel@tonic-gate *
140Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
150Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
160Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
170Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
180Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
190Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
200Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
210Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
220Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
230Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate /*
26*9600SNobutomo.Nakano@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
270Sstevel@tonic-gate * Use is subject to license terms.
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
300Sstevel@tonic-gate #include "includes.h"
310Sstevel@tonic-gate RCSID("$OpenBSD: auth2-chall.c,v 1.20 2002/06/30 21:59:45 deraadt Exp $");
320Sstevel@tonic-gate
330Sstevel@tonic-gate #include "ssh2.h"
340Sstevel@tonic-gate #include "auth.h"
350Sstevel@tonic-gate #include "buffer.h"
360Sstevel@tonic-gate #include "packet.h"
370Sstevel@tonic-gate #include "xmalloc.h"
380Sstevel@tonic-gate #include "dispatch.h"
390Sstevel@tonic-gate #include "auth.h"
400Sstevel@tonic-gate #include "log.h"
410Sstevel@tonic-gate
420Sstevel@tonic-gate #ifndef lint
430Sstevel@tonic-gate static void auth2_challenge_start(Authctxt *);
440Sstevel@tonic-gate static int send_userauth_info_request(Authctxt *);
450Sstevel@tonic-gate static void input_userauth_info_response(int, u_int32_t, void *);
460Sstevel@tonic-gate
470Sstevel@tonic-gate #ifdef BSD_AUTH
480Sstevel@tonic-gate extern KbdintDevice bsdauth_device;
490Sstevel@tonic-gate #else
500Sstevel@tonic-gate #ifdef SKEY
510Sstevel@tonic-gate extern KbdintDevice skey_device;
520Sstevel@tonic-gate #endif
530Sstevel@tonic-gate #endif
540Sstevel@tonic-gate
550Sstevel@tonic-gate KbdintDevice *devices[] = {
560Sstevel@tonic-gate #ifdef BSD_AUTH
570Sstevel@tonic-gate &bsdauth_device,
580Sstevel@tonic-gate #else
590Sstevel@tonic-gate #ifdef SKEY
600Sstevel@tonic-gate &skey_device,
610Sstevel@tonic-gate #endif
620Sstevel@tonic-gate #endif
630Sstevel@tonic-gate NULL
640Sstevel@tonic-gate };
650Sstevel@tonic-gate
660Sstevel@tonic-gate typedef struct KbdintAuthctxt KbdintAuthctxt;
670Sstevel@tonic-gate struct KbdintAuthctxt
680Sstevel@tonic-gate {
690Sstevel@tonic-gate char *devices;
700Sstevel@tonic-gate void *ctxt;
710Sstevel@tonic-gate KbdintDevice *device;
720Sstevel@tonic-gate u_int nreq;
730Sstevel@tonic-gate };
740Sstevel@tonic-gate
750Sstevel@tonic-gate static KbdintAuthctxt *
kbdint_alloc(const char * devs)760Sstevel@tonic-gate kbdint_alloc(const char *devs)
770Sstevel@tonic-gate {
780Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt;
790Sstevel@tonic-gate Buffer b;
800Sstevel@tonic-gate int i;
810Sstevel@tonic-gate
820Sstevel@tonic-gate kbdintctxt = xmalloc(sizeof(KbdintAuthctxt));
830Sstevel@tonic-gate if (strcmp(devs, "") == 0) {
840Sstevel@tonic-gate buffer_init(&b);
850Sstevel@tonic-gate for (i = 0; devices[i]; i++) {
860Sstevel@tonic-gate if (buffer_len(&b) > 0)
870Sstevel@tonic-gate buffer_append(&b, ",", 1);
880Sstevel@tonic-gate buffer_append(&b, devices[i]->name,
890Sstevel@tonic-gate strlen(devices[i]->name));
900Sstevel@tonic-gate }
910Sstevel@tonic-gate buffer_append(&b, "\0", 1);
920Sstevel@tonic-gate kbdintctxt->devices = xstrdup(buffer_ptr(&b));
930Sstevel@tonic-gate buffer_free(&b);
940Sstevel@tonic-gate } else {
950Sstevel@tonic-gate kbdintctxt->devices = xstrdup(devs);
960Sstevel@tonic-gate }
970Sstevel@tonic-gate debug("kbdint_alloc: devices '%s'", kbdintctxt->devices);
980Sstevel@tonic-gate kbdintctxt->ctxt = NULL;
990Sstevel@tonic-gate kbdintctxt->device = NULL;
1000Sstevel@tonic-gate kbdintctxt->nreq = 0;
1010Sstevel@tonic-gate
1020Sstevel@tonic-gate return kbdintctxt;
1030Sstevel@tonic-gate }
1040Sstevel@tonic-gate static void
kbdint_reset_device(KbdintAuthctxt * kbdintctxt)1050Sstevel@tonic-gate kbdint_reset_device(KbdintAuthctxt *kbdintctxt)
1060Sstevel@tonic-gate {
1070Sstevel@tonic-gate if (kbdintctxt->ctxt) {
1080Sstevel@tonic-gate kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
1090Sstevel@tonic-gate kbdintctxt->ctxt = NULL;
1100Sstevel@tonic-gate }
1110Sstevel@tonic-gate kbdintctxt->device = NULL;
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate static void
kbdint_free(KbdintAuthctxt * kbdintctxt)1140Sstevel@tonic-gate kbdint_free(KbdintAuthctxt *kbdintctxt)
1150Sstevel@tonic-gate {
1160Sstevel@tonic-gate if (kbdintctxt->device)
1170Sstevel@tonic-gate kbdint_reset_device(kbdintctxt);
1180Sstevel@tonic-gate if (kbdintctxt->devices) {
1190Sstevel@tonic-gate xfree(kbdintctxt->devices);
1200Sstevel@tonic-gate kbdintctxt->devices = NULL;
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate xfree(kbdintctxt);
1230Sstevel@tonic-gate }
1240Sstevel@tonic-gate /* get next device */
1250Sstevel@tonic-gate static int
kbdint_next_device(KbdintAuthctxt * kbdintctxt)1260Sstevel@tonic-gate kbdint_next_device(KbdintAuthctxt *kbdintctxt)
1270Sstevel@tonic-gate {
1280Sstevel@tonic-gate size_t len;
1290Sstevel@tonic-gate char *t;
1300Sstevel@tonic-gate int i;
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate if (kbdintctxt->device)
1330Sstevel@tonic-gate kbdint_reset_device(kbdintctxt);
1340Sstevel@tonic-gate do {
1350Sstevel@tonic-gate len = kbdintctxt->devices ?
1360Sstevel@tonic-gate strcspn(kbdintctxt->devices, ",") : 0;
1370Sstevel@tonic-gate
1380Sstevel@tonic-gate if (len == 0)
1390Sstevel@tonic-gate break;
1400Sstevel@tonic-gate for (i = 0; devices[i]; i++)
1410Sstevel@tonic-gate if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
1420Sstevel@tonic-gate kbdintctxt->device = devices[i];
1430Sstevel@tonic-gate t = kbdintctxt->devices;
1440Sstevel@tonic-gate kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
1450Sstevel@tonic-gate xfree(t);
1460Sstevel@tonic-gate debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
1470Sstevel@tonic-gate kbdintctxt->devices : "<empty>");
1480Sstevel@tonic-gate } while (kbdintctxt->devices && !kbdintctxt->device);
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate return kbdintctxt->device ? 1 : 0;
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate
1530Sstevel@tonic-gate /*
1540Sstevel@tonic-gate * try challenge-response, set authctxt->method->postponed if we have to
1550Sstevel@tonic-gate * wait for the response.
1560Sstevel@tonic-gate */
1570Sstevel@tonic-gate void
auth2_challenge(Authctxt * authctxt,char * devs)1580Sstevel@tonic-gate auth2_challenge(Authctxt *authctxt, char *devs)
1590Sstevel@tonic-gate {
1600Sstevel@tonic-gate debug("auth2_challenge: user=%s devs=%s",
1610Sstevel@tonic-gate authctxt->user ? authctxt->user : "<nouser>",
1620Sstevel@tonic-gate devs ? devs : "<no devs>");
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate if (authctxt->user == NULL || !devs)
1650Sstevel@tonic-gate return;
1660Sstevel@tonic-gate if (authctxt->method->method_data != NULL) {
1670Sstevel@tonic-gate auth2_challenge_abandon(authctxt);
1680Sstevel@tonic-gate authctxt->method->abandoned = 0;
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate authctxt->method->method_data = (void *) kbdint_alloc(devs);
1710Sstevel@tonic-gate auth2_challenge_start(authctxt);
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate /* unregister kbd-int callbacks and context */
1750Sstevel@tonic-gate static void
auth2_challenge_stop(Authctxt * authctxt)1760Sstevel@tonic-gate auth2_challenge_stop(Authctxt *authctxt)
1770Sstevel@tonic-gate {
1780Sstevel@tonic-gate /* unregister callback */
1790Sstevel@tonic-gate dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
1800Sstevel@tonic-gate if (authctxt->method->method_data != NULL) {
1810Sstevel@tonic-gate kbdint_free((KbdintAuthctxt *) authctxt->method->method_data);
1820Sstevel@tonic-gate authctxt->method->method_data = NULL;
1830Sstevel@tonic-gate }
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate void
auth2_challenge_abandon(Authctxt * authctxt)1870Sstevel@tonic-gate auth2_challenge_abandon(Authctxt *authctxt)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate auth2_challenge_stop(authctxt);
1900Sstevel@tonic-gate authctxt->method->abandoned = 1;
1910Sstevel@tonic-gate authctxt->method->postponed = 0;
1920Sstevel@tonic-gate authctxt->method->authenticated = 0;
1930Sstevel@tonic-gate authctxt->method->abandons++;
1940Sstevel@tonic-gate authctxt->method->attempts++;
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate /* side effect: sets authctxt->method->postponed if a reply was sent*/
1980Sstevel@tonic-gate static void
auth2_challenge_start(Authctxt * authctxt)1990Sstevel@tonic-gate auth2_challenge_start(Authctxt *authctxt)
2000Sstevel@tonic-gate {
2010Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt = (KbdintAuthctxt *)
2020Sstevel@tonic-gate authctxt->method->method_data;
2030Sstevel@tonic-gate
2040Sstevel@tonic-gate debug2("auth2_challenge_start: devices %s",
2050Sstevel@tonic-gate kbdintctxt->devices ? kbdintctxt->devices : "<empty>");
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate if (kbdint_next_device(kbdintctxt) == 0) {
2080Sstevel@tonic-gate auth2_challenge_stop(authctxt);
2090Sstevel@tonic-gate return;
2100Sstevel@tonic-gate }
2110Sstevel@tonic-gate debug("auth2_challenge_start: trying authentication method '%s'",
2120Sstevel@tonic-gate kbdintctxt->device->name);
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
2150Sstevel@tonic-gate auth2_challenge_stop(authctxt);
2160Sstevel@tonic-gate return;
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate if (send_userauth_info_request(authctxt) == 0) {
2190Sstevel@tonic-gate auth2_challenge_stop(authctxt);
2200Sstevel@tonic-gate return;
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
2230Sstevel@tonic-gate &input_userauth_info_response);
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate authctxt->method->postponed = 1;
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate static int
send_userauth_info_request(Authctxt * authctxt)2290Sstevel@tonic-gate send_userauth_info_request(Authctxt *authctxt)
2300Sstevel@tonic-gate {
2310Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt;
2320Sstevel@tonic-gate char *name, *instr, **prompts;
2330Sstevel@tonic-gate int i;
2340Sstevel@tonic-gate u_int *echo_on;
2350Sstevel@tonic-gate
2360Sstevel@tonic-gate kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data;
2370Sstevel@tonic-gate if (kbdintctxt->device->query(kbdintctxt->ctxt,
2380Sstevel@tonic-gate &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on))
2390Sstevel@tonic-gate return 0;
2400Sstevel@tonic-gate
2410Sstevel@tonic-gate packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
2420Sstevel@tonic-gate packet_put_cstring(name);
243*9600SNobutomo.Nakano@Sun.COM packet_put_utf8_cstring(instr);
2440Sstevel@tonic-gate packet_put_cstring(""); /* language not used */
2450Sstevel@tonic-gate packet_put_int(kbdintctxt->nreq);
2460Sstevel@tonic-gate for (i = 0; i < kbdintctxt->nreq; i++) {
247*9600SNobutomo.Nakano@Sun.COM packet_put_utf8_cstring(prompts[i]);
2480Sstevel@tonic-gate packet_put_char(echo_on[i]);
2490Sstevel@tonic-gate }
2500Sstevel@tonic-gate packet_send();
2510Sstevel@tonic-gate packet_write_wait();
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate for (i = 0; i < kbdintctxt->nreq; i++)
2540Sstevel@tonic-gate xfree(prompts[i]);
2550Sstevel@tonic-gate xfree(prompts);
2560Sstevel@tonic-gate xfree(echo_on);
2570Sstevel@tonic-gate xfree(name);
2580Sstevel@tonic-gate xfree(instr);
2590Sstevel@tonic-gate return 1;
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate
2620Sstevel@tonic-gate static void
input_userauth_info_response(int type,u_int32_t seq,void * ctxt)2630Sstevel@tonic-gate input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
2640Sstevel@tonic-gate {
2650Sstevel@tonic-gate Authctxt *authctxt = ctxt;
2660Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt;
2670Sstevel@tonic-gate int i, res, len;
2680Sstevel@tonic-gate u_int nresp;
2690Sstevel@tonic-gate char **response = NULL, *method;
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate if (authctxt == NULL)
2720Sstevel@tonic-gate fatal("input_userauth_info_response: no authctxt");
2730Sstevel@tonic-gate kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data;
2740Sstevel@tonic-gate if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
2750Sstevel@tonic-gate fatal("input_userauth_info_response: no kbdintctxt");
2760Sstevel@tonic-gate if (kbdintctxt->device == NULL)
2770Sstevel@tonic-gate fatal("input_userauth_info_response: no device");
2780Sstevel@tonic-gate
2790Sstevel@tonic-gate nresp = packet_get_int();
2800Sstevel@tonic-gate if (nresp != kbdintctxt->nreq)
2810Sstevel@tonic-gate fatal("input_userauth_info_response: wrong number of replies");
2820Sstevel@tonic-gate if (nresp > 100)
2830Sstevel@tonic-gate fatal("input_userauth_info_response: too many replies");
2840Sstevel@tonic-gate if (nresp > 0) {
2850Sstevel@tonic-gate response = xmalloc(nresp * sizeof(char *));
2860Sstevel@tonic-gate for (i = 0; i < nresp; i++)
2870Sstevel@tonic-gate response[i] = packet_get_string(NULL);
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate packet_check_eom();
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate if (authctxt->valid) {
2920Sstevel@tonic-gate res = kbdintctxt->device->respond(kbdintctxt->ctxt,
2930Sstevel@tonic-gate nresp, response);
2940Sstevel@tonic-gate } else {
2950Sstevel@tonic-gate res = -1;
2960Sstevel@tonic-gate }
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate for (i = 0; i < nresp; i++) {
2990Sstevel@tonic-gate memset(response[i], 'r', strlen(response[i]));
3000Sstevel@tonic-gate xfree(response[i]);
3010Sstevel@tonic-gate }
3020Sstevel@tonic-gate if (response)
3030Sstevel@tonic-gate xfree(response);
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate authctxt->method->postponed = 0; /* reset */
3060Sstevel@tonic-gate switch (res) {
3070Sstevel@tonic-gate case 0:
3080Sstevel@tonic-gate /* Success! */
3090Sstevel@tonic-gate authctxt->method->authenticated = 1;
3100Sstevel@tonic-gate break;
3110Sstevel@tonic-gate case 1:
3120Sstevel@tonic-gate /* Authentication needs further interaction */
3130Sstevel@tonic-gate if (send_userauth_info_request(authctxt) == 1) {
3140Sstevel@tonic-gate authctxt->method->postponed = 1;
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate break;
3170Sstevel@tonic-gate default:
3180Sstevel@tonic-gate /* Failure! */
3190Sstevel@tonic-gate break;
3200Sstevel@tonic-gate }
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate
3230Sstevel@tonic-gate len = strlen("keyboard-interactive") + 2 +
3240Sstevel@tonic-gate strlen(kbdintctxt->device->name);
3250Sstevel@tonic-gate method = xmalloc(len);
3260Sstevel@tonic-gate snprintf(method, len, "keyboard-interactive/%s",
3270Sstevel@tonic-gate kbdintctxt->device->name);
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate if (authctxt->method->authenticated || authctxt->method->abandoned) {
3300Sstevel@tonic-gate auth2_challenge_stop(authctxt);
3310Sstevel@tonic-gate } else {
3320Sstevel@tonic-gate /* start next device */
3330Sstevel@tonic-gate /* may set authctxt->method->postponed */
3340Sstevel@tonic-gate auth2_challenge_start(authctxt);
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate userauth_finish(authctxt, method);
3370Sstevel@tonic-gate xfree(method);
3380Sstevel@tonic-gate }
3390Sstevel@tonic-gate
3400Sstevel@tonic-gate void
privsep_challenge_enable(void)3410Sstevel@tonic-gate privsep_challenge_enable(void)
3420Sstevel@tonic-gate {
3430Sstevel@tonic-gate #ifdef BSD_AUTH
3440Sstevel@tonic-gate extern KbdintDevice mm_bsdauth_device;
3450Sstevel@tonic-gate #endif
3460Sstevel@tonic-gate #ifdef SKEY
3470Sstevel@tonic-gate extern KbdintDevice mm_skey_device;
3480Sstevel@tonic-gate #endif
3490Sstevel@tonic-gate /* As long as SSHv1 has devices[0] hard coded this is fine */
3500Sstevel@tonic-gate #ifdef BSD_AUTH
3510Sstevel@tonic-gate devices[0] = &mm_bsdauth_device;
3520Sstevel@tonic-gate #else
3530Sstevel@tonic-gate #ifdef SKEY
3540Sstevel@tonic-gate devices[0] = &mm_skey_device;
3550Sstevel@tonic-gate #endif
3560Sstevel@tonic-gate #endif
3570Sstevel@tonic-gate }
3580Sstevel@tonic-gate #endif /* lint */
359