r4231: commiting changes to 3.0.10
[Samba.git] / source / libads / sasl.c
blob97ba9c92862792cab182505a0e1a43cc650f14e6
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 #include "includes.h"
23 #ifdef HAVE_LDAP
25 /*
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
31 const char *mechs[] = {OID_NTLMSSP, NULL};
32 DATA_BLOB msg1 = data_blob(NULL, 0);
33 DATA_BLOB blob, chal1, chal2, auth;
34 uint8 challenge[8];
35 uint8 nthash[24], lmhash[24], sess_key[16];
36 uint32 neg_flags;
37 struct berval cred, *scred = NULL;
38 ADS_STATUS status;
39 int rc;
41 if (!ads->auth.password) {
42 /* No password, don't segfault below... */
43 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
46 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
47 NTLMSSP_NEGOTIATE_128 |
48 NTLMSSP_NEGOTIATE_NTLM;
50 memset(sess_key, 0, 16);
52 /* generate the ntlmssp negotiate packet */
53 msrpc_gen(&blob, "CddB",
54 "NTLMSSP",
55 NTLMSSP_NEGOTIATE,
56 neg_flags,
57 sess_key, 16);
59 /* and wrap it in a SPNEGO wrapper */
60 msg1 = gen_negTokenTarg(mechs, blob);
61 data_blob_free(&blob);
63 cred.bv_val = (char *)msg1.data;
64 cred.bv_len = msg1.length;
66 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
67 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
68 status = ADS_ERROR(rc);
69 goto failed;
72 blob = data_blob(scred->bv_val, scred->bv_len);
73 ber_bvfree(scred);
75 /* the server gives us back two challenges */
76 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
77 DEBUG(3,("Failed to parse challenges\n"));
78 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
79 goto failed;
82 data_blob_free(&blob);
84 /* encrypt the password with the challenge */
85 memcpy(challenge, chal1.data + 24, 8);
86 SMBencrypt(ads->auth.password, challenge,lmhash);
87 SMBNTencrypt(ads->auth.password, challenge,nthash);
89 data_blob_free(&chal1);
90 data_blob_free(&chal2);
92 /* this generates the actual auth packet */
93 msrpc_gen(&blob, "CdBBUUUBd",
94 "NTLMSSP",
95 NTLMSSP_AUTH,
96 lmhash, 24,
97 nthash, 24,
98 lp_workgroup(),
99 ads->auth.user_name,
100 global_myname(),
101 sess_key, 16,
102 neg_flags);
104 /* wrap it in SPNEGO */
105 auth = spnego_gen_auth(blob);
107 data_blob_free(&blob);
109 /* Remember to free the msg1 blob. The contents of this
110 have been copied into cred and need freeing before reassignment. */
111 data_blob_free(&msg1);
113 /* now send the auth packet and we should be done */
114 cred.bv_val = (char *)auth.data;
115 cred.bv_len = auth.length;
117 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
119 ber_bvfree(scred);
120 data_blob_free(&auth);
122 return ADS_ERROR(rc);
124 failed:
126 /* Remember to free the msg1 blob. The contents of this
127 have been copied into cred and need freeing. */
128 data_blob_free(&msg1);
130 if(scred)
131 ber_bvfree(scred);
132 return status;
136 perform a LDAP/SASL/SPNEGO/KRB5 bind
138 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
140 DATA_BLOB blob = data_blob(NULL, 0);
141 struct berval cred, *scred = NULL;
142 DATA_BLOB session_key = data_blob(NULL, 0);
143 int rc;
145 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
147 if (rc) {
148 return ADS_ERROR_KRB5(rc);
151 /* now send the auth packet and we should be done */
152 cred.bv_val = (char *)blob.data;
153 cred.bv_len = blob.length;
155 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
157 data_blob_free(&blob);
158 data_blob_free(&session_key);
159 if(scred)
160 ber_bvfree(scred);
162 return ADS_ERROR(rc);
166 this performs a SASL/SPNEGO bind
168 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
170 struct berval *scred=NULL;
171 int rc, i;
172 ADS_STATUS status;
173 DATA_BLOB blob;
174 char *principal = NULL;
175 char *OIDs[ASN1_MAX_OIDS];
176 BOOL got_kerberos_mechanism = False;
178 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
180 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
181 status = ADS_ERROR(rc);
182 goto failed;
185 blob = data_blob(scred->bv_val, scred->bv_len);
187 ber_bvfree(scred);
189 #if 0
190 file_save("sasl_spnego.dat", blob.data, blob.length);
191 #endif
193 /* the server sent us the first part of the SPNEGO exchange in the negprot
194 reply */
195 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
196 data_blob_free(&blob);
197 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
198 goto failed;
200 data_blob_free(&blob);
202 /* make sure the server understands kerberos */
203 for (i=0;OIDs[i];i++) {
204 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
205 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
206 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
207 got_kerberos_mechanism = True;
209 free(OIDs[i]);
211 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
213 #ifdef HAVE_KRB5
214 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
215 got_kerberos_mechanism) {
216 status = ads_sasl_spnego_krb5_bind(ads, principal);
217 if (ADS_ERR_OK(status)) {
218 SAFE_FREE(principal);
219 return status;
222 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
224 if (ADS_ERR_OK(status)) {
225 status = ads_sasl_spnego_krb5_bind(ads, principal);
228 /* only fallback to NTLMSSP if allowed */
229 if (ADS_ERR_OK(status) ||
230 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
231 SAFE_FREE(principal);
232 return status;
235 #endif
237 SAFE_FREE(principal);
239 /* lets do NTLMSSP ... this has the big advantage that we don't need
240 to sync clocks, and we don't rely on special versions of the krb5
241 library for HMAC_MD4 encryption */
242 return ads_sasl_spnego_ntlmssp_bind(ads);
244 failed:
245 return status;
248 #ifdef HAVE_GSSAPI
249 #define MAX_GSS_PASSES 3
251 /* this performs a SASL/gssapi bind
252 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
253 is very dependent on correctly configured DNS whereas
254 this routine is much less fragile
255 see RFC2078 and RFC2222 for details
257 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
259 uint32 minor_status;
260 gss_name_t serv_name;
261 gss_buffer_desc input_name;
262 gss_ctx_id_t context_handle;
263 gss_OID mech_type = GSS_C_NULL_OID;
264 gss_buffer_desc output_token, input_token;
265 uint32 ret_flags, conf_state;
266 struct berval cred;
267 struct berval *scred = NULL;
268 int i=0;
269 int gss_rc, rc;
270 uint8 *p;
271 uint32 max_msg_size;
272 char *sname;
273 unsigned sec_layer;
274 ADS_STATUS status;
275 krb5_principal principal;
276 krb5_context ctx = NULL;
277 krb5_enctype enc_types[] = {
278 #ifdef ENCTYPE_ARCFOUR_HMAC
279 ENCTYPE_ARCFOUR_HMAC,
280 #endif
281 ENCTYPE_DES_CBC_MD5,
282 ENCTYPE_NULL};
283 gss_OID_desc nt_principal =
284 {10, "\052\206\110\206\367\022\001\002\002\002"};
286 /* we need to fetch a service ticket as the ldap user in the
287 servers realm, regardless of our realm */
288 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
289 krb5_init_context(&ctx);
290 krb5_set_default_tgs_ktypes(ctx, enc_types);
291 krb5_parse_name(ctx, sname, &principal);
292 free(sname);
293 krb5_free_context(ctx);
295 input_name.value = &principal;
296 input_name.length = sizeof(principal);
298 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
299 if (gss_rc) {
300 return ADS_ERROR_GSS(gss_rc, minor_status);
303 context_handle = GSS_C_NO_CONTEXT;
305 input_token.value = NULL;
306 input_token.length = 0;
308 for (i=0; i < MAX_GSS_PASSES; i++) {
309 gss_rc = gss_init_sec_context(&minor_status,
310 GSS_C_NO_CREDENTIAL,
311 &context_handle,
312 serv_name,
313 mech_type,
314 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
316 NULL,
317 &input_token,
318 NULL,
319 &output_token,
320 &ret_flags,
321 NULL);
323 if (input_token.value) {
324 gss_release_buffer(&minor_status, &input_token);
327 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
328 status = ADS_ERROR_GSS(gss_rc, minor_status);
329 goto failed;
332 cred.bv_val = output_token.value;
333 cred.bv_len = output_token.length;
335 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
336 &scred);
337 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
338 status = ADS_ERROR(rc);
339 goto failed;
342 if (output_token.value) {
343 gss_release_buffer(&minor_status, &output_token);
346 if (scred) {
347 input_token.value = scred->bv_val;
348 input_token.length = scred->bv_len;
349 } else {
350 input_token.value = NULL;
351 input_token.length = 0;
354 if (gss_rc == 0) break;
357 gss_release_name(&minor_status, &serv_name);
359 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
360 (int *)&conf_state,NULL);
361 if (gss_rc) {
362 status = ADS_ERROR_GSS(gss_rc, minor_status);
363 goto failed;
366 gss_release_buffer(&minor_status, &input_token);
368 p = (uint8 *)output_token.value;
370 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
372 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
373 sec_layer = *p;
375 gss_release_buffer(&minor_status, &output_token);
377 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
378 p = output_token.value;
380 *p++ = 1; /* no sign & seal selection */
381 /* choose the same size as the server gave us */
382 *p++ = max_msg_size>>16;
383 *p++ = max_msg_size>>8;
384 *p++ = max_msg_size;
385 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
386 p += strlen((const char *)p);
388 output_token.length = PTR_DIFF(p, output_token.value);
390 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
391 &output_token, (int *)&conf_state,
392 &input_token);
393 if (gss_rc) {
394 status = ADS_ERROR_GSS(gss_rc, minor_status);
395 goto failed;
398 free(output_token.value);
400 cred.bv_val = input_token.value;
401 cred.bv_len = input_token.length;
403 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
404 &scred);
405 status = ADS_ERROR(rc);
407 gss_release_buffer(&minor_status, &input_token);
409 failed:
410 if(scred)
411 ber_bvfree(scred);
412 return status;
414 #endif
416 /* mapping between SASL mechanisms and functions */
417 static struct {
418 const char *name;
419 ADS_STATUS (*fn)(ADS_STRUCT *);
420 } sasl_mechanisms[] = {
421 {"GSS-SPNEGO", ads_sasl_spnego_bind},
422 #ifdef HAVE_GSSAPI
423 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
424 #endif
425 {NULL, NULL}
428 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
430 const char *attrs[] = {"supportedSASLMechanisms", NULL};
431 char **values;
432 ADS_STATUS status;
433 int i, j;
434 void *res;
436 /* get a list of supported SASL mechanisms */
437 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
438 if (!ADS_ERR_OK(status)) return status;
440 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
442 /* try our supported mechanisms in order */
443 for (i=0;sasl_mechanisms[i].name;i++) {
444 /* see if the server supports it */
445 for (j=0;values && values[j];j++) {
446 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
447 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
448 status = sasl_mechanisms[i].fn(ads);
449 ldap_value_free(values);
450 ldap_msgfree(res);
451 return status;
456 ldap_value_free(values);
457 ldap_msgfree(res);
458 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
461 #endif