auth2-chall.c revision 99063
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" 2699063SdesRCSID("$OpenBSD: auth2-chall.c,v 1.19 2002/06/26 13:55:37 markus Exp $"); 2799052SdesRCSID("$FreeBSD: head/crypto/openssh/auth2-chall.c 99063 2002-06-29 11:48:59Z 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" 3592555Sdes#include "auth.h" 3676259Sgreen#include "log.h" 3776259Sgreen 3892555Sdesstatic int auth2_challenge_start(Authctxt *); 3992555Sdesstatic int send_userauth_info_request(Authctxt *); 4092555Sdesstatic void input_userauth_info_response(int, u_int32_t, void *); 4176259Sgreen 4292555Sdes#ifdef BSD_AUTH 4392555Sdesextern KbdintDevice bsdauth_device; 4498941Sdes#else 4599052Sdes#ifdef USE_PAM 4699052Sdesextern KbdintDevice pam_device; 4799052Sdes#endif 4898941Sdes#ifdef SKEY 4992555Sdesextern KbdintDevice skey_device; 5092555Sdes#endif 5198941Sdes#endif 5292555Sdes 5392555SdesKbdintDevice *devices[] = { 5492555Sdes#ifdef BSD_AUTH 5592555Sdes &bsdauth_device, 5698941Sdes#else 5799052Sdes#ifdef USE_PAM 5899052Sdes &pam_device, 5999052Sdes#endif 6098941Sdes#ifdef SKEY 6192555Sdes &skey_device, 6292555Sdes#endif 6398941Sdes#endif 6492555Sdes NULL 6592555Sdes}; 6692555Sdes 6792555Sdestypedef struct KbdintAuthctxt KbdintAuthctxt; 6892555Sdesstruct KbdintAuthctxt 6992555Sdes{ 7092555Sdes char *devices; 7192555Sdes void *ctxt; 7292555Sdes KbdintDevice *device; 7399063Sdes u_int nreq; 7492555Sdes}; 7592555Sdes 7692555Sdesstatic KbdintAuthctxt * 7792555Sdeskbdint_alloc(const char *devs) 7892555Sdes{ 7992555Sdes KbdintAuthctxt *kbdintctxt; 8092555Sdes Buffer b; 8192555Sdes int i; 8292555Sdes 8392555Sdes kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 8492555Sdes if (strcmp(devs, "") == 0) { 8592555Sdes buffer_init(&b); 8692555Sdes for (i = 0; devices[i]; i++) { 8792555Sdes if (buffer_len(&b) > 0) 8892555Sdes buffer_append(&b, ",", 1); 8992555Sdes buffer_append(&b, devices[i]->name, 9092555Sdes strlen(devices[i]->name)); 9192555Sdes } 9292555Sdes buffer_append(&b, "\0", 1); 9392555Sdes kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 9492555Sdes buffer_free(&b); 9592555Sdes } else { 9692555Sdes kbdintctxt->devices = xstrdup(devs); 9792555Sdes } 9892555Sdes debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 9992555Sdes kbdintctxt->ctxt = NULL; 10092555Sdes kbdintctxt->device = NULL; 10199063Sdes kbdintctxt->nreq = 0; 10292555Sdes 10392555Sdes return kbdintctxt; 10492555Sdes} 10592555Sdesstatic void 10692555Sdeskbdint_reset_device(KbdintAuthctxt *kbdintctxt) 10792555Sdes{ 10892555Sdes if (kbdintctxt->ctxt) { 10992555Sdes kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 11092555Sdes kbdintctxt->ctxt = NULL; 11192555Sdes } 11292555Sdes kbdintctxt->device = NULL; 11392555Sdes} 11492555Sdesstatic void 11592555Sdeskbdint_free(KbdintAuthctxt *kbdintctxt) 11692555Sdes{ 11792555Sdes if (kbdintctxt->device) 11892555Sdes kbdint_reset_device(kbdintctxt); 11992555Sdes if (kbdintctxt->devices) { 12092555Sdes xfree(kbdintctxt->devices); 12192555Sdes kbdintctxt->devices = NULL; 12292555Sdes } 12392555Sdes xfree(kbdintctxt); 12492555Sdes} 12592555Sdes/* get next device */ 12692555Sdesstatic int 12792555Sdeskbdint_next_device(KbdintAuthctxt *kbdintctxt) 12892555Sdes{ 12992555Sdes size_t len; 13092555Sdes char *t; 13192555Sdes int i; 13292555Sdes 13392555Sdes if (kbdintctxt->device) 13492555Sdes kbdint_reset_device(kbdintctxt); 13592555Sdes do { 13692555Sdes len = kbdintctxt->devices ? 13792555Sdes strcspn(kbdintctxt->devices, ",") : 0; 13892555Sdes 13992555Sdes if (len == 0) 14092555Sdes break; 14192555Sdes for (i = 0; devices[i]; i++) 14292555Sdes if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 14392555Sdes kbdintctxt->device = devices[i]; 14492555Sdes t = kbdintctxt->devices; 14592555Sdes kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 14692555Sdes xfree(t); 14792555Sdes debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 14892555Sdes kbdintctxt->devices : "<empty>"); 14992555Sdes } while (kbdintctxt->devices && !kbdintctxt->device); 15092555Sdes 15192555Sdes return kbdintctxt->device ? 1 : 0; 15292555Sdes} 15392555Sdes 15476259Sgreen/* 15592555Sdes * try challenge-response, set authctxt->postponed if we have to 15676259Sgreen * wait for the response. 15776259Sgreen */ 15876259Sgreenint 15976259Sgreenauth2_challenge(Authctxt *authctxt, char *devs) 16076259Sgreen{ 16192555Sdes debug("auth2_challenge: user=%s devs=%s", 16292555Sdes authctxt->user ? authctxt->user : "<nouser>", 16392555Sdes devs ? devs : "<no devs>"); 16476259Sgreen 16592555Sdes if (authctxt->user == NULL || !devs) 16676259Sgreen return 0; 16792555Sdes if (authctxt->kbdintctxt == NULL) 16892555Sdes authctxt->kbdintctxt = kbdint_alloc(devs); 16992555Sdes return auth2_challenge_start(authctxt); 17092555Sdes} 17192555Sdes 17292555Sdes/* unregister kbd-int callbacks and context */ 17392555Sdesvoid 17492555Sdesauth2_challenge_stop(Authctxt *authctxt) 17592555Sdes{ 17692555Sdes /* unregister callback */ 17792555Sdes dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 17892555Sdes if (authctxt->kbdintctxt != NULL) { 17992555Sdes kbdint_free(authctxt->kbdintctxt); 18092555Sdes authctxt->kbdintctxt = NULL; 18192555Sdes } 18292555Sdes} 18392555Sdes 18492555Sdes/* side effect: sets authctxt->postponed if a reply was sent*/ 18592555Sdesstatic int 18692555Sdesauth2_challenge_start(Authctxt *authctxt) 18792555Sdes{ 18892555Sdes KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; 18992555Sdes 19092555Sdes debug2("auth2_challenge_start: devices %s", 19192555Sdes kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 19292555Sdes 19392555Sdes if (kbdint_next_device(kbdintctxt) == 0) { 19492555Sdes auth2_challenge_stop(authctxt); 19576259Sgreen return 0; 19692555Sdes } 19792555Sdes debug("auth2_challenge_start: trying authentication method '%s'", 19892555Sdes kbdintctxt->device->name); 19992555Sdes 20092555Sdes if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 20192555Sdes auth2_challenge_stop(authctxt); 20292555Sdes return 0; 20392555Sdes } 20492555Sdes if (send_userauth_info_request(authctxt) == 0) { 20592555Sdes auth2_challenge_stop(authctxt); 20692555Sdes return 0; 20792555Sdes } 20876259Sgreen dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 20976259Sgreen &input_userauth_info_response); 21092555Sdes 21176259Sgreen authctxt->postponed = 1; 21276259Sgreen return 0; 21376259Sgreen} 21476259Sgreen 21592555Sdesstatic int 21692555Sdessend_userauth_info_request(Authctxt *authctxt) 21776259Sgreen{ 21892555Sdes KbdintAuthctxt *kbdintctxt; 21992555Sdes char *name, *instr, **prompts; 22092555Sdes int i; 22199063Sdes u_int *echo_on; 22276259Sgreen 22392555Sdes kbdintctxt = authctxt->kbdintctxt; 22492555Sdes if (kbdintctxt->device->query(kbdintctxt->ctxt, 22599063Sdes &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 22692555Sdes return 0; 22792555Sdes 22876259Sgreen packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 22992555Sdes packet_put_cstring(name); 23092555Sdes packet_put_cstring(instr); 23198684Sdes packet_put_cstring(""); /* language not used */ 23299063Sdes packet_put_int(kbdintctxt->nreq); 23399063Sdes for (i = 0; i < kbdintctxt->nreq; i++) { 23492555Sdes packet_put_cstring(prompts[i]); 23592555Sdes packet_put_char(echo_on[i]); 23692555Sdes } 23776259Sgreen packet_send(); 23876259Sgreen packet_write_wait(); 23992555Sdes 24099063Sdes for (i = 0; i < kbdintctxt->nreq; i++) 24192555Sdes xfree(prompts[i]); 24292555Sdes xfree(prompts); 24392555Sdes xfree(echo_on); 24492555Sdes xfree(name); 24592555Sdes xfree(instr); 24692555Sdes return 1; 24776259Sgreen} 24876259Sgreen 24992555Sdesstatic void 25092555Sdesinput_userauth_info_response(int type, u_int32_t seq, void *ctxt) 25176259Sgreen{ 25276259Sgreen Authctxt *authctxt = ctxt; 25392555Sdes KbdintAuthctxt *kbdintctxt; 25492555Sdes int i, authenticated = 0, res, len; 25592555Sdes u_int nresp; 25692555Sdes char **response = NULL, *method; 25776259Sgreen 25876259Sgreen if (authctxt == NULL) 25976259Sgreen fatal("input_userauth_info_response: no authctxt"); 26092555Sdes kbdintctxt = authctxt->kbdintctxt; 26192555Sdes if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 26292555Sdes fatal("input_userauth_info_response: no kbdintctxt"); 26392555Sdes if (kbdintctxt->device == NULL) 26492555Sdes fatal("input_userauth_info_response: no device"); 26576259Sgreen 26676259Sgreen authctxt->postponed = 0; /* reset */ 26776259Sgreen nresp = packet_get_int(); 26899063Sdes if (nresp != kbdintctxt->nreq) 26999063Sdes fatal("input_userauth_info_response: wrong number of replies"); 27099063Sdes if (nresp > 100) 27199063Sdes fatal("input_userauth_info_response: too many replies"); 27292555Sdes if (nresp > 0) { 27392555Sdes response = xmalloc(nresp * sizeof(char*)); 27492555Sdes for (i = 0; i < nresp; i++) 27592555Sdes response[i] = packet_get_string(NULL); 27692555Sdes } 27792555Sdes packet_check_eom(); 27892555Sdes 27992555Sdes if (authctxt->valid) { 28092555Sdes res = kbdintctxt->device->respond(kbdintctxt->ctxt, 28192555Sdes nresp, response); 28292555Sdes } else { 28392555Sdes res = -1; 28492555Sdes } 28592555Sdes 28692555Sdes for (i = 0; i < nresp; i++) { 28792555Sdes memset(response[i], 'r', strlen(response[i])); 28892555Sdes xfree(response[i]); 28992555Sdes } 29092555Sdes if (response) 29176259Sgreen xfree(response); 29292555Sdes 29392555Sdes switch (res) { 29492555Sdes case 0: 29592555Sdes /* Success! */ 29692555Sdes authenticated = 1; 29792555Sdes break; 29892555Sdes case 1: 29992555Sdes /* Authentication needs further interaction */ 30092555Sdes if (send_userauth_info_request(authctxt) == 1) 30192555Sdes authctxt->postponed = 1; 30292555Sdes break; 30392555Sdes default: 30492555Sdes /* Failure! */ 30592555Sdes break; 30676259Sgreen } 30776259Sgreen 30892555Sdes len = strlen("keyboard-interactive") + 2 + 30992555Sdes strlen(kbdintctxt->device->name); 31092555Sdes method = xmalloc(len); 31192555Sdes snprintf(method, len, "keyboard-interactive/%s", 31292555Sdes kbdintctxt->device->name); 31392555Sdes 31492555Sdes if (!authctxt->postponed) { 31592555Sdes if (authenticated) { 31692555Sdes auth2_challenge_stop(authctxt); 31792555Sdes } else { 31892555Sdes /* start next device */ 31992555Sdes /* may set authctxt->postponed */ 32092555Sdes auth2_challenge_start(authctxt); 32192555Sdes } 32292555Sdes } 32376259Sgreen userauth_finish(authctxt, authenticated, method); 32492555Sdes xfree(method); 32576259Sgreen} 32698684Sdes 32798684Sdesvoid 32898684Sdesprivsep_challenge_enable(void) 32998684Sdes{ 33098684Sdes#ifdef BSD_AUTH 33198684Sdes extern KbdintDevice mm_bsdauth_device; 33298684Sdes#endif 33399052Sdes#ifdef USE_PAM 33499052Sdes extern KbdintDevice mm_pam_device; 33599052Sdes#endif 33698684Sdes#ifdef SKEY 33798684Sdes extern KbdintDevice mm_skey_device; 33898684Sdes#endif 33999052Sdes int n = 0; 34099052Sdes 34198684Sdes#ifdef BSD_AUTH 34299052Sdes devices[n++] = &mm_bsdauth_device; 34398684Sdes#else 34499052Sdes#ifdef USE_PAM 34599052Sdes devices[n++] = &mm_pam_device; 34699052Sdes#endif 34798684Sdes#ifdef SKEY 34899052Sdes devices[n++] = &mm_skey_device; 34998684Sdes#endif 35098684Sdes#endif 35198684Sdes} 352