1 /* 2 * Copyright (c) 2001 Markus Friedl. All rights reserved. 3 * Copyright (c) 2001 Per Allansson. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 /* 26 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #include "includes.h" 31 RCSID("$OpenBSD: auth2-chall.c,v 1.20 2002/06/30 21:59:45 deraadt Exp $"); 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ssh2.h" 36 #include "auth.h" 37 #include "buffer.h" 38 #include "packet.h" 39 #include "xmalloc.h" 40 #include "dispatch.h" 41 #include "auth.h" 42 #include "log.h" 43 44 #ifndef lint 45 static void auth2_challenge_start(Authctxt *); 46 static int send_userauth_info_request(Authctxt *); 47 static void input_userauth_info_response(int, u_int32_t, void *); 48 49 #ifdef BSD_AUTH 50 extern KbdintDevice bsdauth_device; 51 #else 52 #ifdef SKEY 53 extern KbdintDevice skey_device; 54 #endif 55 #endif 56 57 KbdintDevice *devices[] = { 58 #ifdef BSD_AUTH 59 &bsdauth_device, 60 #else 61 #ifdef SKEY 62 &skey_device, 63 #endif 64 #endif 65 NULL 66 }; 67 68 typedef struct KbdintAuthctxt KbdintAuthctxt; 69 struct KbdintAuthctxt 70 { 71 char *devices; 72 void *ctxt; 73 KbdintDevice *device; 74 u_int nreq; 75 }; 76 77 static KbdintAuthctxt * 78 kbdint_alloc(const char *devs) 79 { 80 KbdintAuthctxt *kbdintctxt; 81 Buffer b; 82 int i; 83 84 kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 85 if (strcmp(devs, "") == 0) { 86 buffer_init(&b); 87 for (i = 0; devices[i]; i++) { 88 if (buffer_len(&b) > 0) 89 buffer_append(&b, ",", 1); 90 buffer_append(&b, devices[i]->name, 91 strlen(devices[i]->name)); 92 } 93 buffer_append(&b, "\0", 1); 94 kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 95 buffer_free(&b); 96 } else { 97 kbdintctxt->devices = xstrdup(devs); 98 } 99 debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 100 kbdintctxt->ctxt = NULL; 101 kbdintctxt->device = NULL; 102 kbdintctxt->nreq = 0; 103 104 return kbdintctxt; 105 } 106 static void 107 kbdint_reset_device(KbdintAuthctxt *kbdintctxt) 108 { 109 if (kbdintctxt->ctxt) { 110 kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 111 kbdintctxt->ctxt = NULL; 112 } 113 kbdintctxt->device = NULL; 114 } 115 static void 116 kbdint_free(KbdintAuthctxt *kbdintctxt) 117 { 118 if (kbdintctxt->device) 119 kbdint_reset_device(kbdintctxt); 120 if (kbdintctxt->devices) { 121 xfree(kbdintctxt->devices); 122 kbdintctxt->devices = NULL; 123 } 124 xfree(kbdintctxt); 125 } 126 /* get next device */ 127 static int 128 kbdint_next_device(KbdintAuthctxt *kbdintctxt) 129 { 130 size_t len; 131 char *t; 132 int i; 133 134 if (kbdintctxt->device) 135 kbdint_reset_device(kbdintctxt); 136 do { 137 len = kbdintctxt->devices ? 138 strcspn(kbdintctxt->devices, ",") : 0; 139 140 if (len == 0) 141 break; 142 for (i = 0; devices[i]; i++) 143 if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 144 kbdintctxt->device = devices[i]; 145 t = kbdintctxt->devices; 146 kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 147 xfree(t); 148 debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 149 kbdintctxt->devices : "<empty>"); 150 } while (kbdintctxt->devices && !kbdintctxt->device); 151 152 return kbdintctxt->device ? 1 : 0; 153 } 154 155 /* 156 * try challenge-response, set authctxt->method->postponed if we have to 157 * wait for the response. 158 */ 159 void 160 auth2_challenge(Authctxt *authctxt, char *devs) 161 { 162 debug("auth2_challenge: user=%s devs=%s", 163 authctxt->user ? authctxt->user : "<nouser>", 164 devs ? devs : "<no devs>"); 165 166 if (authctxt->user == NULL || !devs) 167 return; 168 if (authctxt->method->method_data != NULL) { 169 auth2_challenge_abandon(authctxt); 170 authctxt->method->abandoned = 0; 171 } 172 authctxt->method->method_data = (void *) kbdint_alloc(devs); 173 auth2_challenge_start(authctxt); 174 } 175 176 /* unregister kbd-int callbacks and context */ 177 static void 178 auth2_challenge_stop(Authctxt *authctxt) 179 { 180 /* unregister callback */ 181 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 182 if (authctxt->method->method_data != NULL) { 183 kbdint_free((KbdintAuthctxt *) authctxt->method->method_data); 184 authctxt->method->method_data = NULL; 185 } 186 } 187 188 void 189 auth2_challenge_abandon(Authctxt *authctxt) 190 { 191 auth2_challenge_stop(authctxt); 192 authctxt->method->abandoned = 1; 193 authctxt->method->postponed = 0; 194 authctxt->method->authenticated = 0; 195 authctxt->method->abandons++; 196 authctxt->method->attempts++; 197 } 198 199 /* side effect: sets authctxt->method->postponed if a reply was sent*/ 200 static void 201 auth2_challenge_start(Authctxt *authctxt) 202 { 203 KbdintAuthctxt *kbdintctxt = (KbdintAuthctxt *) 204 authctxt->method->method_data; 205 206 debug2("auth2_challenge_start: devices %s", 207 kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 208 209 if (kbdint_next_device(kbdintctxt) == 0) { 210 auth2_challenge_stop(authctxt); 211 return; 212 } 213 debug("auth2_challenge_start: trying authentication method '%s'", 214 kbdintctxt->device->name); 215 216 if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 217 auth2_challenge_stop(authctxt); 218 return; 219 } 220 if (send_userauth_info_request(authctxt) == 0) { 221 auth2_challenge_stop(authctxt); 222 return; 223 } 224 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 225 &input_userauth_info_response); 226 227 authctxt->method->postponed = 1; 228 } 229 230 static int 231 send_userauth_info_request(Authctxt *authctxt) 232 { 233 KbdintAuthctxt *kbdintctxt; 234 char *name, *instr, **prompts; 235 int i; 236 u_int *echo_on; 237 238 kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; 239 if (kbdintctxt->device->query(kbdintctxt->ctxt, 240 &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 241 return 0; 242 243 packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 244 packet_put_cstring(name); 245 packet_put_cstring(instr); 246 packet_put_cstring(""); /* language not used */ 247 packet_put_int(kbdintctxt->nreq); 248 for (i = 0; i < kbdintctxt->nreq; i++) { 249 packet_put_cstring(prompts[i]); 250 packet_put_char(echo_on[i]); 251 } 252 packet_send(); 253 packet_write_wait(); 254 255 for (i = 0; i < kbdintctxt->nreq; i++) 256 xfree(prompts[i]); 257 xfree(prompts); 258 xfree(echo_on); 259 xfree(name); 260 xfree(instr); 261 return 1; 262 } 263 264 static void 265 input_userauth_info_response(int type, u_int32_t seq, void *ctxt) 266 { 267 Authctxt *authctxt = ctxt; 268 KbdintAuthctxt *kbdintctxt; 269 int i, res, len; 270 u_int nresp; 271 char **response = NULL, *method; 272 273 if (authctxt == NULL) 274 fatal("input_userauth_info_response: no authctxt"); 275 kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; 276 if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 277 fatal("input_userauth_info_response: no kbdintctxt"); 278 if (kbdintctxt->device == NULL) 279 fatal("input_userauth_info_response: no device"); 280 281 nresp = packet_get_int(); 282 if (nresp != kbdintctxt->nreq) 283 fatal("input_userauth_info_response: wrong number of replies"); 284 if (nresp > 100) 285 fatal("input_userauth_info_response: too many replies"); 286 if (nresp > 0) { 287 response = xmalloc(nresp * sizeof(char *)); 288 for (i = 0; i < nresp; i++) 289 response[i] = packet_get_string(NULL); 290 } 291 packet_check_eom(); 292 293 if (authctxt->valid) { 294 res = kbdintctxt->device->respond(kbdintctxt->ctxt, 295 nresp, response); 296 } else { 297 res = -1; 298 } 299 300 for (i = 0; i < nresp; i++) { 301 memset(response[i], 'r', strlen(response[i])); 302 xfree(response[i]); 303 } 304 if (response) 305 xfree(response); 306 307 authctxt->method->postponed = 0; /* reset */ 308 switch (res) { 309 case 0: 310 /* Success! */ 311 authctxt->method->authenticated = 1; 312 break; 313 case 1: 314 /* Authentication needs further interaction */ 315 if (send_userauth_info_request(authctxt) == 1) { 316 authctxt->method->postponed = 1; 317 } 318 break; 319 default: 320 /* Failure! */ 321 break; 322 } 323 324 325 len = strlen("keyboard-interactive") + 2 + 326 strlen(kbdintctxt->device->name); 327 method = xmalloc(len); 328 snprintf(method, len, "keyboard-interactive/%s", 329 kbdintctxt->device->name); 330 331 if (authctxt->method->authenticated || authctxt->method->abandoned) { 332 auth2_challenge_stop(authctxt); 333 } else { 334 /* start next device */ 335 /* may set authctxt->method->postponed */ 336 auth2_challenge_start(authctxt); 337 } 338 userauth_finish(authctxt, method); 339 xfree(method); 340 } 341 342 void 343 privsep_challenge_enable(void) 344 { 345 #ifdef BSD_AUTH 346 extern KbdintDevice mm_bsdauth_device; 347 #endif 348 #ifdef SKEY 349 extern KbdintDevice mm_skey_device; 350 #endif 351 /* As long as SSHv1 has devices[0] hard coded this is fine */ 352 #ifdef BSD_AUTH 353 devices[0] = &mm_bsdauth_device; 354 #else 355 #ifdef SKEY 356 devices[0] = &mm_skey_device; 357 #endif 358 #endif 359 } 360 #endif /* lint */ 361