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