~ubuntu-branches/ubuntu/lucid/openssh/lucid

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