make sure to call get_user_groups() with the full winbindd name for a user if he...
[Samba/gebeck_regimport.git] / source3 / libads / sasl.c
blob51228035974b1036bb1c8486c6edf4ebff5ea1a5
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;
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;
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);
74 /* the server gives us back two challenges */
75 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
76 DEBUG(3,("Failed to parse challenges\n"));
77 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
78 goto failed;
81 data_blob_free(&blob);
83 /* encrypt the password with the challenge */
84 memcpy(challenge, chal1.data + 24, 8);
85 SMBencrypt(ads->auth.password, challenge,lmhash);
86 SMBNTencrypt(ads->auth.password, challenge,nthash);
88 data_blob_free(&chal1);
89 data_blob_free(&chal2);
91 /* this generates the actual auth packet */
92 msrpc_gen(&blob, "CdBBUUUBd",
93 "NTLMSSP",
94 NTLMSSP_AUTH,
95 lmhash, 24,
96 nthash, 24,
97 lp_workgroup(),
98 ads->auth.user_name,
99 global_myname(),
100 sess_key, 16,
101 neg_flags);
103 /* wrap it in SPNEGO */
104 auth = spnego_gen_auth(blob);
106 data_blob_free(&blob);
108 /* now send the auth packet and we should be done */
109 cred.bv_val = (char *)auth.data;
110 cred.bv_len = auth.length;
112 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
114 return ADS_ERROR(rc);
116 failed:
117 return status;
121 perform a LDAP/SASL/SPNEGO/KRB5 bind
123 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
125 DATA_BLOB blob;
126 struct berval cred, *scred;
127 unsigned char sk[16];
128 int rc;
130 blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, sk);
132 if (!blob.data) {
133 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
136 /* now send the auth packet and we should be done */
137 cred.bv_val = (char *)blob.data;
138 cred.bv_len = blob.length;
140 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
142 data_blob_free(&blob);
144 return ADS_ERROR(rc);
148 this performs a SASL/SPNEGO bind
150 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
152 struct berval *scred=NULL;
153 int rc, i;
154 ADS_STATUS status;
155 DATA_BLOB blob;
156 char *principal;
157 char *OIDs[ASN1_MAX_OIDS];
158 BOOL got_kerberos_mechanism = False;
160 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
162 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
163 status = ADS_ERROR(rc);
164 goto failed;
167 blob = data_blob(scred->bv_val, scred->bv_len);
169 #if 0
170 file_save("sasl_spnego.dat", blob.data, blob.length);
171 #endif
173 /* the server sent us the first part of the SPNEGO exchange in the negprot
174 reply */
175 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
176 data_blob_free(&blob);
177 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
178 goto failed;
180 data_blob_free(&blob);
182 /* make sure the server understands kerberos */
183 for (i=0;OIDs[i];i++) {
184 DEBUG(3,("got OID=%s\n", OIDs[i]));
185 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
186 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
187 got_kerberos_mechanism = True;
189 free(OIDs[i]);
191 DEBUG(3,("got principal=%s\n", principal));
193 #ifdef HAVE_KRB5
194 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
195 got_kerberos_mechanism) {
196 status = ads_sasl_spnego_krb5_bind(ads, principal);
197 if (ADS_ERR_OK(status))
198 return status;
199 if (ads_kinit_password(ads) == 0) {
200 status = ads_sasl_spnego_krb5_bind(ads, principal);
202 /* only fallback to NTLMSSP if allowed */
203 if (ADS_ERR_OK(status) ||
204 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
205 return status;
208 #endif
210 /* lets do NTLMSSP ... this has the big advantage that we don't need
211 to sync clocks, and we don't rely on special versions of the krb5
212 library for HMAC_MD4 encryption */
213 return ads_sasl_spnego_ntlmssp_bind(ads);
215 failed:
216 return status;
219 #ifdef HAVE_GSSAPI
220 #define MAX_GSS_PASSES 3
222 /* this performs a SASL/gssapi bind
223 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
224 is very dependent on correctly configured DNS whereas
225 this routine is much less fragile
226 see RFC2078 and RFC2222 for details
228 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
230 uint32 minor_status;
231 gss_name_t serv_name;
232 gss_buffer_desc input_name;
233 gss_ctx_id_t context_handle;
234 gss_OID mech_type = GSS_C_NULL_OID;
235 gss_buffer_desc output_token, input_token;
236 uint32 ret_flags, conf_state;
237 struct berval cred;
238 struct berval *scred;
239 int i=0;
240 int gss_rc, rc;
241 uint8 *p;
242 uint32 max_msg_size;
243 char *sname;
244 unsigned sec_layer;
245 ADS_STATUS status;
246 krb5_principal principal;
247 krb5_context ctx;
248 krb5_enctype enc_types[] = {
249 #ifdef ENCTYPE_ARCFOUR_HMAC
250 ENCTYPE_ARCFOUR_HMAC,
251 #endif
252 ENCTYPE_DES_CBC_MD5,
253 ENCTYPE_NULL};
254 gss_OID_desc nt_principal =
255 {10, "\052\206\110\206\367\022\001\002\002\002"};
257 /* we need to fetch a service ticket as the ldap user in the
258 servers realm, regardless of our realm */
259 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
260 krb5_init_context(&ctx);
261 krb5_set_default_tgs_ktypes(ctx, enc_types);
262 krb5_parse_name(ctx, sname, &principal);
263 free(sname);
264 krb5_free_context(ctx);
266 input_name.value = &principal;
267 input_name.length = sizeof(principal);
269 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
270 if (gss_rc) {
271 return ADS_ERROR_GSS(gss_rc, minor_status);
274 context_handle = GSS_C_NO_CONTEXT;
276 input_token.value = NULL;
277 input_token.length = 0;
279 for (i=0; i < MAX_GSS_PASSES; i++) {
280 gss_rc = gss_init_sec_context(&minor_status,
281 GSS_C_NO_CREDENTIAL,
282 &context_handle,
283 serv_name,
284 mech_type,
285 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
287 NULL,
288 &input_token,
289 NULL,
290 &output_token,
291 &ret_flags,
292 NULL);
294 if (input_token.value) {
295 gss_release_buffer(&minor_status, &input_token);
298 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
299 status = ADS_ERROR_GSS(gss_rc, minor_status);
300 goto failed;
303 cred.bv_val = output_token.value;
304 cred.bv_len = output_token.length;
306 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
307 &scred);
308 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
309 status = ADS_ERROR(rc);
310 goto failed;
313 if (output_token.value) {
314 gss_release_buffer(&minor_status, &output_token);
317 if (scred) {
318 input_token.value = scred->bv_val;
319 input_token.length = scred->bv_len;
320 } else {
321 input_token.value = NULL;
322 input_token.length = 0;
325 if (gss_rc == 0) break;
328 gss_release_name(&minor_status, &serv_name);
330 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
331 (int *)&conf_state,NULL);
332 if (gss_rc) {
333 status = ADS_ERROR_GSS(gss_rc, minor_status);
334 goto failed;
337 gss_release_buffer(&minor_status, &input_token);
339 p = (uint8 *)output_token.value;
341 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
343 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
344 sec_layer = *p;
346 gss_release_buffer(&minor_status, &output_token);
348 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
349 p = output_token.value;
351 *p++ = 1; /* no sign & seal selection */
352 /* choose the same size as the server gave us */
353 *p++ = max_msg_size>>16;
354 *p++ = max_msg_size>>8;
355 *p++ = max_msg_size;
356 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
357 p += strlen((const char *)p);
359 output_token.length = PTR_DIFF(p, output_token.value);
361 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
362 &output_token, (int *)&conf_state,
363 &input_token);
364 if (gss_rc) {
365 status = ADS_ERROR_GSS(gss_rc, minor_status);
366 goto failed;
369 free(output_token.value);
371 cred.bv_val = input_token.value;
372 cred.bv_len = input_token.length;
374 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
375 &scred);
376 status = ADS_ERROR(rc);
378 gss_release_buffer(&minor_status, &input_token);
380 failed:
381 return status;
383 #endif
385 /* mapping between SASL mechanisms and functions */
386 static struct {
387 const char *name;
388 ADS_STATUS (*fn)(ADS_STRUCT *);
389 } sasl_mechanisms[] = {
390 {"GSS-SPNEGO", ads_sasl_spnego_bind},
391 #ifdef HAVE_GSSAPI
392 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
393 #endif
394 {NULL, NULL}
397 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
399 const char *attrs[] = {"supportedSASLMechanisms", NULL};
400 char **values;
401 ADS_STATUS status;
402 int i, j;
403 void *res;
405 /* get a list of supported SASL mechanisms */
406 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
407 if (!ADS_ERR_OK(status)) return status;
409 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
411 /* try our supported mechanisms in order */
412 for (i=0;sasl_mechanisms[i].name;i++) {
413 /* see if the server supports it */
414 for (j=0;values && values[j];j++) {
415 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
416 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
417 status = sasl_mechanisms[i].fn(ads);
418 ldap_value_free(values);
419 ldap_msgfree(res);
420 return status;
425 ldap_value_free(values);
426 ldap_msgfree(res);
427 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
430 #endif