2 /* $OpenBSD: auth2-chall.c,v 1.34 2008/12/09 04:32:22 djm Exp $ */
4 * Copyright (c) 2001 Markus Friedl. All rights reserved.
5 * Copyright (c) 2001 Per Allansson. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 __RCSID("$NetBSD: auth2-chall.c,v 1.20 2008/04/06 23:38:19 christos Exp $");
30 #include <sys/types.h>
47 extern ServerOptions options
;
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 *);
54 extern KbdintDevice bsdauth_device
;
57 extern KbdintDevice sshpam_device
;
60 extern KbdintDevice skey_device
;
64 KbdintDevice
*devices
[] = {
78 typedef struct KbdintAuthctxt KbdintAuthctxt
;
88 void remove_kbdint_device(const char *);
90 remove_kbdint_device(const char *devname
)
94 for (i
= 0; devices
[i
] != NULL
; i
++)
95 if (strcmp(devices
[i
]->name
, devname
) == 0) {
96 for (j
= i
; devices
[j
] != NULL
; j
++)
97 devices
[j
] = devices
[j
+1];
103 static KbdintAuthctxt
*
104 kbdint_alloc(const char *devs
)
106 KbdintAuthctxt
*kbdintctxt
;
111 if (!options
.use_pam
)
112 remove_kbdint_device("pam");
115 kbdintctxt
= xmalloc(sizeof(KbdintAuthctxt
));
116 if (strcmp(devs
, "") == 0) {
118 for (i
= 0; devices
[i
]; i
++) {
119 if (buffer_len(&b
) > 0)
120 buffer_append(&b
, ",", 1);
121 buffer_append(&b
, devices
[i
]->name
,
122 strlen(devices
[i
]->name
));
124 buffer_append(&b
, "\0", 1);
125 kbdintctxt
->devices
= xstrdup(buffer_ptr(&b
));
128 kbdintctxt
->devices
= xstrdup(devs
);
130 debug("kbdint_alloc: devices '%s'", kbdintctxt
->devices
);
131 kbdintctxt
->ctxt
= NULL
;
132 kbdintctxt
->device
= NULL
;
133 kbdintctxt
->nreq
= 0;
138 kbdint_reset_device(KbdintAuthctxt
*kbdintctxt
)
140 if (kbdintctxt
->ctxt
) {
141 kbdintctxt
->device
->free_ctx(kbdintctxt
->ctxt
);
142 kbdintctxt
->ctxt
= NULL
;
144 kbdintctxt
->device
= NULL
;
147 kbdint_free(KbdintAuthctxt
*kbdintctxt
)
149 if (kbdintctxt
->device
)
150 kbdint_reset_device(kbdintctxt
);
151 if (kbdintctxt
->devices
) {
152 xfree(kbdintctxt
->devices
);
153 kbdintctxt
->devices
= NULL
;
157 /* get next device */
159 kbdint_next_device(KbdintAuthctxt
*kbdintctxt
)
165 if (kbdintctxt
->device
)
166 kbdint_reset_device(kbdintctxt
);
168 len
= kbdintctxt
->devices
?
169 strcspn(kbdintctxt
->devices
, ",") : 0;
173 for (i
= 0; devices
[i
]; i
++)
174 if (strncmp(kbdintctxt
->devices
, devices
[i
]->name
, len
) == 0)
175 kbdintctxt
->device
= devices
[i
];
176 t
= kbdintctxt
->devices
;
177 kbdintctxt
->devices
= t
[len
] ? xstrdup(t
+len
+1) : NULL
;
179 debug2("kbdint_next_device: devices %s", kbdintctxt
->devices
?
180 kbdintctxt
->devices
: "<empty>");
181 } while (kbdintctxt
->devices
&& !kbdintctxt
->device
);
183 return kbdintctxt
->device
? 1 : 0;
187 * try challenge-response, set authctxt->postponed if we have to
188 * wait for the response.
191 auth2_challenge(Authctxt
*authctxt
, char *devs
)
193 debug("auth2_challenge: user=%s devs=%s",
194 authctxt
->user
? authctxt
->user
: "<nouser>",
195 devs
? devs
: "<no devs>");
197 if (authctxt
->user
== NULL
|| !devs
)
199 if (authctxt
->kbdintctxt
== NULL
)
200 authctxt
->kbdintctxt
= kbdint_alloc(devs
);
201 return auth2_challenge_start(authctxt
);
204 /* unregister kbd-int callbacks and context */
206 auth2_challenge_stop(Authctxt
*authctxt
)
208 /* unregister callback */
209 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE
, NULL
);
210 if (authctxt
->kbdintctxt
!= NULL
) {
211 kbdint_free(authctxt
->kbdintctxt
);
212 authctxt
->kbdintctxt
= NULL
;
216 /* side effect: sets authctxt->postponed if a reply was sent*/
218 auth2_challenge_start(Authctxt
*authctxt
)
220 KbdintAuthctxt
*kbdintctxt
= authctxt
->kbdintctxt
;
222 debug2("auth2_challenge_start: devices %s",
223 kbdintctxt
->devices
? kbdintctxt
->devices
: "<empty>");
225 if (kbdint_next_device(kbdintctxt
) == 0) {
226 auth2_challenge_stop(authctxt
);
229 debug("auth2_challenge_start: trying authentication method '%s'",
230 kbdintctxt
->device
->name
);
232 if ((kbdintctxt
->ctxt
= kbdintctxt
->device
->init_ctx(authctxt
)) == NULL
) {
233 auth2_challenge_stop(authctxt
);
236 if (send_userauth_info_request(authctxt
) == 0) {
237 auth2_challenge_stop(authctxt
);
240 dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE
,
241 &input_userauth_info_response
);
243 authctxt
->postponed
= 1;
248 send_userauth_info_request(Authctxt
*authctxt
)
250 KbdintAuthctxt
*kbdintctxt
;
251 char *name
, *instr
, **prompts
;
254 kbdintctxt
= authctxt
->kbdintctxt
;
255 if (kbdintctxt
->device
->query(kbdintctxt
->ctxt
,
256 &name
, &instr
, &kbdintctxt
->nreq
, &prompts
, &echo_on
))
259 packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST
);
260 packet_put_cstring(name
);
261 packet_put_cstring(instr
);
262 packet_put_cstring(""); /* language not used */
263 packet_put_int(kbdintctxt
->nreq
);
264 for (i
= 0; i
< kbdintctxt
->nreq
; i
++) {
265 packet_put_cstring(prompts
[i
]);
266 packet_put_char(echo_on
[i
]);
271 for (i
= 0; i
< kbdintctxt
->nreq
; i
++)
281 input_userauth_info_response(int type
, u_int32_t seq
, void *ctxt
)
283 Authctxt
*authctxt
= ctxt
;
284 KbdintAuthctxt
*kbdintctxt
;
285 int authenticated
= 0, res
;
287 char **response
= NULL
, *method
;
289 if (authctxt
== NULL
)
290 fatal("input_userauth_info_response: no authctxt");
291 kbdintctxt
= authctxt
->kbdintctxt
;
292 if (kbdintctxt
== NULL
|| kbdintctxt
->ctxt
== NULL
)
293 fatal("input_userauth_info_response: no kbdintctxt");
294 if (kbdintctxt
->device
== NULL
)
295 fatal("input_userauth_info_response: no device");
297 authctxt
->postponed
= 0; /* reset */
298 nresp
= packet_get_int();
299 if (nresp
!= kbdintctxt
->nreq
)
300 fatal("input_userauth_info_response: wrong number of replies");
302 fatal("input_userauth_info_response: too many replies");
304 response
= xcalloc(nresp
, sizeof(char *));
305 for (i
= 0; i
< nresp
; i
++)
306 response
[i
] = packet_get_string(NULL
);
310 res
= kbdintctxt
->device
->respond(kbdintctxt
->ctxt
, nresp
, response
);
312 for (i
= 0; i
< nresp
; i
++) {
313 memset(response
[i
], 'r', strlen(response
[i
]));
322 authenticated
= authctxt
->valid
? 1 : 0;
325 /* Authentication needs further interaction */
326 if (send_userauth_info_request(authctxt
) == 1)
327 authctxt
->postponed
= 1;
334 xasprintf(&method
, "keyboard-interactive/%s", kbdintctxt
->device
->name
);
336 if (!authctxt
->postponed
) {
338 auth2_challenge_stop(authctxt
);
340 /* start next device */
341 /* may set authctxt->postponed */
342 auth2_challenge_start(authctxt
);
345 userauth_finish(authctxt
, authenticated
, method
);
350 privsep_challenge_enable(void)
352 #if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY)
356 extern KbdintDevice mm_bsdauth_device
;
359 extern KbdintDevice mm_sshpam_device
;
362 extern KbdintDevice mm_skey_device
;
364 /* As long as SSHv1 has devices[0] hard coded this is fine */
366 devices
[n
++] = &mm_bsdauth_device
;
369 devices
[n
++] = &mm_sshpam_device
;
372 devices
[n
++] = &mm_skey_device
;