Line 0
Link Here
|
|
|
1 |
/* NTLM (via ntlm_auth) plugin |
2 |
* Volker Lendecke, Andrew Bartlett |
3 |
*/ |
4 |
/* |
5 |
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. |
6 |
* |
7 |
* Redistribution and use in source and binary forms, with or without |
8 |
* modification, are permitted provided that the following conditions |
9 |
* are met: |
10 |
* |
11 |
* 1. Redistributions of source code must retain the above copyright |
12 |
* notice, this list of conditions and the following disclaimer. |
13 |
* |
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
* notice, this list of conditions and the following disclaimer in |
16 |
* the documentation and/or other materials provided with the |
17 |
* distribution. |
18 |
* |
19 |
* 3. The name "Carnegie Mellon University" must not be used to |
20 |
* endorse or promote products derived from this software without |
21 |
* prior written permission. For permission or any other legal |
22 |
* details, please contact |
23 |
* Office of Technology Transfer |
24 |
* Carnegie Mellon University |
25 |
* 5000 Forbes Avenue |
26 |
* Pittsburgh, PA 15213-3890 |
27 |
* (412) 268-4387, fax: (412) 268-7395 |
28 |
* tech-transfer@andrew.cmu.edu |
29 |
* |
30 |
* 4. Redistributions of any form whatsoever must retain the following |
31 |
* acknowledgment: |
32 |
* "This product includes software developed by Computing Services |
33 |
* at Carnegie Mellon University (http://www.cmu.edu/computing/)." |
34 |
* |
35 |
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
36 |
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
37 |
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
38 |
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
39 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
40 |
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
41 |
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
42 |
*/ |
43 |
|
44 |
#include <config.h> |
45 |
#include <stdio.h> |
46 |
#include <string.h> |
47 |
#include <sasl.h> |
48 |
#include <saslplug.h> |
49 |
#include <syslog.h> |
50 |
|
51 |
#include <sys/types.h> |
52 |
#include <sys/wait.h> |
53 |
#include <unistd.h> |
54 |
|
55 |
#include "plugin_common.h" |
56 |
#include "smb_helper.h" |
57 |
|
58 |
#ifdef macintosh |
59 |
#include <sasl_ntlm_plugin_decl.h> |
60 |
#endif |
61 |
|
62 |
/***************************** Common Section *****************************/ |
63 |
|
64 |
static const char plugin_id[] = "$Id: ntlm.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $"; |
65 |
|
66 |
/***************************** Server Section *****************************/ |
67 |
|
68 |
struct ntlm_context { |
69 |
struct smb_helper *helper; |
70 |
int first; |
71 |
sasl_secret_t *password; /* user password */ |
72 |
unsigned int free_password; /* set if we need to free password */ |
73 |
unsigned int sent_password; /* set if we have told ntlm_auth the password already */ |
74 |
const char *authid; |
75 |
const char *domain; |
76 |
}; |
77 |
|
78 |
static int ntlm_server_mech_new(void *glob_context __attribute__((unused)), |
79 |
sasl_server_params_t *sparams, |
80 |
const char *challenge __attribute__((unused)), |
81 |
unsigned challen __attribute__((unused)), |
82 |
void **conn_context) |
83 |
{ |
84 |
char * argv[] = { "ntlm_auth", |
85 |
"--helper-protocol=squid-2.5-ntlmssp", |
86 |
NULL }; |
87 |
|
88 |
struct ntlm_context *context = |
89 |
malloc(sizeof(struct ntlm_context)); |
90 |
|
91 |
if (context == NULL) { |
92 |
syslog(LOG_DEBUG, "ntlm_server_mech_new: No memory\n"); |
93 |
MEMERROR(sparams->utils); |
94 |
return SASL_NOMEM; |
95 |
} |
96 |
|
97 |
memset(context, 0, sizeof(struct ntlm_context)); |
98 |
context->first = 1; |
99 |
|
100 |
*conn_context = context; |
101 |
|
102 |
return fork_helper(&context->helper, "ntlm_auth", |
103 |
argv, sparams->utils); |
104 |
} |
105 |
|
106 |
static int ntlm_server_mech_step(void *conn_context, |
107 |
sasl_server_params_t *params, |
108 |
const char *clientin, |
109 |
unsigned clientinlen, |
110 |
const char **serverout, |
111 |
unsigned *serveroutlen, |
112 |
sasl_out_params_t *oparams) |
113 |
{ |
114 |
struct ntlm_context *context = |
115 |
(struct ntlm_context *)conn_context; |
116 |
|
117 |
unsigned char childbuf[1025]; |
118 |
unsigned childbuflen; |
119 |
|
120 |
unsigned base64len; |
121 |
|
122 |
static unsigned char bin[1025]; |
123 |
|
124 |
int result; |
125 |
|
126 |
if (context->first) { |
127 |
strncpy(childbuf, "YR ", sizeof(childbuf)-1); |
128 |
context->first = 0; |
129 |
} else { |
130 |
strncpy(childbuf, "KK ", sizeof(childbuf)-1); |
131 |
} |
132 |
|
133 |
if (params->utils->encode64(clientin, clientinlen, |
134 |
childbuf+3, sizeof(childbuf)-4, |
135 |
&childbuflen) != SASL_OK) { |
136 |
syslog(LOG_DEBUG, |
137 |
"Could not convert clientin to base64\n"); |
138 |
return SASL_FAIL; |
139 |
} |
140 |
|
141 |
if ((result = interact_helper(context->helper, |
142 |
childbuf, |
143 |
sizeof(childbuf), |
144 |
&childbuflen)) != SASL_OK) |
145 |
return result; |
146 |
|
147 |
/* The child's reply contains 3 parts: |
148 |
- The code: TT, AF or NA |
149 |
- The blob to send to the client, coded in base64 |
150 |
- or for AF it's domain\\user |
151 |
for NA it's the NT error code |
152 |
*/ |
153 |
|
154 |
if (strlen(childbuf) < 3) { |
155 |
syslog(LOG_DEBUG, "Got invalid response from child (parse of ntlm_auth output failure)\n"); |
156 |
return SASL_FAIL; |
157 |
} |
158 |
|
159 |
base64len = strlen(childbuf) - 3; |
160 |
|
161 |
if ( strncmp(childbuf, "TT ", 3) == 0) { |
162 |
if (params->utils->decode64(childbuf+3, base64len, |
163 |
bin, sizeof(bin)-1, |
164 |
serveroutlen) != SASL_OK) { |
165 |
syslog(LOG_DEBUG, "Could not decode child's base64\n"); |
166 |
return SASL_FAIL; |
167 |
} |
168 |
*serverout = bin; |
169 |
|
170 |
/* Next round */ |
171 |
return SASL_CONTINUE; |
172 |
} |
173 |
if (strncmp(childbuf, "NA ", 3) == 0) { |
174 |
/* Not Authenticated */ |
175 |
kill_helper(context->helper); |
176 |
return SASL_BADAUTH; |
177 |
} |
178 |
|
179 |
if (strncmp(childbuf, "AF ", 3) == 0) { |
180 |
/* Authentication Fine */ |
181 |
char *user = childbuf + 3; |
182 |
int status; |
183 |
|
184 |
kill_helper(context->helper); |
185 |
|
186 |
if ( (status = params->canon_user(params->utils->conn, |
187 |
user, 0, SASL_CU_AUTHID, |
188 |
oparams)) != SASL_OK ) { |
189 |
syslog(LOG_DEBUG, "canon_user for AUTHID [%s] failed\n", user); |
190 |
return status; |
191 |
} |
192 |
|
193 |
if ( (status = params->canon_user(params->utils->conn, |
194 |
user, 0, SASL_CU_AUTHZID, |
195 |
oparams)) != SASL_OK ) { |
196 |
syslog(LOG_DEBUG, "canon_user for AUTHZID [%s] failed\n", user); |
197 |
return status; |
198 |
} |
199 |
|
200 |
oparams->doneflag = 1; |
201 |
oparams->mech_ssf = 0; |
202 |
oparams->maxoutbuf = 0; |
203 |
oparams->encode_context = NULL; |
204 |
oparams->encode = NULL; |
205 |
oparams->decode_context = NULL; |
206 |
oparams->decode = NULL; |
207 |
|
208 |
return SASL_OK; |
209 |
} |
210 |
|
211 |
syslog(LOG_DEBUG, "Child's response unknown\n"); |
212 |
return SASL_FAIL; |
213 |
} |
214 |
|
215 |
static void ntlm_server_mech_dispose(void *conn_context, |
216 |
const sasl_utils_t *utils __attribute__((unused))) |
217 |
{ |
218 |
struct ntlm_context *context = |
219 |
(struct ntlm_context *)conn_context; |
220 |
|
221 |
kill_helper(context->helper); |
222 |
|
223 |
return; |
224 |
} |
225 |
|
226 |
static sasl_server_plug_t ntlm_server_plugins[] = |
227 |
{ |
228 |
{ |
229 |
"NTLM", /* mech_name */ |
230 |
0, /* max_ssf */ |
231 |
SASL_SEC_NOANONYMOUS |
232 |
| SASL_SEC_NOPLAINTEXT, /* security_flags */ |
233 |
SASL_FEAT_WANT_CLIENT_FIRST, /* features */ |
234 |
NULL, /* glob_context */ |
235 |
&ntlm_server_mech_new, /* mech_new */ |
236 |
&ntlm_server_mech_step, /* mech_step */ |
237 |
&ntlm_server_mech_dispose, /* mech_dispose */ |
238 |
NULL, /* mech_free */ |
239 |
NULL, /* setpass */ |
240 |
NULL, /* user_query */ |
241 |
NULL, /* idle */ |
242 |
NULL, /* mech_avail */ |
243 |
NULL /* spare */ |
244 |
} |
245 |
}; |
246 |
|
247 |
int ntlm_server_plug_init(const sasl_utils_t *utils, |
248 |
int maxversion, |
249 |
int *out_version, |
250 |
sasl_server_plug_t **pluglist, |
251 |
int *plugcount) |
252 |
{ |
253 |
if (maxversion < SASL_SERVER_PLUG_VERSION) { |
254 |
SETERROR(utils, "NTLM version mismatch"); |
255 |
return SASL_BADVERS; |
256 |
} |
257 |
|
258 |
*out_version = SASL_SERVER_PLUG_VERSION; |
259 |
*pluglist = ntlm_server_plugins; |
260 |
*plugcount = 1; |
261 |
|
262 |
return SASL_OK; |
263 |
} |
264 |
|
265 |
/***************************** Client Section *****************************/ |
266 |
|
267 |
static int ntlm_new_client_helper(struct ntlm_context *context, |
268 |
sasl_client_params_t *params, |
269 |
sasl_interact_t **prompt_need, |
270 |
sasl_out_params_t *oparams) |
271 |
{ |
272 |
int auth_result = SASL_OK; |
273 |
int domain_result = SASL_OK; |
274 |
|
275 |
int pass_result = SASL_OK; |
276 |
int result; |
277 |
|
278 |
char child_user_arg[512]; |
279 |
char child_domain_arg[512]; |
280 |
|
281 |
const char *domains[] = { |
282 |
"", |
283 |
NULL |
284 |
}; |
285 |
|
286 |
char *argv[] = { "ntlm_auth", |
287 |
"--helper-protocol=ntlmssp-client-1", |
288 |
child_user_arg, |
289 |
child_domain_arg, |
290 |
NULL }; |
291 |
|
292 |
if (context->authid == NULL) { |
293 |
auth_result = _plug_get_authid(params->utils, |
294 |
&context->authid, prompt_need); |
295 |
|
296 |
if ( (auth_result != SASL_OK) && |
297 |
(auth_result != SASL_INTERACT) && |
298 |
(context->authid == NULL) ) |
299 |
return auth_result; |
300 |
} |
301 |
|
302 |
if (context->domain == NULL) { |
303 |
domain_result = _plug_get_realm(params->utils, domains, |
304 |
&context->domain, prompt_need); |
305 |
if ( (domain_result != SASL_OK) && |
306 |
(domain_result != SASL_INTERACT) && |
307 |
(context->domain == NULL) ) |
308 |
return domain_result; |
309 |
} |
310 |
|
311 |
if (context->password == NULL) { |
312 |
pass_result = _plug_get_password(params->utils, &context->password, |
313 |
&context->free_password, prompt_need); |
314 |
|
315 |
if ( (pass_result != SASL_OK) && |
316 |
(pass_result != SASL_INTERACT) |
317 |
&& (context->password == NULL)) |
318 |
return pass_result; |
319 |
} |
320 |
|
321 |
/* free prompts we got */ |
322 |
if (prompt_need && *prompt_need) { |
323 |
params->utils->free(*prompt_need); |
324 |
*prompt_need = NULL; |
325 |
} |
326 |
|
327 |
if ( (auth_result == SASL_INTERACT) || |
328 |
(domain_result == SASL_INTERACT) ) { |
329 |
result = _plug_make_prompts(params->utils, prompt_need, |
330 |
NULL, NULL, |
331 |
(auth_result == SASL_INTERACT) ? |
332 |
"Username" : NULL, NULL, |
333 |
pass_result == SASL_INTERACT ? |
334 |
"Password" : NULL, NULL, |
335 |
NULL, NULL, NULL, |
336 |
NULL, |
337 |
(domain_result == SASL_INTERACT) ? |
338 |
"Realm (Domain)" : NULL, NULL); |
339 |
|
340 |
if (result != SASL_OK) |
341 |
return result; |
342 |
return SASL_INTERACT; |
343 |
} |
344 |
|
345 |
if (!context->password) { |
346 |
PARAMERROR(params->utils); |
347 |
return SASL_BADPARAM; |
348 |
} |
349 |
|
350 |
oparams->user = strdup(context->authid); |
351 |
oparams->ulen = strlen(context->authid); |
352 |
oparams->authid = strdup(context->authid); |
353 |
oparams->alen = oparams->ulen; |
354 |
|
355 |
snprintf(child_user_arg, sizeof(child_user_arg)-1, |
356 |
"--username=%s", context->authid); |
357 |
snprintf(child_domain_arg, sizeof(child_domain_arg)-1, |
358 |
"--domain=%s", context->domain); |
359 |
|
360 |
return fork_helper(&context->helper, "ntlm_auth", |
361 |
argv, params->utils); |
362 |
} |
363 |
|
364 |
static int ntlm_client_mech_new(void *glob_context __attribute__((unused)), |
365 |
sasl_client_params_t *params, |
366 |
void **conn_context) |
367 |
{ |
368 |
struct ntlm_context *context = |
369 |
malloc(sizeof(struct ntlm_context)); |
370 |
|
371 |
if (context == NULL) { |
372 |
MEMERROR(params->utils); |
373 |
return SASL_NOMEM; |
374 |
} |
375 |
|
376 |
memset(context, 0, sizeof(struct ntlm_context)); |
377 |
context->first = 1; |
378 |
|
379 |
*conn_context = context; |
380 |
|
381 |
return SASL_OK; |
382 |
} |
383 |
|
384 |
static int ntlm_client_mech_step(void *conn_context, |
385 |
sasl_client_params_t *params, |
386 |
const char *serverin, |
387 |
unsigned serverinlen, |
388 |
sasl_interact_t **prompt_need, |
389 |
const char **clientout, |
390 |
unsigned *clientoutlen, |
391 |
sasl_out_params_t *oparams) |
392 |
{ |
393 |
struct ntlm_context *context = |
394 |
(struct ntlm_context *)conn_context; |
395 |
|
396 |
unsigned char childbuf[1025]; |
397 |
unsigned childbuflen; |
398 |
|
399 |
int result; |
400 |
|
401 |
if (context->helper == NULL) { |
402 |
result = ntlm_new_client_helper(context, params, |
403 |
prompt_need, oparams); |
404 |
|
405 |
if (result != SASL_OK) |
406 |
return result; |
407 |
} |
408 |
|
409 |
if (!context->sent_password) { |
410 |
|
411 |
strncpy(childbuf, "PW ", sizeof(childbuf)-1); |
412 |
|
413 |
if (params->utils->encode64(context->password->data, context->password->len, |
414 |
childbuf+3, sizeof(childbuf)-3, |
415 |
&childbuflen) != SASL_OK) { |
416 |
syslog(LOG_DEBUG, |
417 |
"Could not convert password to base64\n"); |
418 |
return SASL_FAIL; |
419 |
} |
420 |
|
421 |
if ((result = interact_helper(context->helper, |
422 |
childbuf, |
423 |
sizeof(childbuf), |
424 |
&childbuflen)) != SASL_OK) |
425 |
return result; |
426 |
|
427 |
if (strncmp(childbuf, "OK", 2) != 0) { |
428 |
syslog(LOG_DEBUG, "Child did not accept our password\n"); |
429 |
return SASL_FAIL; |
430 |
} |
431 |
context->sent_password = 1; |
432 |
} |
433 |
|
434 |
if (context->first) { |
435 |
strncpy(childbuf, "YR", sizeof(childbuf)-1); |
436 |
context->first = 0; |
437 |
} else { |
438 |
strncpy(childbuf, "TT", sizeof(childbuf)-1); |
439 |
} |
440 |
|
441 |
if (serverinlen) { |
442 |
strncpy(childbuf+2, " ", sizeof(childbuf)-3); |
443 |
if (params->utils->encode64(serverin, serverinlen, |
444 |
childbuf+3, sizeof(childbuf)-4, |
445 |
&childbuflen) != SASL_OK) { |
446 |
syslog(LOG_DEBUG, |
447 |
"Could not convert serverin to base64\n"); |
448 |
return SASL_FAIL; |
449 |
} |
450 |
} |
451 |
|
452 |
if ((result = interact_helper(context->helper, |
453 |
childbuf, |
454 |
sizeof(childbuf), |
455 |
&childbuflen)) != SASL_OK) |
456 |
return result; |
457 |
|
458 |
/* The child's reply can be: |
459 |
"PW" -> It wants a password (but we premptivly deal with that above) |
460 |
"YR <base64>" -> Send (initial) <base64> to the server |
461 |
"KK <base64>" -> Send <base64> to the server |
462 |
"AF" -> Authenticated |
463 |
"NA" -> Not Authenticated |
464 |
*/ |
465 |
|
466 |
if ((strncmp(childbuf, "YR ", 3) == 0) || (strncmp(childbuf, "KK ", 3) == 0)) { |
467 |
static char bin[2048]; |
468 |
|
469 |
if (params->utils->decode64(childbuf+3, childbuflen-3, |
470 |
bin, sizeof(bin)-1, |
471 |
clientoutlen) != SASL_OK) { |
472 |
syslog(LOG_DEBUG, "Could not decode child's base64\n"); |
473 |
return SASL_FAIL; |
474 |
} |
475 |
*clientout = bin; |
476 |
return SASL_CONTINUE; |
477 |
} |
478 |
|
479 |
if ( (strncmp(childbuf, "AF", 2) == 0) || |
480 |
(strncmp(childbuf, "NA", 2) == 0) ) { |
481 |
|
482 |
kill_helper(context->helper); |
483 |
|
484 |
*clientout = NULL; |
485 |
*clientoutlen = 0; |
486 |
|
487 |
/* set oparams */ |
488 |
oparams->doneflag = 1; |
489 |
oparams->mech_ssf = 0; |
490 |
oparams->maxoutbuf = 0; |
491 |
oparams->encode_context = NULL; |
492 |
oparams->encode = NULL; |
493 |
oparams->decode_context = NULL; |
494 |
oparams->decode = NULL; |
495 |
|
496 |
return (strncmp(childbuf, "AF", 2) == 0) ? |
497 |
SASL_OK : SASL_BADAUTH; |
498 |
} |
499 |
|
500 |
return SASL_FAIL; |
501 |
} |
502 |
|
503 |
static void ntlm_client_mech_dispose(void *conn_context, |
504 |
const sasl_utils_t *utils __attribute__((unused))) |
505 |
{ |
506 |
struct ntlm_context *context = |
507 |
(struct ntlm_context *)conn_context; |
508 |
|
509 |
kill_helper(context->helper); |
510 |
|
511 |
return; |
512 |
} |
513 |
|
514 |
static void ntlm_client_mech_free(void *conn_context, |
515 |
const sasl_utils_t *utils __attribute__((unused))) |
516 |
{ |
517 |
struct ntlm_context *context = |
518 |
(struct ntlm_context *)conn_context; |
519 |
|
520 |
if (context->free_password) |
521 |
_plug_free_secret(utils, &context->password); |
522 |
|
523 |
return; |
524 |
} |
525 |
|
526 |
static sasl_client_plug_t ntlm_client_plugins[] = |
527 |
{ |
528 |
{ |
529 |
"NTLM", /* mech_name */ |
530 |
0, /* max_ssf */ |
531 |
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT, /* security_flags */ |
532 |
SASL_FEAT_WANT_CLIENT_FIRST, /* features */ |
533 |
NULL, /* required_prompts */ |
534 |
NULL, /* glob_context */ |
535 |
&ntlm_client_mech_new, /* mech_new */ |
536 |
&ntlm_client_mech_step, /* mech_step */ |
537 |
&ntlm_client_mech_dispose, /* mech_dispose */ |
538 |
&ntlm_client_mech_free, /* mech_free */ |
539 |
NULL, /* idle */ |
540 |
NULL, /* spare */ |
541 |
NULL /* spare */ |
542 |
} |
543 |
}; |
544 |
|
545 |
int ntlm_client_plug_init(sasl_utils_t *utils, |
546 |
int maxversion, |
547 |
int *out_version, |
548 |
sasl_client_plug_t **pluglist, |
549 |
int *plugcount) |
550 |
{ |
551 |
if (maxversion < SASL_CLIENT_PLUG_VERSION) { |
552 |
SETERROR(utils, "NTLM version mismatch"); |
553 |
return SASL_BADVERS; |
554 |
} |
555 |
|
556 |
*out_version = SASL_CLIENT_PLUG_VERSION; |
557 |
*pluglist = ntlm_client_plugins; |
558 |
*plugcount = 1; |
559 |
|
560 |
return SASL_OK; |
561 |
} |