Fixes to linker flags for AIX winbind client from Stephen Roylance.
[Samba/gebeck_regimport.git] / source3 / libads / sasl.c
blob29d4533a54fa056357e72184c1594114a95e63ee
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 if (ADS_ERR_OK(status))
202 return status;
204 #endif
206 /* lets do NTLMSSP ... this has the big advantage that we don't need
207 to sync clocks, and we don't rely on special versions of the krb5
208 library for HMAC_MD4 encryption */
209 return ads_sasl_spnego_ntlmssp_bind(ads);
211 failed:
212 return status;
215 #ifdef HAVE_GSSAPI
216 #define MAX_GSS_PASSES 3
218 /* this performs a SASL/gssapi bind
219 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
220 is very dependent on correctly configured DNS whereas
221 this routine is much less fragile
222 see RFC2078 and RFC2222 for details
224 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
226 int minor_status;
227 gss_name_t serv_name;
228 gss_buffer_desc input_name;
229 gss_ctx_id_t context_handle;
230 gss_OID mech_type = GSS_C_NULL_OID;
231 gss_buffer_desc output_token, input_token;
232 OM_uint32 ret_flags, conf_state;
233 struct berval cred;
234 struct berval *scred;
235 int i=0;
236 int gss_rc, rc;
237 uint8 *p;
238 uint32 max_msg_size;
239 char *sname;
240 unsigned sec_layer;
241 ADS_STATUS status;
242 krb5_principal principal;
243 krb5_context ctx;
244 krb5_enctype enc_types[] = {
245 #ifdef ENCTYPE_ARCFOUR_HMAC
246 ENCTYPE_ARCFOUR_HMAC,
247 #endif
248 ENCTYPE_DES_CBC_MD5,
249 ENCTYPE_NULL};
250 gss_OID_desc nt_principal =
251 {10, "\052\206\110\206\367\022\001\002\002\002"};
253 /* we need to fetch a service ticket as the ldap user in the
254 servers realm, regardless of our realm */
255 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
256 krb5_init_context(&ctx);
257 krb5_set_default_tgs_ktypes(ctx, enc_types);
258 krb5_parse_name(ctx, sname, &principal);
259 free(sname);
260 krb5_free_context(ctx);
262 input_name.value = &principal;
263 input_name.length = sizeof(principal);
265 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
266 if (gss_rc) {
267 return ADS_ERROR_GSS(gss_rc, minor_status);
270 context_handle = GSS_C_NO_CONTEXT;
272 input_token.value = NULL;
273 input_token.length = 0;
275 for (i=0; i < MAX_GSS_PASSES; i++) {
276 gss_rc = gss_init_sec_context(&minor_status,
277 GSS_C_NO_CREDENTIAL,
278 &context_handle,
279 serv_name,
280 mech_type,
281 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
283 NULL,
284 &input_token,
285 NULL,
286 &output_token,
287 &ret_flags,
288 NULL);
290 if (input_token.value) {
291 gss_release_buffer(&minor_status, &input_token);
294 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
295 status = ADS_ERROR_GSS(gss_rc, minor_status);
296 goto failed;
299 cred.bv_val = output_token.value;
300 cred.bv_len = output_token.length;
302 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
303 &scred);
304 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
305 status = ADS_ERROR(rc);
306 goto failed;
309 if (output_token.value) {
310 gss_release_buffer(&minor_status, &output_token);
313 if (scred) {
314 input_token.value = scred->bv_val;
315 input_token.length = scred->bv_len;
316 } else {
317 input_token.value = NULL;
318 input_token.length = 0;
321 if (gss_rc == 0) break;
324 gss_release_name(&minor_status, &serv_name);
326 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
327 &conf_state,NULL);
328 if (gss_rc) {
329 status = ADS_ERROR_GSS(gss_rc, minor_status);
330 goto failed;
333 gss_release_buffer(&minor_status, &input_token);
335 p = (uint8 *)output_token.value;
337 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
339 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
340 sec_layer = *p;
342 gss_release_buffer(&minor_status, &output_token);
344 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
345 p = output_token.value;
347 *p++ = 1; /* no sign & seal selection */
348 /* choose the same size as the server gave us */
349 *p++ = max_msg_size>>16;
350 *p++ = max_msg_size>>8;
351 *p++ = max_msg_size;
352 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
353 p += strlen(p);
355 output_token.length = PTR_DIFF(p, output_token.value);
357 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
358 &output_token, &conf_state,
359 &input_token);
360 if (gss_rc) {
361 status = ADS_ERROR_GSS(gss_rc, minor_status);
362 goto failed;
365 free(output_token.value);
367 cred.bv_val = input_token.value;
368 cred.bv_len = input_token.length;
370 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
371 &scred);
372 status = ADS_ERROR(rc);
374 gss_release_buffer(&minor_status, &input_token);
376 failed:
377 return status;
379 #endif
381 /* mapping between SASL mechanisms and functions */
382 static struct {
383 const char *name;
384 ADS_STATUS (*fn)(ADS_STRUCT *);
385 } sasl_mechanisms[] = {
386 {"GSS-SPNEGO", ads_sasl_spnego_bind},
387 #ifdef HAVE_GSSAPI
388 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
389 #endif
390 {NULL, NULL}
393 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
395 const char *attrs[] = {"supportedSASLMechanisms", NULL};
396 char **values;
397 ADS_STATUS status;
398 int i, j;
399 void *res;
401 /* get a list of supported SASL mechanisms */
402 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
403 if (!ADS_ERR_OK(status)) return status;
405 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
407 /* try our supported mechanisms in order */
408 for (i=0;sasl_mechanisms[i].name;i++) {
409 /* see if the server supports it */
410 for (j=0;values && values[j];j++) {
411 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
412 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
413 status = sasl_mechanisms[i].fn(ads);
414 ldap_value_free(values);
415 ldap_msgfree(res);
416 return status;
421 ldap_value_free(values);
422 ldap_msgfree(res);
423 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
426 #endif