r10656: BIG merge from trunk. Features not copied over
[Samba/nascimento.git] / source3 / libads / sasl.c
blob72cbf7264ec964bd22d74c06f546464ee2a29572
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, 0);
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 #ifdef HAVE_KRB5
177 BOOL got_kerberos_mechanism = False;
178 #endif
180 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
182 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
183 status = ADS_ERROR(rc);
184 goto failed;
187 blob = data_blob(scred->bv_val, scred->bv_len);
189 ber_bvfree(scred);
191 #if 0
192 file_save("sasl_spnego.dat", blob.data, blob.length);
193 #endif
195 /* the server sent us the first part of the SPNEGO exchange in the negprot
196 reply */
197 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
198 data_blob_free(&blob);
199 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
200 goto failed;
202 data_blob_free(&blob);
204 /* make sure the server understands kerberos */
205 for (i=0;OIDs[i];i++) {
206 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
207 #ifdef HAVE_KRB5
208 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
209 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
210 got_kerberos_mechanism = True;
212 #endif
213 free(OIDs[i]);
215 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
217 #ifdef HAVE_KRB5
218 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
219 got_kerberos_mechanism) {
220 status = ads_sasl_spnego_krb5_bind(ads, principal);
221 if (ADS_ERR_OK(status)) {
222 SAFE_FREE(principal);
223 return status;
226 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
228 if (ADS_ERR_OK(status)) {
229 status = ads_sasl_spnego_krb5_bind(ads, principal);
232 /* only fallback to NTLMSSP if allowed */
233 if (ADS_ERR_OK(status) ||
234 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
235 SAFE_FREE(principal);
236 return status;
239 #endif
241 SAFE_FREE(principal);
243 /* lets do NTLMSSP ... this has the big advantage that we don't need
244 to sync clocks, and we don't rely on special versions of the krb5
245 library for HMAC_MD4 encryption */
246 return ads_sasl_spnego_ntlmssp_bind(ads);
248 failed:
249 return status;
252 #ifdef HAVE_GSSAPI
253 #define MAX_GSS_PASSES 3
255 /* this performs a SASL/gssapi bind
256 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
257 is very dependent on correctly configured DNS whereas
258 this routine is much less fragile
259 see RFC2078 and RFC2222 for details
261 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
263 uint32 minor_status;
264 gss_name_t serv_name;
265 gss_buffer_desc input_name;
266 gss_ctx_id_t context_handle;
267 gss_OID mech_type = GSS_C_NULL_OID;
268 gss_buffer_desc output_token, input_token;
269 uint32 ret_flags, conf_state;
270 struct berval cred;
271 struct berval *scred = NULL;
272 int i=0;
273 int gss_rc, rc;
274 uint8 *p;
275 uint32 max_msg_size;
276 char *sname;
277 unsigned sec_layer;
278 ADS_STATUS status;
279 krb5_principal principal;
280 krb5_context ctx = NULL;
281 krb5_enctype enc_types[] = {
282 #ifdef ENCTYPE_ARCFOUR_HMAC
283 ENCTYPE_ARCFOUR_HMAC,
284 #endif
285 ENCTYPE_DES_CBC_MD5,
286 ENCTYPE_NULL};
287 gss_OID_desc nt_principal =
288 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
290 /* we need to fetch a service ticket as the ldap user in the
291 servers realm, regardless of our realm */
292 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
293 krb5_init_context(&ctx);
294 krb5_set_default_tgs_ktypes(ctx, enc_types);
295 krb5_parse_name(ctx, sname, &principal);
296 free(sname);
297 krb5_free_context(ctx);
299 input_name.value = &principal;
300 input_name.length = sizeof(principal);
302 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
303 if (gss_rc) {
304 return ADS_ERROR_GSS(gss_rc, minor_status);
307 context_handle = GSS_C_NO_CONTEXT;
309 input_token.value = NULL;
310 input_token.length = 0;
312 for (i=0; i < MAX_GSS_PASSES; i++) {
313 gss_rc = gss_init_sec_context(&minor_status,
314 GSS_C_NO_CREDENTIAL,
315 &context_handle,
316 serv_name,
317 mech_type,
318 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
320 NULL,
321 &input_token,
322 NULL,
323 &output_token,
324 &ret_flags,
325 NULL);
327 if (input_token.value) {
328 gss_release_buffer(&minor_status, &input_token);
331 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
332 status = ADS_ERROR_GSS(gss_rc, minor_status);
333 goto failed;
336 cred.bv_val = output_token.value;
337 cred.bv_len = output_token.length;
339 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
340 &scred);
341 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
342 status = ADS_ERROR(rc);
343 goto failed;
346 if (output_token.value) {
347 gss_release_buffer(&minor_status, &output_token);
350 if (scred) {
351 input_token.value = scred->bv_val;
352 input_token.length = scred->bv_len;
353 } else {
354 input_token.value = NULL;
355 input_token.length = 0;
358 if (gss_rc == 0) break;
361 gss_release_name(&minor_status, &serv_name);
363 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
364 (int *)&conf_state,NULL);
365 if (gss_rc) {
366 status = ADS_ERROR_GSS(gss_rc, minor_status);
367 goto failed;
370 gss_release_buffer(&minor_status, &input_token);
372 p = (uint8 *)output_token.value;
374 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
376 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
377 sec_layer = *p;
379 gss_release_buffer(&minor_status, &output_token);
381 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
382 p = output_token.value;
384 *p++ = 1; /* no sign & seal selection */
385 /* choose the same size as the server gave us */
386 *p++ = max_msg_size>>16;
387 *p++ = max_msg_size>>8;
388 *p++ = max_msg_size;
389 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
390 p += strlen((const char *)p);
392 output_token.length = PTR_DIFF(p, output_token.value);
394 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
395 &output_token, (int *)&conf_state,
396 &input_token);
397 if (gss_rc) {
398 status = ADS_ERROR_GSS(gss_rc, minor_status);
399 goto failed;
402 free(output_token.value);
404 cred.bv_val = input_token.value;
405 cred.bv_len = input_token.length;
407 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
408 &scred);
409 status = ADS_ERROR(rc);
411 gss_release_buffer(&minor_status, &input_token);
413 failed:
414 if(scred)
415 ber_bvfree(scred);
416 return status;
418 #endif
420 /* mapping between SASL mechanisms and functions */
421 static struct {
422 const char *name;
423 ADS_STATUS (*fn)(ADS_STRUCT *);
424 } sasl_mechanisms[] = {
425 {"GSS-SPNEGO", ads_sasl_spnego_bind},
426 #ifdef HAVE_GSSAPI
427 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
428 #endif
429 {NULL, NULL}
432 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
434 const char *attrs[] = {"supportedSASLMechanisms", NULL};
435 char **values;
436 ADS_STATUS status;
437 int i, j;
438 void *res;
440 /* get a list of supported SASL mechanisms */
441 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
442 if (!ADS_ERR_OK(status)) return status;
444 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
446 /* try our supported mechanisms in order */
447 for (i=0;sasl_mechanisms[i].name;i++) {
448 /* see if the server supports it */
449 for (j=0;values && values[j];j++) {
450 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
451 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
452 status = sasl_mechanisms[i].fn(ads);
453 ldap_value_free(values);
454 ldap_msgfree(res);
455 return status;
460 ldap_value_free(values);
461 ldap_msgfree(res);
462 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
465 #endif