auth2-chall.c revision 149753
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" 26149753SdesRCSID("$OpenBSD: auth2-chall.c,v 1.24 2005/07/17 07:17:54 djm Exp $"); 2799052SdesRCSID("$FreeBSD: head/crypto/openssh/auth2-chall.c 149753 2005-09-03 07:04:25Z des $"); 2876259Sgreen 2976259Sgreen#include "ssh2.h" 3076259Sgreen#include "auth.h" 3192555Sdes#include "buffer.h" 3276259Sgreen#include "packet.h" 3376259Sgreen#include "xmalloc.h" 3476259Sgreen#include "dispatch.h" 3576259Sgreen#include "log.h" 36147005Sdes#include "servconf.h" 3776259Sgreen 38147005Sdes/* import */ 39147005Sdesextern ServerOptions options; 40147005Sdes 4192555Sdesstatic int auth2_challenge_start(Authctxt *); 4292555Sdesstatic int send_userauth_info_request(Authctxt *); 4392555Sdesstatic void input_userauth_info_response(int, u_int32_t, void *); 4476259Sgreen 4592555Sdes#ifdef BSD_AUTH 4692555Sdesextern KbdintDevice bsdauth_device; 4798941Sdes#else 4899052Sdes#ifdef USE_PAM 49124211Sdesextern KbdintDevice sshpam_device; 5099052Sdes#endif 5198941Sdes#ifdef SKEY 5292555Sdesextern KbdintDevice skey_device; 5392555Sdes#endif 5498941Sdes#endif 5592555Sdes 5692555SdesKbdintDevice *devices[] = { 5792555Sdes#ifdef BSD_AUTH 5892555Sdes &bsdauth_device, 5998941Sdes#else 6099052Sdes#ifdef USE_PAM 61124211Sdes &sshpam_device, 6299052Sdes#endif 6398941Sdes#ifdef SKEY 6492555Sdes &skey_device, 6592555Sdes#endif 6698941Sdes#endif 6792555Sdes NULL 6892555Sdes}; 6992555Sdes 7092555Sdestypedef struct KbdintAuthctxt KbdintAuthctxt; 7192555Sdesstruct KbdintAuthctxt 7292555Sdes{ 7392555Sdes char *devices; 7492555Sdes void *ctxt; 7592555Sdes KbdintDevice *device; 7699063Sdes u_int nreq; 7792555Sdes}; 7892555Sdes 79147005Sdes#ifdef USE_PAM 80147005Sdesvoid 81147005Sdesremove_kbdint_device(const char *devname) 82147005Sdes{ 83147005Sdes int i, j; 84147005Sdes 85147005Sdes for (i = 0; devices[i] != NULL; i++) 86147005Sdes if (strcmp(devices[i]->name, devname) == 0) { 87147005Sdes for (j = i; devices[j] != NULL; j++) 88147005Sdes devices[j] = devices[j+1]; 89147005Sdes i--; 90147005Sdes } 91147005Sdes} 92147005Sdes#endif 93147005Sdes 9492555Sdesstatic KbdintAuthctxt * 9592555Sdeskbdint_alloc(const char *devs) 9692555Sdes{ 9792555Sdes KbdintAuthctxt *kbdintctxt; 9892555Sdes Buffer b; 9992555Sdes int i; 10092555Sdes 101147005Sdes#ifdef USE_PAM 102147005Sdes if (!options.use_pam) 103147005Sdes remove_kbdint_device("pam"); 104147005Sdes#endif 105147005Sdes 10692555Sdes kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 10792555Sdes if (strcmp(devs, "") == 0) { 10892555Sdes buffer_init(&b); 10992555Sdes for (i = 0; devices[i]; i++) { 11092555Sdes if (buffer_len(&b) > 0) 11192555Sdes buffer_append(&b, ",", 1); 11292555Sdes buffer_append(&b, devices[i]->name, 11392555Sdes strlen(devices[i]->name)); 11492555Sdes } 11592555Sdes buffer_append(&b, "\0", 1); 11692555Sdes kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 11792555Sdes buffer_free(&b); 11892555Sdes } else { 11992555Sdes kbdintctxt->devices = xstrdup(devs); 12092555Sdes } 12192555Sdes debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 12292555Sdes kbdintctxt->ctxt = NULL; 12392555Sdes kbdintctxt->device = NULL; 12499063Sdes kbdintctxt->nreq = 0; 12592555Sdes 12692555Sdes return kbdintctxt; 12792555Sdes} 12892555Sdesstatic void 12992555Sdeskbdint_reset_device(KbdintAuthctxt *kbdintctxt) 13092555Sdes{ 13192555Sdes if (kbdintctxt->ctxt) { 13292555Sdes kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 13392555Sdes kbdintctxt->ctxt = NULL; 13492555Sdes } 13592555Sdes kbdintctxt->device = NULL; 13692555Sdes} 13792555Sdesstatic void 13892555Sdeskbdint_free(KbdintAuthctxt *kbdintctxt) 13992555Sdes{ 14092555Sdes if (kbdintctxt->device) 14192555Sdes kbdint_reset_device(kbdintctxt); 14292555Sdes if (kbdintctxt->devices) { 14392555Sdes xfree(kbdintctxt->devices); 14492555Sdes kbdintctxt->devices = NULL; 14592555Sdes } 14692555Sdes xfree(kbdintctxt); 14792555Sdes} 14892555Sdes/* get next device */ 14992555Sdesstatic int 15092555Sdeskbdint_next_device(KbdintAuthctxt *kbdintctxt) 15192555Sdes{ 15292555Sdes size_t len; 15392555Sdes char *t; 15492555Sdes int i; 15592555Sdes 15692555Sdes if (kbdintctxt->device) 15792555Sdes kbdint_reset_device(kbdintctxt); 15892555Sdes do { 15992555Sdes len = kbdintctxt->devices ? 16092555Sdes strcspn(kbdintctxt->devices, ",") : 0; 16192555Sdes 16292555Sdes if (len == 0) 16392555Sdes break; 16492555Sdes for (i = 0; devices[i]; i++) 16592555Sdes if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 16692555Sdes kbdintctxt->device = devices[i]; 16792555Sdes t = kbdintctxt->devices; 16892555Sdes kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 16992555Sdes xfree(t); 17092555Sdes debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 171149753Sdes kbdintctxt->devices : "<empty>"); 17292555Sdes } while (kbdintctxt->devices && !kbdintctxt->device); 17392555Sdes 17492555Sdes return kbdintctxt->device ? 1 : 0; 17592555Sdes} 17692555Sdes 17776259Sgreen/* 17892555Sdes * try challenge-response, set authctxt->postponed if we have to 17976259Sgreen * wait for the response. 18076259Sgreen */ 18176259Sgreenint 18276259Sgreenauth2_challenge(Authctxt *authctxt, char *devs) 18376259Sgreen{ 18492555Sdes debug("auth2_challenge: user=%s devs=%s", 18592555Sdes authctxt->user ? authctxt->user : "<nouser>", 18692555Sdes devs ? devs : "<no devs>"); 18776259Sgreen 18892555Sdes if (authctxt->user == NULL || !devs) 18976259Sgreen return 0; 19092555Sdes if (authctxt->kbdintctxt == NULL) 19192555Sdes authctxt->kbdintctxt = kbdint_alloc(devs); 19292555Sdes return auth2_challenge_start(authctxt); 19392555Sdes} 19492555Sdes 19592555Sdes/* unregister kbd-int callbacks and context */ 19692555Sdesvoid 19792555Sdesauth2_challenge_stop(Authctxt *authctxt) 19892555Sdes{ 19992555Sdes /* unregister callback */ 20092555Sdes dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 20192555Sdes if (authctxt->kbdintctxt != NULL) { 20292555Sdes kbdint_free(authctxt->kbdintctxt); 20392555Sdes authctxt->kbdintctxt = NULL; 20492555Sdes } 20592555Sdes} 20692555Sdes 20792555Sdes/* side effect: sets authctxt->postponed if a reply was sent*/ 20892555Sdesstatic int 20992555Sdesauth2_challenge_start(Authctxt *authctxt) 21092555Sdes{ 21192555Sdes KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; 21292555Sdes 21392555Sdes debug2("auth2_challenge_start: devices %s", 21492555Sdes kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 21592555Sdes 21692555Sdes if (kbdint_next_device(kbdintctxt) == 0) { 21792555Sdes auth2_challenge_stop(authctxt); 21876259Sgreen return 0; 21992555Sdes } 22092555Sdes debug("auth2_challenge_start: trying authentication method '%s'", 22192555Sdes kbdintctxt->device->name); 22292555Sdes 22392555Sdes if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 22492555Sdes auth2_challenge_stop(authctxt); 22592555Sdes return 0; 22692555Sdes } 22792555Sdes if (send_userauth_info_request(authctxt) == 0) { 22892555Sdes auth2_challenge_stop(authctxt); 22992555Sdes return 0; 23092555Sdes } 23176259Sgreen dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 23276259Sgreen &input_userauth_info_response); 23392555Sdes 23476259Sgreen authctxt->postponed = 1; 23576259Sgreen return 0; 23676259Sgreen} 23776259Sgreen 23892555Sdesstatic int 23992555Sdessend_userauth_info_request(Authctxt *authctxt) 24076259Sgreen{ 24192555Sdes KbdintAuthctxt *kbdintctxt; 24292555Sdes char *name, *instr, **prompts; 243149753Sdes u_int i, *echo_on; 24476259Sgreen 24592555Sdes kbdintctxt = authctxt->kbdintctxt; 24692555Sdes if (kbdintctxt->device->query(kbdintctxt->ctxt, 24799063Sdes &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 24892555Sdes return 0; 24992555Sdes 25076259Sgreen packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 25192555Sdes packet_put_cstring(name); 25292555Sdes packet_put_cstring(instr); 25398684Sdes packet_put_cstring(""); /* language not used */ 25499063Sdes packet_put_int(kbdintctxt->nreq); 25599063Sdes for (i = 0; i < kbdintctxt->nreq; i++) { 25692555Sdes packet_put_cstring(prompts[i]); 25792555Sdes packet_put_char(echo_on[i]); 25892555Sdes } 25976259Sgreen packet_send(); 26076259Sgreen packet_write_wait(); 26192555Sdes 26299063Sdes for (i = 0; i < kbdintctxt->nreq; i++) 26392555Sdes xfree(prompts[i]); 26492555Sdes xfree(prompts); 26592555Sdes xfree(echo_on); 26692555Sdes xfree(name); 26792555Sdes xfree(instr); 26892555Sdes return 1; 26976259Sgreen} 27076259Sgreen 27192555Sdesstatic void 27292555Sdesinput_userauth_info_response(int type, u_int32_t seq, void *ctxt) 27376259Sgreen{ 27476259Sgreen Authctxt *authctxt = ctxt; 27592555Sdes KbdintAuthctxt *kbdintctxt; 276149753Sdes int authenticated = 0, res, len; 277149753Sdes u_int i, nresp; 27892555Sdes char **response = NULL, *method; 27976259Sgreen 28076259Sgreen if (authctxt == NULL) 28176259Sgreen fatal("input_userauth_info_response: no authctxt"); 28292555Sdes kbdintctxt = authctxt->kbdintctxt; 28392555Sdes if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 28492555Sdes fatal("input_userauth_info_response: no kbdintctxt"); 28592555Sdes if (kbdintctxt->device == NULL) 28692555Sdes fatal("input_userauth_info_response: no device"); 28776259Sgreen 28876259Sgreen authctxt->postponed = 0; /* reset */ 28976259Sgreen nresp = packet_get_int(); 29099063Sdes if (nresp != kbdintctxt->nreq) 29199063Sdes fatal("input_userauth_info_response: wrong number of replies"); 29299063Sdes if (nresp > 100) 29399063Sdes fatal("input_userauth_info_response: too many replies"); 29492555Sdes if (nresp > 0) { 295106130Sdes response = xmalloc(nresp * sizeof(char *)); 29692555Sdes for (i = 0; i < nresp; i++) 29792555Sdes response[i] = packet_get_string(NULL); 29892555Sdes } 29992555Sdes packet_check_eom(); 30092555Sdes 301147005Sdes res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); 30292555Sdes 30392555Sdes for (i = 0; i < nresp; i++) { 30492555Sdes memset(response[i], 'r', strlen(response[i])); 30592555Sdes xfree(response[i]); 30692555Sdes } 30792555Sdes if (response) 30876259Sgreen xfree(response); 30992555Sdes 31092555Sdes switch (res) { 31192555Sdes case 0: 31292555Sdes /* Success! */ 313147005Sdes authenticated = authctxt->valid ? 1 : 0; 31492555Sdes break; 31592555Sdes case 1: 31692555Sdes /* Authentication needs further interaction */ 31792555Sdes if (send_userauth_info_request(authctxt) == 1) 31892555Sdes authctxt->postponed = 1; 31992555Sdes break; 32092555Sdes default: 32192555Sdes /* Failure! */ 32292555Sdes break; 32376259Sgreen } 32476259Sgreen 32592555Sdes len = strlen("keyboard-interactive") + 2 + 32692555Sdes strlen(kbdintctxt->device->name); 32792555Sdes method = xmalloc(len); 32892555Sdes snprintf(method, len, "keyboard-interactive/%s", 32992555Sdes kbdintctxt->device->name); 33092555Sdes 33192555Sdes if (!authctxt->postponed) { 33292555Sdes if (authenticated) { 33392555Sdes auth2_challenge_stop(authctxt); 33492555Sdes } else { 33592555Sdes /* start next device */ 33692555Sdes /* may set authctxt->postponed */ 33792555Sdes auth2_challenge_start(authctxt); 33892555Sdes } 33992555Sdes } 34076259Sgreen userauth_finish(authctxt, authenticated, method); 34192555Sdes xfree(method); 34276259Sgreen} 34398684Sdes 34498684Sdesvoid 34598684Sdesprivsep_challenge_enable(void) 34698684Sdes{ 347124211Sdes#if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY) 348124211Sdes int n = 0; 349124211Sdes#endif 35098684Sdes#ifdef BSD_AUTH 35198684Sdes extern KbdintDevice mm_bsdauth_device; 35298684Sdes#endif 35399052Sdes#ifdef USE_PAM 354124211Sdes extern KbdintDevice mm_sshpam_device; 35599052Sdes#endif 35698684Sdes#ifdef SKEY 35798684Sdes extern KbdintDevice mm_skey_device; 35898684Sdes#endif 35999052Sdes 36098684Sdes#ifdef BSD_AUTH 36199052Sdes devices[n++] = &mm_bsdauth_device; 36298684Sdes#else 36399052Sdes#ifdef USE_PAM 364124211Sdes devices[n++] = &mm_sshpam_device; 36599052Sdes#endif 36698684Sdes#ifdef SKEY 36799052Sdes devices[n++] = &mm_skey_device; 36898684Sdes#endif 36998684Sdes#endif 37098684Sdes} 371