source: trunk-3.0/source/libads/sasl.c@ 101

Last change on this file since 101 was 26, checked in by Paul Smedley, 18 years ago

Updated source to 3.0.25rc1

File size: 14.4 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 ads sasl code
4 Copyright (C) Andrew Tridgell 2001
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22
23#ifdef HAVE_LDAP
24
25/*
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
28*/
29static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
30{
31 DATA_BLOB msg1 = data_blob(NULL, 0);
32 DATA_BLOB blob = data_blob(NULL, 0);
33 DATA_BLOB blob_in = data_blob(NULL, 0);
34 DATA_BLOB blob_out = data_blob(NULL, 0);
35 struct berval cred, *scred = NULL;
36 int rc;
37 NTSTATUS nt_status;
38 int turn = 1;
39
40 struct ntlmssp_state *ntlmssp_state;
41
42 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
43 return ADS_ERROR_NT(nt_status);
44 }
45 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
46
47 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
48 return ADS_ERROR_NT(nt_status);
49 }
50 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
51 return ADS_ERROR_NT(nt_status);
52 }
53 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
54 return ADS_ERROR_NT(nt_status);
55 }
56
57 blob_in = data_blob(NULL, 0);
58
59 do {
60 nt_status = ntlmssp_update(ntlmssp_state,
61 blob_in, &blob_out);
62 data_blob_free(&blob_in);
63 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
64 || NT_STATUS_IS_OK(nt_status))
65 && blob_out.length) {
66 if (turn == 1) {
67 /* and wrap it in a SPNEGO wrapper */
68 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
69 } else {
70 /* wrap it in SPNEGO */
71 msg1 = spnego_gen_auth(blob_out);
72 }
73
74 data_blob_free(&blob_out);
75
76 cred.bv_val = (char *)msg1.data;
77 cred.bv_len = msg1.length;
78 scred = NULL;
79 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
80 data_blob_free(&msg1);
81 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
82 if (scred) {
83 ber_bvfree(scred);
84 }
85
86 ntlmssp_end(&ntlmssp_state);
87 return ADS_ERROR(rc);
88 }
89 if (scred) {
90 blob = data_blob(scred->bv_val, scred->bv_len);
91 ber_bvfree(scred);
92 } else {
93 blob = data_blob(NULL, 0);
94 }
95
96 } else {
97
98 ntlmssp_end(&ntlmssp_state);
99 data_blob_free(&blob_out);
100 return ADS_ERROR_NT(nt_status);
101 }
102
103 if ((turn == 1) &&
104 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
105 DATA_BLOB tmp_blob = data_blob(NULL, 0);
106 /* the server might give us back two challenges */
107 if (!spnego_parse_challenge(blob, &blob_in,
108 &tmp_blob)) {
109
110 ntlmssp_end(&ntlmssp_state);
111 data_blob_free(&blob);
112 DEBUG(3,("Failed to parse challenges\n"));
113 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
114 }
115 data_blob_free(&tmp_blob);
116 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
117 if (!spnego_parse_auth_response(blob, nt_status,
118 &blob_in)) {
119
120 ntlmssp_end(&ntlmssp_state);
121 data_blob_free(&blob);
122 DEBUG(3,("Failed to parse auth response\n"));
123 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
124 }
125 }
126 data_blob_free(&blob);
127 data_blob_free(&blob_out);
128 turn++;
129 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
130
131 /* we have a reference conter on ntlmssp_state, if we are signing
132 then the state will be kept by the signing engine */
133
134 ntlmssp_end(&ntlmssp_state);
135
136 return ADS_ERROR(rc);
137}
138
139#ifdef HAVE_KRB5
140/*
141 perform a LDAP/SASL/SPNEGO/KRB5 bind
142*/
143static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
144{
145 DATA_BLOB blob = data_blob(NULL, 0);
146 struct berval cred, *scred = NULL;
147 DATA_BLOB session_key = data_blob(NULL, 0);
148 int rc;
149
150 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
151 &ads->auth.tgs_expire);
152
153 if (rc) {
154 return ADS_ERROR_KRB5(rc);
155 }
156
157 /* now send the auth packet and we should be done */
158 cred.bv_val = (char *)blob.data;
159 cred.bv_len = blob.length;
160
161 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
162
163 data_blob_free(&blob);
164 data_blob_free(&session_key);
165 if(scred)
166 ber_bvfree(scred);
167
168 return ADS_ERROR(rc);
169}
170#endif
171
172/*
173 this performs a SASL/SPNEGO bind
174*/
175static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
176{
177 struct berval *scred=NULL;
178 int rc, i;
179 ADS_STATUS status;
180 DATA_BLOB blob;
181 char *principal = NULL;
182 char *OIDs[ASN1_MAX_OIDS];
183#ifdef HAVE_KRB5
184 BOOL got_kerberos_mechanism = False;
185#endif
186
187 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
188
189 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
190 status = ADS_ERROR(rc);
191 goto failed;
192 }
193
194 blob = data_blob(scred->bv_val, scred->bv_len);
195
196 ber_bvfree(scred);
197
198#if 0
199 file_save("sasl_spnego.dat", blob.data, blob.length);
200#endif
201
202 /* the server sent us the first part of the SPNEGO exchange in the negprot
203 reply */
204 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
205 data_blob_free(&blob);
206 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
207 goto failed;
208 }
209 data_blob_free(&blob);
210
211 /* make sure the server understands kerberos */
212 for (i=0;OIDs[i];i++) {
213 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
214#ifdef HAVE_KRB5
215 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
216 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
217 got_kerberos_mechanism = True;
218 }
219#endif
220 free(OIDs[i]);
221 }
222 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
223
224#ifdef HAVE_KRB5
225 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
226 got_kerberos_mechanism)
227 {
228 /* I've seen a child Windows 2000 domain not send
229 the principal name back in the first round of
230 the SASL bind reply. So we guess based on server
231 name and realm. --jerry */
232 if ( !principal ) {
233 if ( ads->server.realm && ads->server.ldap_server ) {
234 char *server, *server_realm;
235
236 server = SMB_STRDUP( ads->server.ldap_server );
237 server_realm = SMB_STRDUP( ads->server.realm );
238
239 if ( !server || !server_realm )
240 return ADS_ERROR(LDAP_NO_MEMORY);
241
242 strlower_m( server );
243 strupper_m( server_realm );
244 asprintf( &principal, "ldap/%s@%s", server, server_realm );
245
246 SAFE_FREE( server );
247 SAFE_FREE( server_realm );
248
249 if ( !principal )
250 return ADS_ERROR(LDAP_NO_MEMORY);
251 }
252
253 }
254
255 status = ads_sasl_spnego_krb5_bind(ads, principal);
256 if (ADS_ERR_OK(status)) {
257 SAFE_FREE(principal);
258 return status;
259 }
260
261 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
262 "calling kinit\n", ads_errstr(status)));
263
264 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
265
266 if (ADS_ERR_OK(status)) {
267 status = ads_sasl_spnego_krb5_bind(ads, principal);
268 }
269
270 /* only fallback to NTLMSSP if allowed */
271 if (ADS_ERR_OK(status) ||
272 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
273 SAFE_FREE(principal);
274 return status;
275 }
276 }
277#endif
278
279 SAFE_FREE(principal);
280
281 /* lets do NTLMSSP ... this has the big advantage that we don't need
282 to sync clocks, and we don't rely on special versions of the krb5
283 library for HMAC_MD4 encryption */
284 return ads_sasl_spnego_ntlmssp_bind(ads);
285
286failed:
287 return status;
288}
289
290#ifdef HAVE_GSSAPI
291#define MAX_GSS_PASSES 3
292
293/* this performs a SASL/gssapi bind
294 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
295 is very dependent on correctly configured DNS whereas
296 this routine is much less fragile
297 see RFC2078 and RFC2222 for details
298*/
299static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
300{
301 uint32 minor_status;
302 gss_name_t serv_name;
303 gss_buffer_desc input_name;
304 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
305 gss_OID mech_type = GSS_C_NULL_OID;
306 gss_buffer_desc output_token, input_token;
307 uint32 ret_flags, conf_state;
308 struct berval cred;
309 struct berval *scred = NULL;
310 int i=0;
311 int gss_rc, rc;
312 uint8 *p;
313 uint32 max_msg_size = 0;
314 char *sname = NULL;
315 ADS_STATUS status;
316 krb5_principal principal = NULL;
317 krb5_context ctx = NULL;
318 krb5_enctype enc_types[] = {
319#ifdef ENCTYPE_ARCFOUR_HMAC
320 ENCTYPE_ARCFOUR_HMAC,
321#endif
322 ENCTYPE_DES_CBC_MD5,
323 ENCTYPE_NULL};
324 gss_OID_desc nt_principal =
325 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
326
327 /* we need to fetch a service ticket as the ldap user in the
328 servers realm, regardless of our realm */
329 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
330
331 initialize_krb5_error_table();
332 status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
333 if (!ADS_ERR_OK(status)) {
334 SAFE_FREE(sname);
335 return status;
336 }
337 status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
338 if (!ADS_ERR_OK(status)) {
339 SAFE_FREE(sname);
340 krb5_free_context(ctx);
341 return status;
342 }
343 status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
344 if (!ADS_ERR_OK(status)) {
345 SAFE_FREE(sname);
346 krb5_free_context(ctx);
347 return status;
348 }
349
350 input_name.value = &principal;
351 input_name.length = sizeof(principal);
352
353 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
354
355 /*
356 * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
357 * to point to the *address* of the krb5_principal, and the gss libraries
358 * to a shallow copy of the krb5_principal pointer - so we need to keep
359 * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
360 * Just one more way in which MIT engineers screwed me over.... JRA.
361 */
362
363 SAFE_FREE(sname);
364
365 if (gss_rc) {
366 krb5_free_principal(ctx, principal);
367 krb5_free_context(ctx);
368 return ADS_ERROR_GSS(gss_rc, minor_status);
369 }
370
371 input_token.value = NULL;
372 input_token.length = 0;
373
374 for (i=0; i < MAX_GSS_PASSES; i++) {
375 gss_rc = gss_init_sec_context(&minor_status,
376 GSS_C_NO_CREDENTIAL,
377 &context_handle,
378 serv_name,
379 mech_type,
380 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
381 0,
382 NULL,
383 &input_token,
384 NULL,
385 &output_token,
386 &ret_flags,
387 NULL);
388
389 if (input_token.value) {
390 gss_release_buffer(&minor_status, &input_token);
391 }
392
393 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
394 status = ADS_ERROR_GSS(gss_rc, minor_status);
395 goto failed;
396 }
397
398 cred.bv_val = (char *)output_token.value;
399 cred.bv_len = output_token.length;
400
401 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
402 &scred);
403 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
404 status = ADS_ERROR(rc);
405 goto failed;
406 }
407
408 if (output_token.value) {
409 gss_release_buffer(&minor_status, &output_token);
410 }
411
412 if (scred) {
413 input_token.value = scred->bv_val;
414 input_token.length = scred->bv_len;
415 } else {
416 input_token.value = NULL;
417 input_token.length = 0;
418 }
419
420 if (gss_rc == 0) break;
421 }
422
423 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
424 (int *)&conf_state,NULL);
425 if (gss_rc) {
426 status = ADS_ERROR_GSS(gss_rc, minor_status);
427 goto failed;
428 }
429
430 gss_release_buffer(&minor_status, &input_token);
431
432 p = (uint8 *)output_token.value;
433
434#if 0
435 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
436#endif
437
438 if (p) {
439 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
440 }
441
442 gss_release_buffer(&minor_status, &output_token);
443
444 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
445 p = (uint8 *)output_token.value;
446
447 *p++ = 1; /* no sign & seal selection */
448 /* choose the same size as the server gave us */
449 *p++ = max_msg_size>>16;
450 *p++ = max_msg_size>>8;
451 *p++ = max_msg_size;
452 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
453 p += strlen((const char *)p);
454
455 output_token.length = PTR_DIFF(p, output_token.value);
456
457 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
458 &output_token, (int *)&conf_state,
459 &input_token);
460 if (gss_rc) {
461 status = ADS_ERROR_GSS(gss_rc, minor_status);
462 goto failed;
463 }
464
465 free(output_token.value);
466
467 cred.bv_val = (char *)input_token.value;
468 cred.bv_len = input_token.length;
469
470 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
471 &scred);
472 status = ADS_ERROR(rc);
473
474 gss_release_buffer(&minor_status, &input_token);
475
476failed:
477
478 gss_release_name(&minor_status, &serv_name);
479 if (context_handle != GSS_C_NO_CONTEXT)
480 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
481 krb5_free_principal(ctx, principal);
482 krb5_free_context(ctx);
483
484 if(scred)
485 ber_bvfree(scred);
486 return status;
487}
488#endif /* HAVE_GGSAPI */
489
490/* mapping between SASL mechanisms and functions */
491static struct {
492 const char *name;
493 ADS_STATUS (*fn)(ADS_STRUCT *);
494} sasl_mechanisms[] = {
495 {"GSS-SPNEGO", ads_sasl_spnego_bind},
496#ifdef HAVE_GSSAPI
497 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
498#endif
499 {NULL, NULL}
500};
501
502ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
503{
504 const char *attrs[] = {"supportedSASLMechanisms", NULL};
505 char **values;
506 ADS_STATUS status;
507 int i, j;
508 LDAPMessage *res;
509
510 /* get a list of supported SASL mechanisms */
511 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
512 if (!ADS_ERR_OK(status)) return status;
513
514 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
515
516 /* try our supported mechanisms in order */
517 for (i=0;sasl_mechanisms[i].name;i++) {
518 /* see if the server supports it */
519 for (j=0;values && values[j];j++) {
520 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
521 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
522 status = sasl_mechanisms[i].fn(ads);
523 ldap_value_free(values);
524 ldap_msgfree(res);
525 return status;
526 }
527 }
528 }
529
530 ldap_value_free(values);
531 ldap_msgfree(res);
532 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
533}
534
535#endif /* HAVE_LDAP */
536
Note: See TracBrowser for help on using the repository browser.