auth2-chall.c revision 181110
166458Sdfr/* $OpenBSD: auth2-chall.c,v 1.31 2006/08/05 08:28:24 dtucker Exp $ */ 266458Sdfr/* 396912Smarcel * Copyright (c) 2001 Markus Friedl. All rights reserved. 496912Smarcel * Copyright (c) 2001 Per Allansson. All rights reserved. 5139790Simp * 666458Sdfr * Redistribution and use in source and binary forms, with or without 766458Sdfr * modification, are permitted provided that the following conditions 866458Sdfr * are met: 966458Sdfr * 1. Redistributions of source code must retain the above copyright 1066458Sdfr * notice, this list of conditions and the following disclaimer. 1166458Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1266458Sdfr * notice, this list of conditions and the following disclaimer in the 1366458Sdfr * documentation and/or other materials provided with the distribution. 1466458Sdfr * 1566458Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1666458Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1766458Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1866458Sdfr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1966458Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2066458Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2166458Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2266458Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2366458Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2466458Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2566458Sdfr */ 2666458Sdfr 2766458Sdfr#include "includes.h" 2866458Sdfr 2966458Sdfr#include <sys/types.h> 3066458Sdfr 3166458Sdfr#include <stdarg.h> 3266458Sdfr#include <stdio.h> 3366458Sdfr#include <string.h> 3466458Sdfr 3566458Sdfr#include "xmalloc.h" 3666458Sdfr#include "ssh2.h" 3766458Sdfr#include "key.h" 3866458Sdfr#include "hostfile.h" 3966458Sdfr#include "auth.h" 4066458Sdfr#include "buffer.h" 4166458Sdfr#include "packet.h" 4266458Sdfr#include "dispatch.h" 4366458Sdfr#include "log.h" 4496912Smarcel#include "servconf.h" 4566458Sdfr 4666458Sdfr/* import */ 47170033Salcextern ServerOptions options; 48170033Salc 4966458Sdfrstatic int auth2_challenge_start(Authctxt *); 50115084Smarcelstatic int send_userauth_info_request(Authctxt *); 5166458Sdfrstatic void input_userauth_info_response(int, u_int32_t, void *); 5266458Sdfr 5366458Sdfr#ifdef BSD_AUTH 5466458Sdfrextern KbdintDevice bsdauth_device; 5566458Sdfr#else 5666458Sdfr#ifdef USE_PAM 5766458Sdfrextern KbdintDevice sshpam_device; 5866458Sdfr#endif 5966458Sdfr#ifdef SKEY 6066458Sdfrextern KbdintDevice skey_device; 6166458Sdfr#endif 6266458Sdfr#endif 6366458Sdfr 6466458SdfrKbdintDevice *devices[] = { 6566458Sdfr#ifdef BSD_AUTH 6666458Sdfr &bsdauth_device, 6766458Sdfr#else 6892670Speter#ifdef USE_PAM 6966458Sdfr &sshpam_device, 7066458Sdfr#endif 7166458Sdfr#ifdef SKEY 7266458Sdfr &skey_device, 7366458Sdfr#endif 7466458Sdfr#endif 7566458Sdfr NULL 7666458Sdfr}; 7766458Sdfr 7866458Sdfrtypedef struct KbdintAuthctxt KbdintAuthctxt; 7966458Sdfrstruct KbdintAuthctxt 8066458Sdfr{ 8166458Sdfr char *devices; 8266458Sdfr void *ctxt; 8366458Sdfr KbdintDevice *device; 8466458Sdfr u_int nreq; 8566458Sdfr}; 8666458Sdfr 8766458Sdfr#ifdef USE_PAM 8866458Sdfrvoid 8966458Sdfrremove_kbdint_device(const char *devname) 9066458Sdfr{ 9166458Sdfr int i, j; 9266458Sdfr 9366458Sdfr for (i = 0; devices[i] != NULL; i++) 9466458Sdfr if (strcmp(devices[i]->name, devname) == 0) { 9566458Sdfr for (j = i; devices[j] != NULL; j++) 9666458Sdfr devices[j] = devices[j+1]; 9766458Sdfr i--; 9866458Sdfr } 9966458Sdfr} 10066458Sdfr#endif 10166458Sdfr 102150008Salcstatic KbdintAuthctxt * 10366458Sdfrkbdint_alloc(const char *devs) 10466458Sdfr{ 10566458Sdfr KbdintAuthctxt *kbdintctxt; 10666458Sdfr Buffer b; 10766458Sdfr int i; 10866458Sdfr 109106486Smarcel#ifdef USE_PAM 110106486Smarcel if (!options.use_pam) 111106486Smarcel remove_kbdint_device("pam"); 112106486Smarcel#endif 113106486Smarcel 114169291Salc kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 115169291Salc if (strcmp(devs, "") == 0) { 116169291Salc buffer_init(&b); 117169291Salc for (i = 0; devices[i]; i++) { 118169291Salc if (buffer_len(&b) > 0) 119170519Salc buffer_append(&b, ",", 1); 120170519Salc buffer_append(&b, devices[i]->name, 121170519Salc strlen(devices[i]->name)); 122170519Salc } 123170519Salc buffer_append(&b, "\0", 1); 124170519Salc kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 125172317Salc buffer_free(&b); 126170519Salc } else { 127170519Salc kbdintctxt->devices = xstrdup(devs); 128170519Salc } 129170519Salc debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 130172317Salc kbdintctxt->ctxt = NULL; 131172317Salc kbdintctxt->device = NULL; 132170519Salc kbdintctxt->nreq = 0; 133170519Salc 134170519Salc return kbdintctxt; 135170519Salc} 136170519Salcstatic void 137170519Salckbdint_reset_device(KbdintAuthctxt *kbdintctxt) 138170519Salc{ 139170519Salc if (kbdintctxt->ctxt) { 140170519Salc kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 141170519Salc kbdintctxt->ctxt = NULL; 142170519Salc } 143170519Salc kbdintctxt->device = NULL; 144170519Salc} 145170519Salcstatic void 146170519Salckbdint_free(KbdintAuthctxt *kbdintctxt) 147170519Salc{ 14896912Smarcel if (kbdintctxt->device) 14996912Smarcel kbdint_reset_device(kbdintctxt); 15096912Smarcel if (kbdintctxt->devices) { 15196912Smarcel xfree(kbdintctxt->devices); 15296912Smarcel kbdintctxt->devices = NULL; 15396912Smarcel } 15496912Smarcel xfree(kbdintctxt); 15596912Smarcel} 15696912Smarcel/* get next device */ 157119906Smarcelstatic int 158119906Smarcelkbdint_next_device(KbdintAuthctxt *kbdintctxt) 159119906Smarcel{ 160119906Smarcel size_t len; 161119906Smarcel char *t; 162119906Smarcel int i; 163119906Smarcel 164119906Smarcel if (kbdintctxt->device) 165119906Smarcel kbdint_reset_device(kbdintctxt); 166119906Smarcel do { 167121268Smarcel len = kbdintctxt->devices ? 168121268Smarcel strcspn(kbdintctxt->devices, ",") : 0; 169119906Smarcel 17066458Sdfr if (len == 0) 17166458Sdfr break; 17266458Sdfr for (i = 0; devices[i]; i++) 17366458Sdfr if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 17466458Sdfr kbdintctxt->device = devices[i]; 175115084Smarcel t = kbdintctxt->devices; 176115084Smarcel kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 177115084Smarcel xfree(t); 178115084Smarcel debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 17966458Sdfr kbdintctxt->devices : "<empty>"); 18066458Sdfr } while (kbdintctxt->devices && !kbdintctxt->device); 181115084Smarcel 18296912Smarcel return kbdintctxt->device ? 1 : 0; 18366458Sdfr} 18466458Sdfr 18566458Sdfr/* 18666458Sdfr * try challenge-response, set authctxt->postponed if we have to 18766458Sdfr * wait for the response. 18866458Sdfr */ 18966458Sdfrint 190168920Ssepotvinauth2_challenge(Authctxt *authctxt, char *devs) 191168920Ssepotvin{ 19266458Sdfr debug("auth2_challenge: user=%s devs=%s", 19366458Sdfr authctxt->user ? authctxt->user : "<nouser>", 19466458Sdfr devs ? devs : "<no devs>"); 19566458Sdfr 19666458Sdfr if (authctxt->user == NULL || !devs) 19766458Sdfr return 0; 19866458Sdfr if (authctxt->kbdintctxt == NULL) 19966458Sdfr authctxt->kbdintctxt = kbdint_alloc(devs); 20066458Sdfr return auth2_challenge_start(authctxt); 20166458Sdfr} 20266458Sdfr 20396912Smarcel/* unregister kbd-int callbacks and context */ 204void 205auth2_challenge_stop(Authctxt *authctxt) 206{ 207 /* unregister callback */ 208 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 209 if (authctxt->kbdintctxt != NULL) { 210 kbdint_free(authctxt->kbdintctxt); 211 authctxt->kbdintctxt = NULL; 212 } 213} 214 215/* side effect: sets authctxt->postponed if a reply was sent*/ 216static int 217auth2_challenge_start(Authctxt *authctxt) 218{ 219 KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; 220 221 debug2("auth2_challenge_start: devices %s", 222 kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 223 224 if (kbdint_next_device(kbdintctxt) == 0) { 225 auth2_challenge_stop(authctxt); 226 return 0; 227 } 228 debug("auth2_challenge_start: trying authentication method '%s'", 229 kbdintctxt->device->name); 230 231 if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 232 auth2_challenge_stop(authctxt); 233 return 0; 234 } 235 if (send_userauth_info_request(authctxt) == 0) { 236 auth2_challenge_stop(authctxt); 237 return 0; 238 } 239 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 240 &input_userauth_info_response); 241 242 authctxt->postponed = 1; 243 return 0; 244} 245 246static int 247send_userauth_info_request(Authctxt *authctxt) 248{ 249 KbdintAuthctxt *kbdintctxt; 250 char *name, *instr, **prompts; 251 u_int i, *echo_on; 252 253 kbdintctxt = authctxt->kbdintctxt; 254 if (kbdintctxt->device->query(kbdintctxt->ctxt, 255 &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 256 return 0; 257 258 packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 259 packet_put_cstring(name); 260 packet_put_cstring(instr); 261 packet_put_cstring(""); /* language not used */ 262 packet_put_int(kbdintctxt->nreq); 263 for (i = 0; i < kbdintctxt->nreq; i++) { 264 packet_put_cstring(prompts[i]); 265 packet_put_char(echo_on[i]); 266 } 267 packet_send(); 268 packet_write_wait(); 269 270 for (i = 0; i < kbdintctxt->nreq; i++) 271 xfree(prompts[i]); 272 xfree(prompts); 273 xfree(echo_on); 274 xfree(name); 275 xfree(instr); 276 return 1; 277} 278 279static void 280input_userauth_info_response(int type, u_int32_t seq, void *ctxt) 281{ 282 Authctxt *authctxt = ctxt; 283 KbdintAuthctxt *kbdintctxt; 284 int authenticated = 0, res, len; 285 u_int i, nresp; 286 char **response = NULL, *method; 287 288 if (authctxt == NULL) 289 fatal("input_userauth_info_response: no authctxt"); 290 kbdintctxt = authctxt->kbdintctxt; 291 if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 292 fatal("input_userauth_info_response: no kbdintctxt"); 293 if (kbdintctxt->device == NULL) 294 fatal("input_userauth_info_response: no device"); 295 296 authctxt->postponed = 0; /* reset */ 297 nresp = packet_get_int(); 298 if (nresp != kbdintctxt->nreq) 299 fatal("input_userauth_info_response: wrong number of replies"); 300 if (nresp > 100) 301 fatal("input_userauth_info_response: too many replies"); 302 if (nresp > 0) { 303 response = xcalloc(nresp, sizeof(char *)); 304 for (i = 0; i < nresp; i++) 305 response[i] = packet_get_string(NULL); 306 } 307 packet_check_eom(); 308 309 res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); 310 311 for (i = 0; i < nresp; i++) { 312 memset(response[i], 'r', strlen(response[i])); 313 xfree(response[i]); 314 } 315 if (response) 316 xfree(response); 317 318 switch (res) { 319 case 0: 320 /* Success! */ 321 authenticated = authctxt->valid ? 1 : 0; 322 break; 323 case 1: 324 /* Authentication needs further interaction */ 325 if (send_userauth_info_request(authctxt) == 1) 326 authctxt->postponed = 1; 327 break; 328 default: 329 /* Failure! */ 330 break; 331 } 332 333 len = strlen("keyboard-interactive") + 2 + 334 strlen(kbdintctxt->device->name); 335 method = xmalloc(len); 336 snprintf(method, len, "keyboard-interactive/%s", 337 kbdintctxt->device->name); 338 339 if (!authctxt->postponed) { 340 if (authenticated) { 341 auth2_challenge_stop(authctxt); 342 } else { 343 /* start next device */ 344 /* may set authctxt->postponed */ 345 auth2_challenge_start(authctxt); 346 } 347 } 348 userauth_finish(authctxt, authenticated, method); 349 xfree(method); 350} 351 352void 353privsep_challenge_enable(void) 354{ 355#if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY) 356 int n = 0; 357#endif 358#ifdef BSD_AUTH 359 extern KbdintDevice mm_bsdauth_device; 360#endif 361#ifdef USE_PAM 362 extern KbdintDevice mm_sshpam_device; 363#endif 364#ifdef SKEY 365 extern KbdintDevice mm_skey_device; 366#endif 367 368#ifdef BSD_AUTH 369 devices[n++] = &mm_bsdauth_device; 370#else 371#ifdef USE_PAM 372 devices[n++] = &mm_sshpam_device; 373#endif 374#ifdef SKEY 375 devices[n++] = &mm_skey_device; 376#endif 377#endif 378} 379