auth2-chall.c revision 192595
133965Sjdp/* $OpenBSD: auth2-chall.c,v 1.34 2008/12/09 04:32:22 djm Exp $ */ 278828Sobrien/* 3218822Sdim * Copyright (c) 2001 Markus Friedl. All rights reserved. 433965Sjdp * Copyright (c) 2001 Per Allansson. All rights reserved. 533965Sjdp * 633965Sjdp * Redistribution and use in source and binary forms, with or without 733965Sjdp * modification, are permitted provided that the following conditions 8104834Sobrien * are met: 933965Sjdp * 1. Redistributions of source code must retain the above copyright 10104834Sobrien * notice, this list of conditions and the following disclaimer. 11104834Sobrien * 2. Redistributions in binary form must reproduce the above copyright 12104834Sobrien * notice, this list of conditions and the following disclaimer in the 13104834Sobrien * documentation and/or other materials provided with the distribution. 1433965Sjdp * 15104834Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16104834Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17104834Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18104834Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1933965Sjdp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20104834Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21104834Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22218822Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2333965Sjdp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24218822Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2533965Sjdp */ 2633965Sjdp 2733965Sjdp#include "includes.h" 28130561Sobrien 2933965Sjdp#include <sys/types.h> 3033965Sjdp 3133965Sjdp#include <stdarg.h> 3233965Sjdp#include <stdio.h> 3333965Sjdp#include <string.h> 3433965Sjdp 3533965Sjdp#include "xmalloc.h" 3633965Sjdp#include "ssh2.h" 3733965Sjdp#include "key.h" 3833965Sjdp#include "hostfile.h" 3933965Sjdp#include "auth.h" 40130561Sobrien#include "buffer.h" 41130561Sobrien#include "packet.h" 42130561Sobrien#include "dispatch.h" 43130561Sobrien#include "log.h" 4433965Sjdp#include "servconf.h" 4533965Sjdp 4633965Sjdp/* import */ 4733965Sjdpextern ServerOptions options; 4833965Sjdp 4933965Sjdpstatic int auth2_challenge_start(Authctxt *); 50130561Sobrienstatic int send_userauth_info_request(Authctxt *); 5133965Sjdpstatic void input_userauth_info_response(int, u_int32_t, void *); 5233965Sjdp 5333965Sjdp#ifdef BSD_AUTH 54130561Sobrienextern KbdintDevice bsdauth_device; 5533965Sjdp#else 5633965Sjdp#ifdef USE_PAM 5733965Sjdpextern KbdintDevice sshpam_device; 58130561Sobrien#endif 59130561Sobrien#ifdef SKEY 60130561Sobrienextern KbdintDevice skey_device; 6133965Sjdp#endif 6233965Sjdp#endif 6333965Sjdp 6489857SobrienKbdintDevice *devices[] = { 6533965Sjdp#ifdef BSD_AUTH 6633965Sjdp &bsdauth_device, 6733965Sjdp#else 6833965Sjdp#ifdef USE_PAM 6933965Sjdp &sshpam_device, 7033965Sjdp#endif 7133965Sjdp#ifdef SKEY 7233965Sjdp &skey_device, 73130561Sobrien#endif 74218822Sdim#endif 7589857Sobrien NULL 7689857Sobrien}; 7789857Sobrien 7889857Sobrientypedef struct KbdintAuthctxt KbdintAuthctxt; 79130561Sobrienstruct KbdintAuthctxt 80218822Sdim{ 8133965Sjdp char *devices; 82130561Sobrien void *ctxt; 8389857Sobrien KbdintDevice *device; 84130561Sobrien u_int nreq; 85130561Sobrien}; 8633965Sjdp 87130561Sobrien#ifdef USE_PAM 88130561Sobrienvoid 8933965Sjdpremove_kbdint_device(const char *devname) 90130561Sobrien{ 9133965Sjdp int i, j; 9233965Sjdp 9333965Sjdp for (i = 0; devices[i] != NULL; i++) 9433965Sjdp if (strcmp(devices[i]->name, devname) == 0) { 9533965Sjdp for (j = i; devices[j] != NULL; j++) 9633965Sjdp devices[j] = devices[j+1]; 9733965Sjdp i--; 98130561Sobrien } 9933965Sjdp} 10033965Sjdp#endif 10133965Sjdp 10233965Sjdpstatic KbdintAuthctxt * 10399461Sobrienkbdint_alloc(const char *devs) 10499461Sobrien{ 10533965Sjdp KbdintAuthctxt *kbdintctxt; 106218822Sdim Buffer b; 10733965Sjdp int i; 10833965Sjdp 10933965Sjdp#ifdef USE_PAM 11033965Sjdp if (!options.use_pam) 11133965Sjdp remove_kbdint_device("pam"); 11233965Sjdp#endif 11389857Sobrien 11489857Sobrien kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 11589857Sobrien if (strcmp(devs, "") == 0) { 116130561Sobrien buffer_init(&b); 11789857Sobrien for (i = 0; devices[i]; i++) { 118218822Sdim if (buffer_len(&b) > 0) 119218822Sdim buffer_append(&b, ",", 1); 120218822Sdim buffer_append(&b, devices[i]->name, 121218822Sdim strlen(devices[i]->name)); 122218822Sdim } 12389857Sobrien buffer_append(&b, "\0", 1); 12489857Sobrien kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 12589857Sobrien buffer_free(&b); 126218822Sdim } else { 127218822Sdim kbdintctxt->devices = xstrdup(devs); 128218822Sdim } 129218822Sdim debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 130218822Sdim kbdintctxt->ctxt = NULL; 131218822Sdim kbdintctxt->device = NULL; 132218822Sdim kbdintctxt->nreq = 0; 133218822Sdim 134218822Sdim return kbdintctxt; 135218822Sdim} 136218822Sdimstatic void 137218822Sdimkbdint_reset_device(KbdintAuthctxt *kbdintctxt) 138218822Sdim{ 139218822Sdim if (kbdintctxt->ctxt) { 140218822Sdim kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 141218822Sdim kbdintctxt->ctxt = NULL; 142218822Sdim } 143218822Sdim kbdintctxt->device = NULL; 144218822Sdim} 145218822Sdimstatic void 146218822Sdimkbdint_free(KbdintAuthctxt *kbdintctxt) 14733965Sjdp{ 14833965Sjdp if (kbdintctxt->device) 14933965Sjdp kbdint_reset_device(kbdintctxt); 15033965Sjdp if (kbdintctxt->devices) { 151218822Sdim xfree(kbdintctxt->devices); 152218822Sdim kbdintctxt->devices = NULL; 15333965Sjdp } 15433965Sjdp xfree(kbdintctxt); 15533965Sjdp} 15633965Sjdp/* get next device */ 157218822Sdimstatic int 15833965Sjdpkbdint_next_device(KbdintAuthctxt *kbdintctxt) 15933965Sjdp{ 160218822Sdim size_t len; 161218822Sdim char *t; 16233965Sjdp int i; 16333965Sjdp 164218822Sdim if (kbdintctxt->device) 165218822Sdim kbdint_reset_device(kbdintctxt); 166218822Sdim do { 167218822Sdim len = kbdintctxt->devices ? 168218822Sdim strcspn(kbdintctxt->devices, ",") : 0; 16933965Sjdp 17033965Sjdp if (len == 0) 17133965Sjdp break; 17233965Sjdp for (i = 0; devices[i]; i++) 173218822Sdim if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 174218822Sdim kbdintctxt->device = devices[i]; 17533965Sjdp t = kbdintctxt->devices; 176130561Sobrien kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 177130561Sobrien xfree(t); 17833965Sjdp debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 17933965Sjdp kbdintctxt->devices : "<empty>"); 18033965Sjdp } while (kbdintctxt->devices && !kbdintctxt->device); 181218822Sdim 18233965Sjdp return kbdintctxt->device ? 1 : 0; 18333965Sjdp} 18433965Sjdp 18533965Sjdp/* 18633965Sjdp * try challenge-response, set authctxt->postponed if we have to 18733965Sjdp * wait for the response. 18833965Sjdp */ 18933965Sjdpint 19033965Sjdpauth2_challenge(Authctxt *authctxt, char *devs) 19133965Sjdp{ 19233965Sjdp debug("auth2_challenge: user=%s devs=%s", 19389857Sobrien authctxt->user ? authctxt->user : "<nouser>", 19433965Sjdp devs ? devs : "<no devs>"); 19533965Sjdp 196218822Sdim if (authctxt->user == NULL || !devs) 197218822Sdim return 0; 198218822Sdim if (authctxt->kbdintctxt == NULL) 199218822Sdim authctxt->kbdintctxt = kbdint_alloc(devs); 200218822Sdim return auth2_challenge_start(authctxt); 201218822Sdim} 202218822Sdim 203218822Sdim/* unregister kbd-int callbacks and context */ 204218822Sdimvoid 205218822Sdimauth2_challenge_stop(Authctxt *authctxt) 206218822Sdim{ 207218822Sdim /* unregister callback */ 208218822Sdim dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 20933965Sjdp if (authctxt->kbdintctxt != NULL) { 210218822Sdim kbdint_free(authctxt->kbdintctxt); 21133965Sjdp authctxt->kbdintctxt = NULL; 21233965Sjdp } 213218822Sdim} 214218822Sdim 215218822Sdim/* side effect: sets authctxt->postponed if a reply was sent*/ 216218822Sdimstatic int 217218822Sdimauth2_challenge_start(Authctxt *authctxt) 218218822Sdim{ 219218822Sdim KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; 220218822Sdim 221218822Sdim debug2("auth2_challenge_start: devices %s", 222218822Sdim kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 223218822Sdim 22433965Sjdp if (kbdint_next_device(kbdintctxt) == 0) { 22589857Sobrien auth2_challenge_stop(authctxt); 22633965Sjdp return 0; 22733965Sjdp } 228218822Sdim debug("auth2_challenge_start: trying authentication method '%s'", 229218822Sdim kbdintctxt->device->name); 230218822Sdim 231218822Sdim if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 232218822Sdim auth2_challenge_stop(authctxt); 233218822Sdim return 0; 234223262Sbenl } 23533965Sjdp if (send_userauth_info_request(authctxt) == 0) { 23633965Sjdp auth2_challenge_stop(authctxt); 23733965Sjdp return 0; 23833965Sjdp } 239218822Sdim dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 240218822Sdim &input_userauth_info_response); 241218822Sdim 242218822Sdim authctxt->postponed = 1; 243218822Sdim return 0; 244218822Sdim} 245218822Sdim 246218822Sdimstatic int 247218822Sdimsend_userauth_info_request(Authctxt *authctxt) 248218822Sdim{ 249218822Sdim KbdintAuthctxt *kbdintctxt; 250218822Sdim char *name, *instr, **prompts; 251218822Sdim u_int i, *echo_on; 252218822Sdim 253218822Sdim kbdintctxt = authctxt->kbdintctxt; 254218822Sdim if (kbdintctxt->device->query(kbdintctxt->ctxt, 255218822Sdim &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 256218822Sdim return 0; 257218822Sdim 258218822Sdim packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 259218822Sdim packet_put_cstring(name); 260218822Sdim packet_put_cstring(instr); 261218822Sdim packet_put_cstring(""); /* language not used */ 262218822Sdim packet_put_int(kbdintctxt->nreq); 263218822Sdim for (i = 0; i < kbdintctxt->nreq; i++) { 26433965Sjdp packet_put_cstring(prompts[i]); 26533965Sjdp packet_put_char(echo_on[i]); 26633965Sjdp } 26733965Sjdp packet_send(); 26833965Sjdp packet_write_wait(); 26933965Sjdp 270104834Sobrien for (i = 0; i < kbdintctxt->nreq; i++) 27133965Sjdp xfree(prompts[i]); 27233965Sjdp xfree(prompts); 273130561Sobrien xfree(echo_on); 27433965Sjdp xfree(name); 27533965Sjdp xfree(instr); 276130561Sobrien return 1; 27733965Sjdp} 27833965Sjdp 279130561Sobrienstatic void 280130561Sobrieninput_userauth_info_response(int type, u_int32_t seq, void *ctxt) 281130561Sobrien{ 28233965Sjdp Authctxt *authctxt = ctxt; 283130561Sobrien KbdintAuthctxt *kbdintctxt; 284130561Sobrien int authenticated = 0, res; 285130561Sobrien u_int i, nresp; 286130561Sobrien char **response = NULL, *method; 287130561Sobrien 288130561Sobrien if (authctxt == NULL) 289130561Sobrien fatal("input_userauth_info_response: no authctxt"); 290130561Sobrien kbdintctxt = authctxt->kbdintctxt; 291130561Sobrien if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 29233965Sjdp fatal("input_userauth_info_response: no kbdintctxt"); 293130561Sobrien if (kbdintctxt->device == NULL) 294130561Sobrien fatal("input_userauth_info_response: no device"); 29533965Sjdp 29633965Sjdp authctxt->postponed = 0; /* reset */ 29733965Sjdp nresp = packet_get_int(); 298130561Sobrien if (nresp != kbdintctxt->nreq) 29933965Sjdp fatal("input_userauth_info_response: wrong number of replies"); 300218822Sdim if (nresp > 100) 301218822Sdim fatal("input_userauth_info_response: too many replies"); 30233965Sjdp if (nresp > 0) { 303218822Sdim response = xcalloc(nresp, sizeof(char *)); 30433965Sjdp for (i = 0; i < nresp; i++) 30533965Sjdp response[i] = packet_get_string(NULL); 306218822Sdim } 30733965Sjdp packet_check_eom(); 30833965Sjdp 309104834Sobrien res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); 31033965Sjdp 311218822Sdim for (i = 0; i < nresp; i++) { 31233965Sjdp memset(response[i], 'r', strlen(response[i])); 31333965Sjdp xfree(response[i]); 31433965Sjdp } 315104834Sobrien if (response) 31638889Sjdp xfree(response); 31738889Sjdp 318218822Sdim switch (res) { 319218822Sdim case 0: 320218822Sdim /* Success! */ 32138889Sjdp authenticated = authctxt->valid ? 1 : 0; 32238889Sjdp break; 32333965Sjdp case 1: 32433965Sjdp /* Authentication needs further interaction */ 325218822Sdim if (send_userauth_info_request(authctxt) == 1) 326218822Sdim authctxt->postponed = 1; 327218822Sdim break; 328218822Sdim default: 329218822Sdim /* Failure! */ 330218822Sdim break; 331218822Sdim } 332218822Sdim 333218822Sdim xasprintf(&method, "keyboard-interactive/%s", kbdintctxt->device->name); 334218822Sdim 335218822Sdim if (!authctxt->postponed) { 336218822Sdim if (authenticated) { 337218822Sdim auth2_challenge_stop(authctxt); 338218822Sdim } else { 339218822Sdim /* start next device */ 340218822Sdim /* may set authctxt->postponed */ 341218822Sdim auth2_challenge_start(authctxt); 342218822Sdim } 343218822Sdim } 344218822Sdim userauth_finish(authctxt, authenticated, method); 345218822Sdim xfree(method); 346218822Sdim} 347218822Sdim 348218822Sdimvoid 349218822Sdimprivsep_challenge_enable(void) 350218822Sdim{ 351218822Sdim#if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY) 352218822Sdim int n = 0; 353218822Sdim#endif 35433965Sjdp#ifdef BSD_AUTH 35589857Sobrien extern KbdintDevice mm_bsdauth_device; 35633965Sjdp#endif 35733965Sjdp#ifdef USE_PAM 35833965Sjdp extern KbdintDevice mm_sshpam_device; 359218822Sdim#endif 36033965Sjdp#ifdef SKEY 361218822Sdim extern KbdintDevice mm_skey_device; 36233965Sjdp#endif 36333965Sjdp 36433965Sjdp#ifdef BSD_AUTH 36589857Sobrien devices[n++] = &mm_bsdauth_device; 36633965Sjdp#else 36733965Sjdp#ifdef USE_PAM 36833965Sjdp devices[n++] = &mm_sshpam_device; 36933965Sjdp#endif 37033965Sjdp#ifdef SKEY 37133965Sjdp devices[n++] = &mm_skey_device; 37233965Sjdp#endif 37333965Sjdp#endif 374218822Sdim} 37533965Sjdp