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 2009 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 #include "ssh2.h" 34 #include "auth.h" 35 #include "buffer.h" 36 #include "packet.h" 37 #include "xmalloc.h" 38 #include "dispatch.h" 39 #include "auth.h" 40 #include "log.h" 41 42 #ifndef lint 43 static void auth2_challenge_start(Authctxt *); 44 static int send_userauth_info_request(Authctxt *); 45 static void input_userauth_info_response(int, u_int32_t, void *); 46 47 #ifdef BSD_AUTH 48 extern KbdintDevice bsdauth_device; 49 #else 50 #ifdef SKEY 51 extern KbdintDevice skey_device; 52 #endif 53 #endif 54 55 KbdintDevice *devices[] = { 56 #ifdef BSD_AUTH 57 &bsdauth_device, 58 #else 59 #ifdef SKEY 60 &skey_device, 61 #endif 62 #endif 63 NULL 64 }; 65 66 typedef struct KbdintAuthctxt KbdintAuthctxt; 67 struct KbdintAuthctxt 68 { 69 char *devices; 70 void *ctxt; 71 KbdintDevice *device; 72 u_int nreq; 73 }; 74 75 static KbdintAuthctxt * 76 kbdint_alloc(const char *devs) 77 { 78 KbdintAuthctxt *kbdintctxt; 79 Buffer b; 80 int i; 81 82 kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 83 if (strcmp(devs, "") == 0) { 84 buffer_init(&b); 85 for (i = 0; devices[i]; i++) { 86 if (buffer_len(&b) > 0) 87 buffer_append(&b, ",", 1); 88 buffer_append(&b, devices[i]->name, 89 strlen(devices[i]->name)); 90 } 91 buffer_append(&b, "\0", 1); 92 kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 93 buffer_free(&b); 94 } else { 95 kbdintctxt->devices = xstrdup(devs); 96 } 97 debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 98 kbdintctxt->ctxt = NULL; 99 kbdintctxt->device = NULL; 100 kbdintctxt->nreq = 0; 101 102 return kbdintctxt; 103 } 104 static void 105 kbdint_reset_device(KbdintAuthctxt *kbdintctxt) 106 { 107 if (kbdintctxt->ctxt) { 108 kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 109 kbdintctxt->ctxt = NULL; 110 } 111 kbdintctxt->device = NULL; 112 } 113 static void 114 kbdint_free(KbdintAuthctxt *kbdintctxt) 115 { 116 if (kbdintctxt->device) 117 kbdint_reset_device(kbdintctxt); 118 if (kbdintctxt->devices) { 119 xfree(kbdintctxt->devices); 120 kbdintctxt->devices = NULL; 121 } 122 xfree(kbdintctxt); 123 } 124 /* get next device */ 125 static int 126 kbdint_next_device(KbdintAuthctxt *kbdintctxt) 127 { 128 size_t len; 129 char *t; 130 int i; 131 132 if (kbdintctxt->device) 133 kbdint_reset_device(kbdintctxt); 134 do { 135 len = kbdintctxt->devices ? 136 strcspn(kbdintctxt->devices, ",") : 0; 137 138 if (len == 0) 139 break; 140 for (i = 0; devices[i]; i++) 141 if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 142 kbdintctxt->device = devices[i]; 143 t = kbdintctxt->devices; 144 kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 145 xfree(t); 146 debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 147 kbdintctxt->devices : "<empty>"); 148 } while (kbdintctxt->devices && !kbdintctxt->device); 149 150 return kbdintctxt->device ? 1 : 0; 151 } 152 153 /* 154 * try challenge-response, set authctxt->method->postponed if we have to 155 * wait for the response. 156 */ 157 void 158 auth2_challenge(Authctxt *authctxt, char *devs) 159 { 160 debug("auth2_challenge: user=%s devs=%s", 161 authctxt->user ? authctxt->user : "<nouser>", 162 devs ? devs : "<no devs>"); 163 164 if (authctxt->user == NULL || !devs) 165 return; 166 if (authctxt->method->method_data != NULL) { 167 auth2_challenge_abandon(authctxt); 168 authctxt->method->abandoned = 0; 169 } 170 authctxt->method->method_data = (void *) kbdint_alloc(devs); 171 auth2_challenge_start(authctxt); 172 } 173 174 /* unregister kbd-int callbacks and context */ 175 static void 176 auth2_challenge_stop(Authctxt *authctxt) 177 { 178 /* unregister callback */ 179 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 180 if (authctxt->method->method_data != NULL) { 181 kbdint_free((KbdintAuthctxt *) authctxt->method->method_data); 182 authctxt->method->method_data = NULL; 183 } 184 } 185 186 void 187 auth2_challenge_abandon(Authctxt *authctxt) 188 { 189 auth2_challenge_stop(authctxt); 190 authctxt->method->abandoned = 1; 191 authctxt->method->postponed = 0; 192 authctxt->method->authenticated = 0; 193 authctxt->method->abandons++; 194 authctxt->method->attempts++; 195 } 196 197 /* side effect: sets authctxt->method->postponed if a reply was sent*/ 198 static void 199 auth2_challenge_start(Authctxt *authctxt) 200 { 201 KbdintAuthctxt *kbdintctxt = (KbdintAuthctxt *) 202 authctxt->method->method_data; 203 204 debug2("auth2_challenge_start: devices %s", 205 kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 206 207 if (kbdint_next_device(kbdintctxt) == 0) { 208 auth2_challenge_stop(authctxt); 209 return; 210 } 211 debug("auth2_challenge_start: trying authentication method '%s'", 212 kbdintctxt->device->name); 213 214 if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 215 auth2_challenge_stop(authctxt); 216 return; 217 } 218 if (send_userauth_info_request(authctxt) == 0) { 219 auth2_challenge_stop(authctxt); 220 return; 221 } 222 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 223 &input_userauth_info_response); 224 225 authctxt->method->postponed = 1; 226 } 227 228 static int 229 send_userauth_info_request(Authctxt *authctxt) 230 { 231 KbdintAuthctxt *kbdintctxt; 232 char *name, *instr, **prompts; 233 int i; 234 u_int *echo_on; 235 236 kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; 237 if (kbdintctxt->device->query(kbdintctxt->ctxt, 238 &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 239 return 0; 240 241 packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 242 packet_put_cstring(name); 243 packet_put_utf8_cstring(instr); 244 packet_put_cstring(""); /* language not used */ 245 packet_put_int(kbdintctxt->nreq); 246 for (i = 0; i < kbdintctxt->nreq; i++) { 247 packet_put_utf8_cstring(prompts[i]); 248 packet_put_char(echo_on[i]); 249 } 250 packet_send(); 251 packet_write_wait(); 252 253 for (i = 0; i < kbdintctxt->nreq; i++) 254 xfree(prompts[i]); 255 xfree(prompts); 256 xfree(echo_on); 257 xfree(name); 258 xfree(instr); 259 return 1; 260 } 261 262 static void 263 input_userauth_info_response(int type, u_int32_t seq, void *ctxt) 264 { 265 Authctxt *authctxt = ctxt; 266 KbdintAuthctxt *kbdintctxt; 267 int i, res, len; 268 u_int nresp; 269 char **response = NULL, *method; 270 271 if (authctxt == NULL) 272 fatal("input_userauth_info_response: no authctxt"); 273 kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; 274 if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 275 fatal("input_userauth_info_response: no kbdintctxt"); 276 if (kbdintctxt->device == NULL) 277 fatal("input_userauth_info_response: no device"); 278 279 nresp = packet_get_int(); 280 if (nresp != kbdintctxt->nreq) 281 fatal("input_userauth_info_response: wrong number of replies"); 282 if (nresp > 100) 283 fatal("input_userauth_info_response: too many replies"); 284 if (nresp > 0) { 285 response = xmalloc(nresp * sizeof(char *)); 286 for (i = 0; i < nresp; i++) 287 response[i] = packet_get_string(NULL); 288 } 289 packet_check_eom(); 290 291 if (authctxt->valid) { 292 res = kbdintctxt->device->respond(kbdintctxt->ctxt, 293 nresp, response); 294 } else { 295 res = -1; 296 } 297 298 for (i = 0; i < nresp; i++) { 299 memset(response[i], 'r', strlen(response[i])); 300 xfree(response[i]); 301 } 302 if (response) 303 xfree(response); 304 305 authctxt->method->postponed = 0; /* reset */ 306 switch (res) { 307 case 0: 308 /* Success! */ 309 authctxt->method->authenticated = 1; 310 break; 311 case 1: 312 /* Authentication needs further interaction */ 313 if (send_userauth_info_request(authctxt) == 1) { 314 authctxt->method->postponed = 1; 315 } 316 break; 317 default: 318 /* Failure! */ 319 break; 320 } 321 322 323 len = strlen("keyboard-interactive") + 2 + 324 strlen(kbdintctxt->device->name); 325 method = xmalloc(len); 326 snprintf(method, len, "keyboard-interactive/%s", 327 kbdintctxt->device->name); 328 329 if (authctxt->method->authenticated || authctxt->method->abandoned) { 330 auth2_challenge_stop(authctxt); 331 } else { 332 /* start next device */ 333 /* may set authctxt->method->postponed */ 334 auth2_challenge_start(authctxt); 335 } 336 userauth_finish(authctxt, method); 337 xfree(method); 338 } 339 340 void 341 privsep_challenge_enable(void) 342 { 343 #ifdef BSD_AUTH 344 extern KbdintDevice mm_bsdauth_device; 345 #endif 346 #ifdef SKEY 347 extern KbdintDevice mm_skey_device; 348 #endif 349 /* As long as SSHv1 has devices[0] hard coded this is fine */ 350 #ifdef BSD_AUTH 351 devices[0] = &mm_bsdauth_device; 352 #else 353 #ifdef SKEY 354 devices[0] = &mm_skey_device; 355 #endif 356 #endif 357 } 358 #endif /* lint */ 359