auth2-chall.c revision 98941
176259Sgreen/* 276259Sgreen * Copyright (c) 2001 Markus Friedl. All rights reserved. 392555Sdes * Copyright (c) 2001 Per Allansson. All rights reserved. 476259Sgreen * 576259Sgreen * Redistribution and use in source and binary forms, with or without 676259Sgreen * modification, are permitted provided that the following conditions 776259Sgreen * are met: 876259Sgreen * 1. Redistributions of source code must retain the above copyright 976259Sgreen * notice, this list of conditions and the following disclaimer. 1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1176259Sgreen * notice, this list of conditions and the following disclaimer in the 1276259Sgreen * documentation and/or other materials provided with the distribution. 1376259Sgreen * 1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2476259Sgreen */ 2576259Sgreen#include "includes.h" 2698684SdesRCSID("$OpenBSD: auth2-chall.c,v 1.18 2002/06/19 00:27:55 deraadt Exp $"); 2776259Sgreen 2876259Sgreen#include "ssh2.h" 2976259Sgreen#include "auth.h" 3092555Sdes#include "buffer.h" 3176259Sgreen#include "packet.h" 3276259Sgreen#include "xmalloc.h" 3376259Sgreen#include "dispatch.h" 3492555Sdes#include "auth.h" 3576259Sgreen#include "log.h" 3676259Sgreen 3792555Sdesstatic int auth2_challenge_start(Authctxt *); 3892555Sdesstatic int send_userauth_info_request(Authctxt *); 3992555Sdesstatic void input_userauth_info_response(int, u_int32_t, void *); 4076259Sgreen 4192555Sdes#ifdef BSD_AUTH 4292555Sdesextern KbdintDevice bsdauth_device; 4398941Sdes#else 4498941Sdes#ifdef SKEY 4592555Sdesextern KbdintDevice skey_device; 4692555Sdes#endif 4798941Sdes#endif 4892555Sdes 4992555SdesKbdintDevice *devices[] = { 5092555Sdes#ifdef BSD_AUTH 5192555Sdes &bsdauth_device, 5298941Sdes#else 5398941Sdes#ifdef SKEY 5492555Sdes &skey_device, 5592555Sdes#endif 5698941Sdes#endif 5792555Sdes NULL 5892555Sdes}; 5992555Sdes 6092555Sdestypedef struct KbdintAuthctxt KbdintAuthctxt; 6192555Sdesstruct KbdintAuthctxt 6292555Sdes{ 6392555Sdes char *devices; 6492555Sdes void *ctxt; 6592555Sdes KbdintDevice *device; 6692555Sdes}; 6792555Sdes 6892555Sdesstatic KbdintAuthctxt * 6992555Sdeskbdint_alloc(const char *devs) 7092555Sdes{ 7192555Sdes KbdintAuthctxt *kbdintctxt; 7292555Sdes Buffer b; 7392555Sdes int i; 7492555Sdes 7592555Sdes kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 7692555Sdes if (strcmp(devs, "") == 0) { 7792555Sdes buffer_init(&b); 7892555Sdes for (i = 0; devices[i]; i++) { 7992555Sdes if (buffer_len(&b) > 0) 8092555Sdes buffer_append(&b, ",", 1); 8192555Sdes buffer_append(&b, devices[i]->name, 8292555Sdes strlen(devices[i]->name)); 8392555Sdes } 8492555Sdes buffer_append(&b, "\0", 1); 8592555Sdes kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 8692555Sdes buffer_free(&b); 8792555Sdes } else { 8892555Sdes kbdintctxt->devices = xstrdup(devs); 8992555Sdes } 9092555Sdes debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 9192555Sdes kbdintctxt->ctxt = NULL; 9292555Sdes kbdintctxt->device = NULL; 9392555Sdes 9492555Sdes return kbdintctxt; 9592555Sdes} 9692555Sdesstatic void 9792555Sdeskbdint_reset_device(KbdintAuthctxt *kbdintctxt) 9892555Sdes{ 9992555Sdes if (kbdintctxt->ctxt) { 10092555Sdes kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 10192555Sdes kbdintctxt->ctxt = NULL; 10292555Sdes } 10392555Sdes kbdintctxt->device = NULL; 10492555Sdes} 10592555Sdesstatic void 10692555Sdeskbdint_free(KbdintAuthctxt *kbdintctxt) 10792555Sdes{ 10892555Sdes if (kbdintctxt->device) 10992555Sdes kbdint_reset_device(kbdintctxt); 11092555Sdes if (kbdintctxt->devices) { 11192555Sdes xfree(kbdintctxt->devices); 11292555Sdes kbdintctxt->devices = NULL; 11392555Sdes } 11492555Sdes xfree(kbdintctxt); 11592555Sdes} 11692555Sdes/* get next device */ 11792555Sdesstatic int 11892555Sdeskbdint_next_device(KbdintAuthctxt *kbdintctxt) 11992555Sdes{ 12092555Sdes size_t len; 12192555Sdes char *t; 12292555Sdes int i; 12392555Sdes 12492555Sdes if (kbdintctxt->device) 12592555Sdes kbdint_reset_device(kbdintctxt); 12692555Sdes do { 12792555Sdes len = kbdintctxt->devices ? 12892555Sdes strcspn(kbdintctxt->devices, ",") : 0; 12992555Sdes 13092555Sdes if (len == 0) 13192555Sdes break; 13292555Sdes for (i = 0; devices[i]; i++) 13392555Sdes if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 13492555Sdes kbdintctxt->device = devices[i]; 13592555Sdes t = kbdintctxt->devices; 13692555Sdes kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 13792555Sdes xfree(t); 13892555Sdes debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 13992555Sdes kbdintctxt->devices : "<empty>"); 14092555Sdes } while (kbdintctxt->devices && !kbdintctxt->device); 14192555Sdes 14292555Sdes return kbdintctxt->device ? 1 : 0; 14392555Sdes} 14492555Sdes 14576259Sgreen/* 14692555Sdes * try challenge-response, set authctxt->postponed if we have to 14776259Sgreen * wait for the response. 14876259Sgreen */ 14976259Sgreenint 15076259Sgreenauth2_challenge(Authctxt *authctxt, char *devs) 15176259Sgreen{ 15292555Sdes debug("auth2_challenge: user=%s devs=%s", 15392555Sdes authctxt->user ? authctxt->user : "<nouser>", 15492555Sdes devs ? devs : "<no devs>"); 15576259Sgreen 15692555Sdes if (authctxt->user == NULL || !devs) 15776259Sgreen return 0; 15892555Sdes if (authctxt->kbdintctxt == NULL) 15992555Sdes authctxt->kbdintctxt = kbdint_alloc(devs); 16092555Sdes return auth2_challenge_start(authctxt); 16192555Sdes} 16292555Sdes 16392555Sdes/* unregister kbd-int callbacks and context */ 16492555Sdesvoid 16592555Sdesauth2_challenge_stop(Authctxt *authctxt) 16692555Sdes{ 16792555Sdes /* unregister callback */ 16892555Sdes dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 16992555Sdes if (authctxt->kbdintctxt != NULL) { 17092555Sdes kbdint_free(authctxt->kbdintctxt); 17192555Sdes authctxt->kbdintctxt = NULL; 17292555Sdes } 17392555Sdes} 17492555Sdes 17592555Sdes/* side effect: sets authctxt->postponed if a reply was sent*/ 17692555Sdesstatic int 17792555Sdesauth2_challenge_start(Authctxt *authctxt) 17892555Sdes{ 17992555Sdes KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; 18092555Sdes 18192555Sdes debug2("auth2_challenge_start: devices %s", 18292555Sdes kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 18392555Sdes 18492555Sdes if (kbdint_next_device(kbdintctxt) == 0) { 18592555Sdes auth2_challenge_stop(authctxt); 18676259Sgreen return 0; 18792555Sdes } 18892555Sdes debug("auth2_challenge_start: trying authentication method '%s'", 18992555Sdes kbdintctxt->device->name); 19092555Sdes 19192555Sdes if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 19292555Sdes auth2_challenge_stop(authctxt); 19392555Sdes return 0; 19492555Sdes } 19592555Sdes if (send_userauth_info_request(authctxt) == 0) { 19692555Sdes auth2_challenge_stop(authctxt); 19792555Sdes return 0; 19892555Sdes } 19976259Sgreen dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 20076259Sgreen &input_userauth_info_response); 20192555Sdes 20276259Sgreen authctxt->postponed = 1; 20376259Sgreen return 0; 20476259Sgreen} 20576259Sgreen 20692555Sdesstatic int 20792555Sdessend_userauth_info_request(Authctxt *authctxt) 20876259Sgreen{ 20992555Sdes KbdintAuthctxt *kbdintctxt; 21092555Sdes char *name, *instr, **prompts; 21192555Sdes int i; 21292555Sdes u_int numprompts, *echo_on; 21376259Sgreen 21492555Sdes kbdintctxt = authctxt->kbdintctxt; 21592555Sdes if (kbdintctxt->device->query(kbdintctxt->ctxt, 21692555Sdes &name, &instr, &numprompts, &prompts, &echo_on)) 21792555Sdes return 0; 21892555Sdes 21976259Sgreen packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 22092555Sdes packet_put_cstring(name); 22192555Sdes packet_put_cstring(instr); 22298684Sdes packet_put_cstring(""); /* language not used */ 22392555Sdes packet_put_int(numprompts); 22492555Sdes for (i = 0; i < numprompts; i++) { 22592555Sdes packet_put_cstring(prompts[i]); 22692555Sdes packet_put_char(echo_on[i]); 22792555Sdes } 22876259Sgreen packet_send(); 22976259Sgreen packet_write_wait(); 23092555Sdes 23192555Sdes for (i = 0; i < numprompts; i++) 23292555Sdes xfree(prompts[i]); 23392555Sdes xfree(prompts); 23492555Sdes xfree(echo_on); 23592555Sdes xfree(name); 23692555Sdes xfree(instr); 23792555Sdes return 1; 23876259Sgreen} 23976259Sgreen 24092555Sdesstatic void 24192555Sdesinput_userauth_info_response(int type, u_int32_t seq, void *ctxt) 24276259Sgreen{ 24376259Sgreen Authctxt *authctxt = ctxt; 24492555Sdes KbdintAuthctxt *kbdintctxt; 24592555Sdes int i, authenticated = 0, res, len; 24692555Sdes u_int nresp; 24792555Sdes char **response = NULL, *method; 24876259Sgreen 24976259Sgreen if (authctxt == NULL) 25076259Sgreen fatal("input_userauth_info_response: no authctxt"); 25192555Sdes kbdintctxt = authctxt->kbdintctxt; 25292555Sdes if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 25392555Sdes fatal("input_userauth_info_response: no kbdintctxt"); 25492555Sdes if (kbdintctxt->device == NULL) 25592555Sdes fatal("input_userauth_info_response: no device"); 25676259Sgreen 25776259Sgreen authctxt->postponed = 0; /* reset */ 25876259Sgreen nresp = packet_get_int(); 25992555Sdes if (nresp > 0) { 26092555Sdes response = xmalloc(nresp * sizeof(char*)); 26192555Sdes for (i = 0; i < nresp; i++) 26292555Sdes response[i] = packet_get_string(NULL); 26392555Sdes } 26492555Sdes packet_check_eom(); 26592555Sdes 26692555Sdes if (authctxt->valid) { 26792555Sdes res = kbdintctxt->device->respond(kbdintctxt->ctxt, 26892555Sdes nresp, response); 26992555Sdes } else { 27092555Sdes res = -1; 27192555Sdes } 27292555Sdes 27392555Sdes for (i = 0; i < nresp; i++) { 27492555Sdes memset(response[i], 'r', strlen(response[i])); 27592555Sdes xfree(response[i]); 27692555Sdes } 27792555Sdes if (response) 27876259Sgreen xfree(response); 27992555Sdes 28092555Sdes switch (res) { 28192555Sdes case 0: 28292555Sdes /* Success! */ 28392555Sdes authenticated = 1; 28492555Sdes break; 28592555Sdes case 1: 28692555Sdes /* Authentication needs further interaction */ 28792555Sdes if (send_userauth_info_request(authctxt) == 1) 28892555Sdes authctxt->postponed = 1; 28992555Sdes break; 29092555Sdes default: 29192555Sdes /* Failure! */ 29292555Sdes break; 29376259Sgreen } 29476259Sgreen 29592555Sdes len = strlen("keyboard-interactive") + 2 + 29692555Sdes strlen(kbdintctxt->device->name); 29792555Sdes method = xmalloc(len); 29892555Sdes snprintf(method, len, "keyboard-interactive/%s", 29992555Sdes kbdintctxt->device->name); 30092555Sdes 30192555Sdes if (!authctxt->postponed) { 30292555Sdes if (authenticated) { 30392555Sdes auth2_challenge_stop(authctxt); 30492555Sdes } else { 30592555Sdes /* start next device */ 30692555Sdes /* may set authctxt->postponed */ 30792555Sdes auth2_challenge_start(authctxt); 30892555Sdes } 30992555Sdes } 31076259Sgreen userauth_finish(authctxt, authenticated, method); 31192555Sdes xfree(method); 31276259Sgreen} 31398684Sdes 31498684Sdesvoid 31598684Sdesprivsep_challenge_enable(void) 31698684Sdes{ 31798684Sdes#ifdef BSD_AUTH 31898684Sdes extern KbdintDevice mm_bsdauth_device; 31998684Sdes#endif 32098684Sdes#ifdef SKEY 32198684Sdes extern KbdintDevice mm_skey_device; 32298684Sdes#endif 32398684Sdes /* As long as SSHv1 has devices[0] hard coded this is fine */ 32498684Sdes#ifdef BSD_AUTH 32598684Sdes devices[0] = &mm_bsdauth_device; 32698684Sdes#else 32798684Sdes#ifdef SKEY 32898684Sdes devices[0] = &mm_skey_device; 32998684Sdes#endif 33098684Sdes#endif 33198684Sdes} 332