Update my copyrights according to my agreement with IBM
[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         unsigned char sk[16];
128         int rc;
129
130         blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, sk);
131
132         if (!blob.data) {
133                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
134         }
135
136         /* now send the auth packet and we should be done */
137         cred.bv_val = blob.data;
138         cred.bv_len = blob.length;
139
140         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
141
142         data_blob_free(&blob);
143
144         return ADS_ERROR(rc);
145 }
146
147 /* 
148    this performs a SASL/SPNEGO bind
149 */
150 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
151 {
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;
159
160         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
161
162         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
163                 status = ADS_ERROR(rc);
164                 goto failed;
165         }
166
167         blob = data_blob(scred->bv_val, scred->bv_len);
168
169 #if 0
170         file_save("sasl_spnego.dat", blob.data, blob.length);
171 #endif
172
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;
179         }
180         data_blob_free(&blob);
181
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;
188                 }
189                 free(OIDs[i]);
190         }
191         DEBUG(3,("got principal=%s\n", principal));
192
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);
201                 }
202                 /* only fallback to NTLMSSP if allowed */
203                 if (ADS_ERR_OK(status) || 
204                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
205                         return status;
206                 }
207         }
208 #endif
209
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);
214
215 failed:
216         return status;
217 }
218
219 #ifdef HAVE_GSSAPI
220 #define MAX_GSS_PASSES 3
221
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
227 */
228 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
229 {
230         int 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         OM_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"};
256
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); 
265
266         input_name.value = &principal;
267         input_name.length = sizeof(principal);
268
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);
272         }
273
274         context_handle = GSS_C_NO_CONTEXT;
275
276         input_token.value = NULL;
277         input_token.length = 0;
278
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,
286                                           0,
287                                           NULL,
288                                           &input_token,
289                                           NULL,
290                                           &output_token,
291                                           &ret_flags,
292                                           NULL);
293
294                 if (input_token.value) {
295                         gss_release_buffer(&minor_status, &input_token);
296                 }
297
298                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
299                         status = ADS_ERROR_GSS(gss_rc, minor_status);
300                         goto failed;
301                 }
302
303                 cred.bv_val = output_token.value;
304                 cred.bv_len = output_token.length;
305
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;
311                 }
312
313                 if (output_token.value) {
314                         gss_release_buffer(&minor_status, &output_token);
315                 }
316
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;
323                 }
324
325                 if (gss_rc == 0) break;
326         }
327
328         gss_release_name(&minor_status, &serv_name);
329
330         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
331                             &conf_state,NULL);
332         if (gss_rc) {
333                 status = ADS_ERROR_GSS(gss_rc, minor_status);
334                 goto failed;
335         }
336
337         gss_release_buffer(&minor_status, &input_token);
338
339         p = (uint8 *)output_token.value;
340
341         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
342
343         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
344         sec_layer = *p;
345
346         gss_release_buffer(&minor_status, &output_token);
347
348         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
349         p = output_token.value;
350
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(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
357         p += strlen(p);
358
359         output_token.length = PTR_DIFF(p, output_token.value);
360
361         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
362                           &output_token, &conf_state,
363                           &input_token);
364         if (gss_rc) {
365                 status = ADS_ERROR_GSS(gss_rc, minor_status);
366                 goto failed;
367         }
368
369         free(output_token.value);
370
371         cred.bv_val = input_token.value;
372         cred.bv_len = input_token.length;
373
374         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
375                               &scred);
376         status = ADS_ERROR(rc);
377
378         gss_release_buffer(&minor_status, &input_token);
379
380 failed:
381         return status;
382 }
383 #endif
384
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}
395 };
396
397 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
398 {
399         const char *attrs[] = {"supportedSASLMechanisms", NULL};
400         char **values;
401         ADS_STATUS status;
402         int i, j;
403         void *res;
404
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;
408
409         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
410
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;
421                         }
422                 }
423         }
424
425         ldap_value_free(values);
426         ldap_msgfree(res);
427         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
428 }
429
430 #endif
431