Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssh/auth2-chall.c
34677 views
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
384