r6369: update release notes
[Samba.git] / source / libads / sasl.c
blobe657f2114e67a7063cde0d06c9ce7780ca61ccea
1 /*
2 Unix SMB/CIFS implementation.
3 ads sasl code
4 Copyright (C) Andrew Tridgell 2001
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.
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.
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.
21 #define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */
23 #include "includes.h"
25 #ifdef HAVE_LDAP
27 /*
28 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
29 we fit on one socket??)
31 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
33 const char *mechs[] = {OID_NTLMSSP, NULL};
34 DATA_BLOB msg1 = data_blob(NULL, 0);
35 DATA_BLOB blob, chal1, chal2, auth;
36 uint8 challenge[8];
37 uint8 nthash[24], lmhash[24], sess_key[16];
38 uint32 neg_flags;
39 struct berval cred, *scred = NULL;
40 ADS_STATUS status;
41 int rc;
43 if (!ads->auth.password) {
44 /* No password, don't segfault below... */
45 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
48 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
49 NTLMSSP_NEGOTIATE_128 |
50 NTLMSSP_NEGOTIATE_NTLM;
52 memset(sess_key, 0, 16);
54 /* generate the ntlmssp negotiate packet */
55 msrpc_gen(&blob, "CddB",
56 "NTLMSSP",
57 NTLMSSP_NEGOTIATE,
58 neg_flags,
59 sess_key, 16);
61 /* and wrap it in a SPNEGO wrapper */
62 msg1 = gen_negTokenTarg(mechs, blob);
63 data_blob_free(&blob);
65 cred.bv_val = (char *)msg1.data;
66 cred.bv_len = msg1.length;
68 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
69 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
70 status = ADS_ERROR(rc);
71 goto failed;
74 blob = data_blob(scred->bv_val, scred->bv_len);
75 ber_bvfree(scred);
77 /* the server gives us back two challenges */
78 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
79 DEBUG(3,("Failed to parse challenges\n"));
80 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
81 goto failed;
84 data_blob_free(&blob);
86 /* encrypt the password with the challenge */
87 memcpy(challenge, chal1.data + 24, 8);
88 SMBencrypt(ads->auth.password, challenge,lmhash);
89 SMBNTencrypt(ads->auth.password, challenge,nthash);
91 data_blob_free(&chal1);
92 data_blob_free(&chal2);
94 /* this generates the actual auth packet */
95 msrpc_gen(&blob, "CdBBUUUBd",
96 "NTLMSSP",
97 NTLMSSP_AUTH,
98 lmhash, 24,
99 nthash, 24,
100 lp_workgroup(),
101 ads->auth.user_name,
102 global_myname(),
103 sess_key, 16,
104 neg_flags);
106 /* wrap it in SPNEGO */
107 auth = spnego_gen_auth(blob);
109 data_blob_free(&blob);
111 /* Remember to free the msg1 blob. The contents of this
112 have been copied into cred and need freeing before reassignment. */
113 data_blob_free(&msg1);
115 /* now send the auth packet and we should be done */
116 cred.bv_val = (char *)auth.data;
117 cred.bv_len = auth.length;
119 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
121 ber_bvfree(scred);
122 data_blob_free(&auth);
124 return ADS_ERROR(rc);
126 failed:
128 /* Remember to free the msg1 blob. The contents of this
129 have been copied into cred and need freeing. */
130 data_blob_free(&msg1);
132 if(scred)
133 ber_bvfree(scred);
134 return status;
138 perform a LDAP/SASL/SPNEGO/KRB5 bind
140 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
142 DATA_BLOB blob = data_blob(NULL, 0);
143 struct berval cred, *scred = NULL;
144 DATA_BLOB session_key = data_blob(NULL, 0);
145 int rc;
147 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
149 if (rc) {
150 return ADS_ERROR_KRB5(rc);
153 /* now send the auth packet and we should be done */
154 cred.bv_val = (char *)blob.data;
155 cred.bv_len = blob.length;
157 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
159 data_blob_free(&blob);
160 data_blob_free(&session_key);
161 if(scred)
162 ber_bvfree(scred);
164 return ADS_ERROR(rc);
168 this performs a SASL/SPNEGO bind
170 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
172 struct berval *scred=NULL;
173 int rc, i;
174 ADS_STATUS status;
175 DATA_BLOB blob;
176 char *principal = NULL;
177 char *OIDs[ASN1_MAX_OIDS];
178 #ifdef HAVE_KRB5
179 BOOL got_kerberos_mechanism = False;
180 #endif
182 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
184 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
185 status = ADS_ERROR(rc);
186 goto failed;
189 blob = data_blob(scred->bv_val, scred->bv_len);
191 ber_bvfree(scred);
193 #if 0
194 file_save("sasl_spnego.dat", blob.data, blob.length);
195 #endif
197 /* the server sent us the first part of the SPNEGO exchange in the negprot
198 reply */
199 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
200 data_blob_free(&blob);
201 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
202 goto failed;
204 data_blob_free(&blob);
206 /* make sure the server understands kerberos */
207 for (i=0;OIDs[i];i++) {
208 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
209 #ifdef HAVE_KRB5
210 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
211 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
212 got_kerberos_mechanism = True;
214 #endif
215 free(OIDs[i]);
217 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
219 #ifdef HAVE_KRB5
220 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
221 got_kerberos_mechanism) {
222 status = ads_sasl_spnego_krb5_bind(ads, principal);
223 if (ADS_ERR_OK(status)) {
224 SAFE_FREE(principal);
225 return status;
228 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
230 if (ADS_ERR_OK(status)) {
231 status = ads_sasl_spnego_krb5_bind(ads, principal);
234 /* only fallback to NTLMSSP if allowed */
235 if (ADS_ERR_OK(status) ||
236 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
237 SAFE_FREE(principal);
238 return status;
241 #endif
243 SAFE_FREE(principal);
245 /* lets do NTLMSSP ... this has the big advantage that we don't need
246 to sync clocks, and we don't rely on special versions of the krb5
247 library for HMAC_MD4 encryption */
248 return ads_sasl_spnego_ntlmssp_bind(ads);
250 failed:
251 return status;
254 #ifdef HAVE_GSSAPI
255 #define MAX_GSS_PASSES 3
257 /* this performs a SASL/gssapi bind
258 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
259 is very dependent on correctly configured DNS whereas
260 this routine is much less fragile
261 see RFC2078 and RFC2222 for details
263 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
265 uint32 minor_status;
266 gss_name_t serv_name;
267 gss_buffer_desc input_name;
268 gss_ctx_id_t context_handle;
269 gss_OID mech_type = GSS_C_NULL_OID;
270 gss_buffer_desc output_token, input_token;
271 uint32 ret_flags, conf_state;
272 struct berval cred;
273 struct berval *scred = NULL;
274 int i=0;
275 int gss_rc, rc;
276 uint8 *p;
277 uint32 max_msg_size;
278 char *sname;
279 unsigned sec_layer;
280 ADS_STATUS status;
281 krb5_principal principal;
282 krb5_context ctx = NULL;
283 krb5_enctype enc_types[] = {
284 #ifdef ENCTYPE_ARCFOUR_HMAC
285 ENCTYPE_ARCFOUR_HMAC,
286 #endif
287 ENCTYPE_DES_CBC_MD5,
288 ENCTYPE_NULL};
289 gss_OID_desc nt_principal =
290 {10, CONST_DISCARD(char *,
291 "\052\206\110\206\367\022\001\002\002\002")};
293 /* we need to fetch a service ticket as the ldap user in the
294 servers realm, regardless of our realm */
295 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
296 krb5_init_context(&ctx);
297 krb5_set_default_tgs_ktypes(ctx, enc_types);
298 krb5_parse_name(ctx, sname, &principal);
299 free(sname);
300 krb5_free_context(ctx);
302 input_name.value = &principal;
303 input_name.length = sizeof(principal);
305 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
306 if (gss_rc) {
307 return ADS_ERROR_GSS(gss_rc, minor_status);
310 context_handle = GSS_C_NO_CONTEXT;
312 input_token.value = NULL;
313 input_token.length = 0;
315 for (i=0; i < MAX_GSS_PASSES; i++) {
316 gss_rc = gss_init_sec_context(&minor_status,
317 GSS_C_NO_CREDENTIAL,
318 &context_handle,
319 serv_name,
320 mech_type,
321 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
323 NULL,
324 &input_token,
325 NULL,
326 &output_token,
327 &ret_flags,
328 NULL);
330 if (input_token.value) {
331 gss_release_buffer(&minor_status, &input_token);
334 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
335 status = ADS_ERROR_GSS(gss_rc, minor_status);
336 goto failed;
339 cred.bv_val = output_token.value;
340 cred.bv_len = output_token.length;
342 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
343 &scred);
344 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
345 status = ADS_ERROR(rc);
346 goto failed;
349 if (output_token.value) {
350 gss_release_buffer(&minor_status, &output_token);
353 if (scred) {
354 input_token.value = scred->bv_val;
355 input_token.length = scred->bv_len;
356 } else {
357 input_token.value = NULL;
358 input_token.length = 0;
361 if (gss_rc == 0) break;
364 gss_release_name(&minor_status, &serv_name);
366 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
367 (int *)&conf_state,NULL);
368 if (gss_rc) {
369 status = ADS_ERROR_GSS(gss_rc, minor_status);
370 goto failed;
373 gss_release_buffer(&minor_status, &input_token);
375 p = (uint8 *)output_token.value;
377 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
379 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
380 sec_layer = *p;
382 gss_release_buffer(&minor_status, &output_token);
384 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
385 p = output_token.value;
387 *p++ = 1; /* no sign & seal selection */
388 /* choose the same size as the server gave us */
389 *p++ = max_msg_size>>16;
390 *p++ = max_msg_size>>8;
391 *p++ = max_msg_size;
392 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
393 p += strlen((const char *)p);
395 output_token.length = PTR_DIFF(p, output_token.value);
397 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
398 &output_token, (int *)&conf_state,
399 &input_token);
400 if (gss_rc) {
401 status = ADS_ERROR_GSS(gss_rc, minor_status);
402 goto failed;
405 free(output_token.value);
407 cred.bv_val = input_token.value;
408 cred.bv_len = input_token.length;
410 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
411 &scred);
412 status = ADS_ERROR(rc);
414 gss_release_buffer(&minor_status, &input_token);
416 failed:
417 if(scred)
418 ber_bvfree(scred);
419 return status;
421 #endif
423 /* mapping between SASL mechanisms and functions */
424 static struct {
425 const char *name;
426 ADS_STATUS (*fn)(ADS_STRUCT *);
427 } sasl_mechanisms[] = {
428 {"GSS-SPNEGO", ads_sasl_spnego_bind},
429 #ifdef HAVE_GSSAPI
430 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
431 #endif
432 {NULL, NULL}
435 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
437 const char *attrs[] = {"supportedSASLMechanisms", NULL};
438 char **values;
439 ADS_STATUS status;
440 int i, j;
441 void *res;
443 /* get a list of supported SASL mechanisms */
444 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
445 if (!ADS_ERR_OK(status)) return status;
447 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
449 /* try our supported mechanisms in order */
450 for (i=0;sasl_mechanisms[i].name;i++) {
451 /* see if the server supports it */
452 for (j=0;values && values[j];j++) {
453 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
454 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
455 status = sasl_mechanisms[i].fn(ads);
456 ldap_value_free(values);
457 ldap_msgfree(res);
458 return status;
463 ldap_value_free(values);
464 ldap_msgfree(res);
465 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
468 #endif