Fix a small typo in a comment and pretty it up a bit.
[Samba/gebeck_regimport.git] / source3 / libads / sasl.c
blob598208b17f8e80c7691e171c2eb3d3389ddc5588
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 = 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 = 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 int rc;
129 blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
131 if (!blob.data) {
132 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
135 /* now send the auth packet and we should be done */
136 cred.bv_val = blob.data;
137 cred.bv_len = blob.length;
139 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
141 data_blob_free(&blob);
143 return ADS_ERROR(rc);
147 this performs a SASL/SPNEGO bind
149 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
151 struct berval *scred=NULL;
152 int rc, i;
153 ADS_STATUS status;
154 DATA_BLOB blob;
155 char *principal;
156 char *OIDs[ASN1_MAX_OIDS];
157 BOOL got_kerberos_mechanism = False;
159 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
161 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
162 status = ADS_ERROR(rc);
163 goto failed;
166 blob = data_blob(scred->bv_val, scred->bv_len);
168 #if 0
169 file_save("sasl_spnego.dat", blob.data, blob.length);
170 #endif
172 /* the server sent us the first part of the SPNEGO exchange in the negprot
173 reply */
174 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
175 data_blob_free(&blob);
176 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
177 goto failed;
179 data_blob_free(&blob);
181 /* make sure the server understands kerberos */
182 for (i=0;OIDs[i];i++) {
183 DEBUG(3,("got OID=%s\n", OIDs[i]));
184 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
185 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
186 got_kerberos_mechanism = True;
188 free(OIDs[i]);
190 DEBUG(3,("got principal=%s\n", principal));
192 #ifdef HAVE_KRB5
193 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
194 got_kerberos_mechanism) {
195 status = ads_sasl_spnego_krb5_bind(ads, principal);
196 if (ADS_ERR_OK(status))
197 return status;
198 if (ads_kinit_password(ads) == 0) {
199 status = ads_sasl_spnego_krb5_bind(ads, principal);
201 /* only fallback to NTLMSSP if allowed */
202 if (ADS_ERR_OK(status) ||
203 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
204 return status;
207 #endif
209 /* lets do NTLMSSP ... this has the big advantage that we don't need
210 to sync clocks, and we don't rely on special versions of the krb5
211 library for HMAC_MD4 encryption */
212 return ads_sasl_spnego_ntlmssp_bind(ads);
214 failed:
215 return status;
218 #ifdef HAVE_GSSAPI
219 #define MAX_GSS_PASSES 3
221 /* this performs a SASL/gssapi bind
222 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
223 is very dependent on correctly configured DNS whereas
224 this routine is much less fragile
225 see RFC2078 and RFC2222 for details
227 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
229 int minor_status;
230 gss_name_t serv_name;
231 gss_buffer_desc input_name;
232 gss_ctx_id_t context_handle;
233 gss_OID mech_type = GSS_C_NULL_OID;
234 gss_buffer_desc output_token, input_token;
235 OM_uint32 ret_flags, conf_state;
236 struct berval cred;
237 struct berval *scred;
238 int i=0;
239 int gss_rc, rc;
240 uint8 *p;
241 uint32 max_msg_size;
242 char *sname;
243 unsigned sec_layer;
244 ADS_STATUS status;
245 krb5_principal principal;
246 krb5_context ctx;
247 krb5_enctype enc_types[] = {
248 #ifdef ENCTYPE_ARCFOUR_HMAC
249 ENCTYPE_ARCFOUR_HMAC,
250 #endif
251 ENCTYPE_DES_CBC_MD5,
252 ENCTYPE_NULL};
253 gss_OID_desc nt_principal =
254 {10, "\052\206\110\206\367\022\001\002\002\002"};
256 /* we need to fetch a service ticket as the ldap user in the
257 servers realm, regardless of our realm */
258 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
259 krb5_init_context(&ctx);
260 krb5_set_default_tgs_ktypes(ctx, enc_types);
261 krb5_parse_name(ctx, sname, &principal);
262 free(sname);
263 krb5_free_context(ctx);
265 input_name.value = &principal;
266 input_name.length = sizeof(principal);
268 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
269 if (gss_rc) {
270 return ADS_ERROR_GSS(gss_rc, minor_status);
273 context_handle = GSS_C_NO_CONTEXT;
275 input_token.value = NULL;
276 input_token.length = 0;
278 for (i=0; i < MAX_GSS_PASSES; i++) {
279 gss_rc = gss_init_sec_context(&minor_status,
280 GSS_C_NO_CREDENTIAL,
281 &context_handle,
282 serv_name,
283 mech_type,
284 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
286 NULL,
287 &input_token,
288 NULL,
289 &output_token,
290 &ret_flags,
291 NULL);
293 if (input_token.value) {
294 gss_release_buffer(&minor_status, &input_token);
297 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
298 status = ADS_ERROR_GSS(gss_rc, minor_status);
299 goto failed;
302 cred.bv_val = output_token.value;
303 cred.bv_len = output_token.length;
305 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
306 &scred);
307 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
308 status = ADS_ERROR(rc);
309 goto failed;
312 if (output_token.value) {
313 gss_release_buffer(&minor_status, &output_token);
316 if (scred) {
317 input_token.value = scred->bv_val;
318 input_token.length = scred->bv_len;
319 } else {
320 input_token.value = NULL;
321 input_token.length = 0;
324 if (gss_rc == 0) break;
327 gss_release_name(&minor_status, &serv_name);
329 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
330 &conf_state,NULL);
331 if (gss_rc) {
332 status = ADS_ERROR_GSS(gss_rc, minor_status);
333 goto failed;
336 gss_release_buffer(&minor_status, &input_token);
338 p = (uint8 *)output_token.value;
340 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
342 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
343 sec_layer = *p;
345 gss_release_buffer(&minor_status, &output_token);
347 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
348 p = output_token.value;
350 *p++ = 1; /* no sign & seal selection */
351 /* choose the same size as the server gave us */
352 *p++ = max_msg_size>>16;
353 *p++ = max_msg_size>>8;
354 *p++ = max_msg_size;
355 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
356 p += strlen(p);
358 output_token.length = PTR_DIFF(p, output_token.value);
360 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
361 &output_token, &conf_state,
362 &input_token);
363 if (gss_rc) {
364 status = ADS_ERROR_GSS(gss_rc, minor_status);
365 goto failed;
368 free(output_token.value);
370 cred.bv_val = input_token.value;
371 cred.bv_len = input_token.length;
373 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
374 &scred);
375 status = ADS_ERROR(rc);
377 gss_release_buffer(&minor_status, &input_token);
379 failed:
380 return status;
382 #endif
384 /* mapping between SASL mechanisms and functions */
385 static struct {
386 const char *name;
387 ADS_STATUS (*fn)(ADS_STRUCT *);
388 } sasl_mechanisms[] = {
389 {"GSS-SPNEGO", ads_sasl_spnego_bind},
390 #ifdef HAVE_GSSAPI
391 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
392 #endif
393 {NULL, NULL}
396 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
398 const char *attrs[] = {"supportedSASLMechanisms", NULL};
399 char **values;
400 ADS_STATUS status;
401 int i, j;
402 void *res;
404 /* get a list of supported SASL mechanisms */
405 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
406 if (!ADS_ERR_OK(status)) return status;
408 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
410 /* try our supported mechanisms in order */
411 for (i=0;sasl_mechanisms[i].name;i++) {
412 /* see if the server supports it */
413 for (j=0;values && values[j];j++) {
414 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
415 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
416 status = sasl_mechanisms[i].fn(ads);
417 ldap_value_free(values);
418 ldap_msgfree(res);
419 return status;
424 ldap_value_free(values);
425 ldap_msgfree(res);
426 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
429 #endif