This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[bbaumbach/samba-autobuild/.git] / source3 / libads / sasl.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads sasl code
4    Copyright (C) Andrew Tridgell 2001
5    
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.
10    
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.
15    
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.
19 */
20
21 #include "includes.h"
22
23 #ifdef HAVE_LDAP
24
25 /* 
26    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27    we fit on one socket??)
28 */
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
30 {
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;
40
41         if (!ads->auth.password) {
42                 /* No password, don't segfault below... */
43                 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
44         }
45
46         neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
47                 NTLMSSP_NEGOTIATE_128 | 
48                 NTLMSSP_NEGOTIATE_NTLM;
49
50         memset(sess_key, 0, 16);
51
52         /* generate the ntlmssp negotiate packet */
53         msrpc_gen(&blob, "CddB",
54                   "NTLMSSP",
55                   NTLMSSP_NEGOTIATE,
56                   neg_flags,
57                   sess_key, 16);
58
59         /* and wrap it in a SPNEGO wrapper */
60         msg1 = gen_negTokenTarg(mechs, blob);
61         data_blob_free(&blob);
62
63         cred.bv_val = msg1.data;
64         cred.bv_len = msg1.length;
65
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;
70         }
71
72         blob = data_blob(scred->bv_val, scred->bv_len);
73
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;
79         }
80
81         data_blob_free(&blob);
82
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);
87
88         data_blob_free(&chal1);
89         data_blob_free(&chal2);
90
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);
102
103         /* wrap it in SPNEGO */
104         auth = spnego_gen_auth(blob);
105
106         data_blob_free(&blob);
107
108         /* now send the auth packet and we should be done */
109         cred.bv_val = auth.data;
110         cred.bv_len = auth.length;
111
112         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
113
114         return ADS_ERROR(rc);
115
116 failed:
117         return status;
118 }
119
120 /* 
121    perform a LDAP/SASL/SPNEGO/KRB5 bind
122 */
123 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
124 {
125         DATA_BLOB blob;
126         struct berval cred, *scred;
127         int rc;
128
129         blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
130
131         if (!blob.data) {
132                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
133         }
134
135         /* now send the auth packet and we should be done */
136         cred.bv_val = blob.data;
137         cred.bv_len = blob.length;
138
139         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
140
141         data_blob_free(&blob);
142
143         return ADS_ERROR(rc);
144 }
145
146 /* 
147    this performs a SASL/SPNEGO bind
148 */
149 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
150 {
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;
158
159         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
160
161         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
162                 status = ADS_ERROR(rc);
163                 goto failed;
164         }
165
166         blob = data_blob(scred->bv_val, scred->bv_len);
167
168 #if 0
169         file_save("sasl_spnego.dat", blob.data, blob.length);
170 #endif
171
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;
178         }
179         data_blob_free(&blob);
180
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;
187                 }
188                 free(OIDs[i]);
189         }
190         DEBUG(3,("got principal=%s\n", principal));
191
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);
200                 }
201                 if (ADS_ERR_OK(status))
202                         return status;
203         }
204 #endif
205
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);
210
211 failed:
212         return status;
213 }
214
215 #ifdef HAVE_GSSAPI
216 #define MAX_GSS_PASSES 3
217
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
223 */
224 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
225 {
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[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
245         gss_OID_desc nt_principal = 
246         {10, "\052\206\110\206\367\022\001\002\002\002"};
247
248         /* we need to fetch a service ticket as the ldap user in the
249            servers realm, regardless of our realm */
250         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
251         krb5_init_context(&ctx);
252         krb5_set_default_tgs_ktypes(ctx, enc_types);
253         krb5_parse_name(ctx, sname, &principal);
254         free(sname);
255         krb5_free_context(ctx); 
256
257         input_name.value = &principal;
258         input_name.length = sizeof(principal);
259
260         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
261         if (gss_rc) {
262                 return ADS_ERROR_GSS(gss_rc, minor_status);
263         }
264
265         context_handle = GSS_C_NO_CONTEXT;
266
267         input_token.value = NULL;
268         input_token.length = 0;
269
270         for (i=0; i < MAX_GSS_PASSES; i++) {
271                 gss_rc = gss_init_sec_context(&minor_status,
272                                           GSS_C_NO_CREDENTIAL,
273                                           &context_handle,
274                                           serv_name,
275                                           mech_type,
276                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
277                                           0,
278                                           NULL,
279                                           &input_token,
280                                           NULL,
281                                           &output_token,
282                                           &ret_flags,
283                                           NULL);
284
285                 if (input_token.value) {
286                         gss_release_buffer(&minor_status, &input_token);
287                 }
288
289                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
290                         status = ADS_ERROR_GSS(gss_rc, minor_status);
291                         goto failed;
292                 }
293
294                 cred.bv_val = output_token.value;
295                 cred.bv_len = output_token.length;
296
297                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
298                                       &scred);
299                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
300                         status = ADS_ERROR(rc);
301                         goto failed;
302                 }
303
304                 if (output_token.value) {
305                         gss_release_buffer(&minor_status, &output_token);
306                 }
307
308                 if (scred) {
309                         input_token.value = scred->bv_val;
310                         input_token.length = scred->bv_len;
311                 } else {
312                         input_token.value = NULL;
313                         input_token.length = 0;
314                 }
315
316                 if (gss_rc == 0) break;
317         }
318
319         gss_release_name(&minor_status, &serv_name);
320
321         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
322                             &conf_state,NULL);
323         if (gss_rc) {
324                 status = ADS_ERROR_GSS(gss_rc, minor_status);
325                 goto failed;
326         }
327
328         gss_release_buffer(&minor_status, &input_token);
329
330         p = (uint8 *)output_token.value;
331
332         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
333
334         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
335         sec_layer = *p;
336
337         gss_release_buffer(&minor_status, &output_token);
338
339         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
340         p = output_token.value;
341
342         *p++ = 1; /* no sign & seal selection */
343         /* choose the same size as the server gave us */
344         *p++ = max_msg_size>>16;
345         *p++ = max_msg_size>>8;
346         *p++ = max_msg_size;
347         snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
348         p += strlen(p);
349
350         output_token.length = PTR_DIFF(p, output_token.value);
351
352         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
353                           &output_token, &conf_state,
354                           &input_token);
355         if (gss_rc) {
356                 status = ADS_ERROR_GSS(gss_rc, minor_status);
357                 goto failed;
358         }
359
360         free(output_token.value);
361
362         cred.bv_val = input_token.value;
363         cred.bv_len = input_token.length;
364
365         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
366                               &scred);
367         status = ADS_ERROR(rc);
368
369         gss_release_buffer(&minor_status, &input_token);
370
371 failed:
372         return status;
373 }
374 #endif
375
376 /* mapping between SASL mechanisms and functions */
377 static struct {
378         const char *name;
379         ADS_STATUS (*fn)(ADS_STRUCT *);
380 } sasl_mechanisms[] = {
381         {"GSS-SPNEGO", ads_sasl_spnego_bind},
382 #ifdef HAVE_GSSAPI
383         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
384 #endif
385         {NULL, NULL}
386 };
387
388 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
389 {
390         const char *attrs[] = {"supportedSASLMechanisms", NULL};
391         char **values;
392         ADS_STATUS status;
393         int i, j;
394         void *res;
395
396         /* get a list of supported SASL mechanisms */
397         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
398         if (!ADS_ERR_OK(status)) return status;
399
400         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
401
402         /* try our supported mechanisms in order */
403         for (i=0;sasl_mechanisms[i].name;i++) {
404                 /* see if the server supports it */
405                 for (j=0;values && values[j];j++) {
406                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
407                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
408                                 status = sasl_mechanisms[i].fn(ads);
409                                 ldap_value_free(values);
410                                 ldap_msgfree(res);
411                                 return status;
412                         }
413                 }
414         }
415
416         ldap_value_free(values);
417         ldap_msgfree(res);
418         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
419 }
420
421 #endif
422