1 /* $OpenBSD: auth2-chall.c,v 1.53 2020/02/26 13:40:09 jsg 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 <sys/types.h> 28 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <stdarg.h> 33 34 #include "xmalloc.h" 35 #include "ssh2.h" 36 #include "sshkey.h" 37 #include "hostfile.h" 38 #include "auth.h" 39 #include "sshbuf.h" 40 #include "packet.h" 41 #include "dispatch.h" 42 #include "ssherr.h" 43 #include "log.h" 44 45 static int auth2_challenge_start(struct ssh *); 46 static int send_userauth_info_request(struct ssh *); 47 static int input_userauth_info_response(int, u_int32_t, struct ssh *); 48 49 extern KbdintDevice bsdauth_device; 50 51 KbdintDevice *devices[] = { 52 &bsdauth_device, 53 NULL 54 }; 55 56 typedef struct KbdintAuthctxt KbdintAuthctxt; 57 struct KbdintAuthctxt 58 { 59 char *devices; 60 void *ctxt; 61 KbdintDevice *device; 62 u_int nreq; 63 u_int devices_done; 64 }; 65 66 static KbdintAuthctxt * 67 kbdint_alloc(const char *devs) 68 { 69 KbdintAuthctxt *kbdintctxt; 70 struct sshbuf *b; 71 int i, r; 72 73 kbdintctxt = xcalloc(1, sizeof(KbdintAuthctxt)); 74 if (strcmp(devs, "") == 0) { 75 if ((b = sshbuf_new()) == NULL) 76 fatal("%s: sshbuf_new failed", __func__); 77 for (i = 0; devices[i]; i++) { 78 if ((r = sshbuf_putf(b, "%s%s", 79 sshbuf_len(b) ? "," : "", devices[i]->name)) != 0) 80 fatal("%s: buffer error: %s", 81 __func__, ssh_err(r)); 82 } 83 if ((kbdintctxt->devices = sshbuf_dup_string(b)) == NULL) 84 fatal("%s: sshbuf_dup_string failed", __func__); 85 sshbuf_free(b); 86 } else { 87 kbdintctxt->devices = xstrdup(devs); 88 } 89 debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 90 kbdintctxt->ctxt = NULL; 91 kbdintctxt->device = NULL; 92 kbdintctxt->nreq = 0; 93 94 return kbdintctxt; 95 } 96 static void 97 kbdint_reset_device(KbdintAuthctxt *kbdintctxt) 98 { 99 if (kbdintctxt->ctxt) { 100 kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 101 kbdintctxt->ctxt = NULL; 102 } 103 kbdintctxt->device = NULL; 104 } 105 static void 106 kbdint_free(KbdintAuthctxt *kbdintctxt) 107 { 108 if (kbdintctxt->device) 109 kbdint_reset_device(kbdintctxt); 110 free(kbdintctxt->devices); 111 freezero(kbdintctxt, sizeof(*kbdintctxt)); 112 } 113 /* get next device */ 114 static int 115 kbdint_next_device(Authctxt *authctxt, KbdintAuthctxt *kbdintctxt) 116 { 117 size_t len; 118 char *t; 119 int i; 120 121 if (kbdintctxt->device) 122 kbdint_reset_device(kbdintctxt); 123 do { 124 len = kbdintctxt->devices ? 125 strcspn(kbdintctxt->devices, ",") : 0; 126 127 if (len == 0) 128 break; 129 for (i = 0; devices[i]; i++) { 130 if ((kbdintctxt->devices_done & (1 << i)) != 0 || 131 !auth2_method_allowed(authctxt, 132 "keyboard-interactive", devices[i]->name)) 133 continue; 134 if (strncmp(kbdintctxt->devices, devices[i]->name, 135 len) == 0) { 136 kbdintctxt->device = devices[i]; 137 kbdintctxt->devices_done |= 1 << i; 138 } 139 } 140 t = kbdintctxt->devices; 141 kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 142 free(t); 143 debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 144 kbdintctxt->devices : "<empty>"); 145 } while (kbdintctxt->devices && !kbdintctxt->device); 146 147 return kbdintctxt->device ? 1 : 0; 148 } 149 150 /* 151 * try challenge-response, set authctxt->postponed if we have to 152 * wait for the response. 153 */ 154 int 155 auth2_challenge(struct ssh *ssh, char *devs) 156 { 157 Authctxt *authctxt = ssh->authctxt; 158 debug("auth2_challenge: user=%s devs=%s", 159 authctxt->user ? authctxt->user : "<nouser>", 160 devs ? devs : "<no devs>"); 161 162 if (authctxt->user == NULL || !devs) 163 return 0; 164 if (authctxt->kbdintctxt == NULL) 165 authctxt->kbdintctxt = kbdint_alloc(devs); 166 return auth2_challenge_start(ssh); 167 } 168 169 /* unregister kbd-int callbacks and context */ 170 void 171 auth2_challenge_stop(struct ssh *ssh) 172 { 173 Authctxt *authctxt = ssh->authctxt; 174 /* unregister callback */ 175 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 176 if (authctxt->kbdintctxt != NULL) { 177 kbdint_free(authctxt->kbdintctxt); 178 authctxt->kbdintctxt = NULL; 179 } 180 } 181 182 /* side effect: sets authctxt->postponed if a reply was sent*/ 183 static int 184 auth2_challenge_start(struct ssh *ssh) 185 { 186 Authctxt *authctxt = ssh->authctxt; 187 KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; 188 189 debug2("auth2_challenge_start: devices %s", 190 kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 191 192 if (kbdint_next_device(authctxt, kbdintctxt) == 0) { 193 auth2_challenge_stop(ssh); 194 return 0; 195 } 196 debug("auth2_challenge_start: trying authentication method '%s'", 197 kbdintctxt->device->name); 198 199 if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 200 auth2_challenge_stop(ssh); 201 return 0; 202 } 203 if (send_userauth_info_request(ssh) == 0) { 204 auth2_challenge_stop(ssh); 205 return 0; 206 } 207 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE, 208 &input_userauth_info_response); 209 210 authctxt->postponed = 1; 211 return 0; 212 } 213 214 static int 215 send_userauth_info_request(struct ssh *ssh) 216 { 217 Authctxt *authctxt = ssh->authctxt; 218 KbdintAuthctxt *kbdintctxt; 219 char *name, *instr, **prompts; 220 u_int r, i, *echo_on; 221 222 kbdintctxt = authctxt->kbdintctxt; 223 if (kbdintctxt->device->query(kbdintctxt->ctxt, 224 &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 225 return 0; 226 227 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST)) != 0 || 228 (r = sshpkt_put_cstring(ssh, name)) != 0 || 229 (r = sshpkt_put_cstring(ssh, instr)) != 0 || 230 (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language not used */ 231 (r = sshpkt_put_u32(ssh, kbdintctxt->nreq)) != 0) 232 fatal("%s: %s", __func__, ssh_err(r)); 233 for (i = 0; i < kbdintctxt->nreq; i++) { 234 if ((r = sshpkt_put_cstring(ssh, prompts[i])) != 0 || 235 (r = sshpkt_put_u8(ssh, echo_on[i])) != 0) 236 fatal("%s: %s", __func__, ssh_err(r)); 237 } 238 if ((r = sshpkt_send(ssh)) != 0 || 239 (r = ssh_packet_write_wait(ssh)) != 0) 240 fatal("%s: %s", __func__, ssh_err(r)); 241 242 for (i = 0; i < kbdintctxt->nreq; i++) 243 free(prompts[i]); 244 free(prompts); 245 free(echo_on); 246 free(name); 247 free(instr); 248 return 1; 249 } 250 251 static int 252 input_userauth_info_response(int type, u_int32_t seq, struct ssh *ssh) 253 { 254 Authctxt *authctxt = ssh->authctxt; 255 KbdintAuthctxt *kbdintctxt; 256 int authenticated = 0, res; 257 int r; 258 u_int i, nresp; 259 const char *devicename = NULL; 260 char **response = NULL; 261 262 if (authctxt == NULL) 263 fatal("input_userauth_info_response: no authctxt"); 264 kbdintctxt = authctxt->kbdintctxt; 265 if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 266 fatal("input_userauth_info_response: no kbdintctxt"); 267 if (kbdintctxt->device == NULL) 268 fatal("input_userauth_info_response: no device"); 269 270 authctxt->postponed = 0; /* reset */ 271 if ((r = sshpkt_get_u32(ssh, &nresp)) != 0) 272 fatal("%s: %s", __func__, ssh_err(r)); 273 if (nresp != kbdintctxt->nreq) 274 fatal("input_userauth_info_response: wrong number of replies"); 275 if (nresp > 100) 276 fatal("input_userauth_info_response: too many replies"); 277 if (nresp > 0) { 278 response = xcalloc(nresp, sizeof(char *)); 279 for (i = 0; i < nresp; i++) 280 if ((r = sshpkt_get_cstring(ssh, &response[i], 281 NULL)) != 0) 282 fatal("%s: %s", __func__, ssh_err(r)); 283 } 284 if ((r = sshpkt_get_end(ssh)) != 0) 285 fatal("%s: %s", __func__, ssh_err(r)); 286 287 res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); 288 289 for (i = 0; i < nresp; i++) { 290 explicit_bzero(response[i], strlen(response[i])); 291 free(response[i]); 292 } 293 free(response); 294 295 switch (res) { 296 case 0: 297 /* Success! */ 298 authenticated = authctxt->valid ? 1 : 0; 299 break; 300 case 1: 301 /* Authentication needs further interaction */ 302 if (send_userauth_info_request(ssh) == 1) 303 authctxt->postponed = 1; 304 break; 305 default: 306 /* Failure! */ 307 break; 308 } 309 devicename = kbdintctxt->device->name; 310 if (!authctxt->postponed) { 311 if (authenticated) { 312 auth2_challenge_stop(ssh); 313 } else { 314 /* start next device */ 315 /* may set authctxt->postponed */ 316 auth2_challenge_start(ssh); 317 } 318 } 319 userauth_finish(ssh, authenticated, "keyboard-interactive", 320 devicename); 321 return 0; 322 } 323 324 void 325 privsep_challenge_enable(void) 326 { 327 extern KbdintDevice mm_bsdauth_device; 328 devices[0] = &mm_bsdauth_device; 329 } 330