Re-enable use of existing kerberos tickets.
[samba.git] / source / 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         extern pstring global_myname;
40         int rc;
41
42         if (!ads->auth.password) {
43                 /* No password, don't segfault below... */
44                 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
45         }
46
47         neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
48                 NTLMSSP_NEGOTIATE_128 | 
49                 NTLMSSP_NEGOTIATE_NTLM;
50
51         memset(sess_key, 0, 16);
52
53         /* generate the ntlmssp negotiate packet */
54         msrpc_gen(&blob, "CddB",
55                   "NTLMSSP",
56                   NTLMSSP_NEGOTIATE,
57                   neg_flags,
58                   sess_key, 16);
59
60         /* and wrap it in a SPNEGO wrapper */
61         msg1 = gen_negTokenTarg(mechs, blob);
62         data_blob_free(&blob);
63
64         cred.bv_val = msg1.data;
65         cred.bv_len = msg1.length;
66
67         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
68         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
69                 status = ADS_ERROR(rc);
70                 goto failed;
71         }
72
73         blob = data_blob(scred->bv_val, scred->bv_len);
74
75         /* the server gives us back two challenges */
76         if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
77                 DEBUG(3,("Failed to parse challenges\n"));
78                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
79                 goto failed;
80         }
81
82         data_blob_free(&blob);
83
84         /* encrypt the password with the challenge */
85         memcpy(challenge, chal1.data + 24, 8);
86         SMBencrypt(ads->auth.password, challenge,lmhash);
87         SMBNTencrypt(ads->auth.password, challenge,nthash);
88
89         data_blob_free(&chal1);
90         data_blob_free(&chal2);
91
92         /* this generates the actual auth packet */
93         msrpc_gen(&blob, "CdBBUUUBd", 
94                   "NTLMSSP", 
95                   NTLMSSP_AUTH, 
96                   lmhash, 24,
97                   nthash, 24,
98                   lp_workgroup(), 
99                   ads->auth.user_name, 
100                   global_myname,
101                   sess_key, 16,
102                   neg_flags);
103
104         /* wrap it in SPNEGO */
105         auth = spnego_gen_auth(blob);
106
107         data_blob_free(&blob);
108
109         /* now send the auth packet and we should be done */
110         cred.bv_val = auth.data;
111         cred.bv_len = auth.length;
112
113         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
114
115         return ADS_ERROR(rc);
116
117 failed:
118         return status;
119 }
120
121 /* 
122    perform a LDAP/SASL/SPNEGO/KRB5 bind
123 */
124 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
125 {
126         DATA_BLOB blob;
127         struct berval cred, *scred;
128         int rc;
129
130         blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
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                         return ads_sasl_spnego_krb5_bind(ads, principal);
201         }
202 #endif
203
204         /* lets do NTLMSSP ... this has the big advantage that we don't need
205            to sync clocks, and we don't rely on special versions of the krb5 
206            library for HMAC_MD4 encryption */
207         return ads_sasl_spnego_ntlmssp_bind(ads);
208
209 failed:
210         return status;
211 }
212
213 #ifdef HAVE_GSSAPI
214 #define MAX_GSS_PASSES 3
215
216 /* this performs a SASL/gssapi bind
217    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
218    is very dependent on correctly configured DNS whereas
219    this routine is much less fragile
220    see RFC2078 and RFC2222 for details
221 */
222 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
223 {
224         int minor_status;
225         gss_name_t serv_name;
226         gss_buffer_desc input_name;
227         gss_ctx_id_t context_handle;
228         gss_OID mech_type = GSS_C_NULL_OID;
229         gss_buffer_desc output_token, input_token;
230         OM_uint32 ret_flags, conf_state;
231         struct berval cred;
232         struct berval *scred;
233         int i=0;
234         int gss_rc, rc;
235         uint8 *p;
236         uint32 max_msg_size;
237         char *sname;
238         unsigned sec_layer;
239         ADS_STATUS status;
240         krb5_principal principal;
241         krb5_context ctx;
242         krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
243         gss_OID_desc nt_principal = 
244         {10, "\052\206\110\206\367\022\001\002\002\002"};
245
246         /* we need to fetch a service ticket as the ldap user in the
247            servers realm, regardless of our realm */
248         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
249         krb5_init_context(&ctx);
250         krb5_set_default_tgs_ktypes(ctx, enc_types);
251         krb5_parse_name(ctx, sname, &principal);
252         free(sname);
253         krb5_free_context(ctx); 
254
255         input_name.value = &principal;
256         input_name.length = sizeof(principal);
257
258         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
259         if (gss_rc) {
260                 return ADS_ERROR_GSS(gss_rc, minor_status);
261         }
262
263         context_handle = GSS_C_NO_CONTEXT;
264
265         input_token.value = NULL;
266         input_token.length = 0;
267
268         for (i=0; i < MAX_GSS_PASSES; i++) {
269                 gss_rc = gss_init_sec_context(&minor_status,
270                                           GSS_C_NO_CREDENTIAL,
271                                           &context_handle,
272                                           serv_name,
273                                           mech_type,
274                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
275                                           0,
276                                           NULL,
277                                           &input_token,
278                                           NULL,
279                                           &output_token,
280                                           &ret_flags,
281                                           NULL);
282
283                 if (input_token.value) {
284                         gss_release_buffer(&minor_status, &input_token);
285                 }
286
287                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
288                         status = ADS_ERROR_GSS(gss_rc, minor_status);
289                         goto failed;
290                 }
291
292                 cred.bv_val = output_token.value;
293                 cred.bv_len = output_token.length;
294
295                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
296                                       &scred);
297                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
298                         status = ADS_ERROR(rc);
299                         goto failed;
300                 }
301
302                 if (output_token.value) {
303                         gss_release_buffer(&minor_status, &output_token);
304                 }
305
306                 if (scred) {
307                         input_token.value = scred->bv_val;
308                         input_token.length = scred->bv_len;
309                 } else {
310                         input_token.value = NULL;
311                         input_token.length = 0;
312                 }
313
314                 if (gss_rc == 0) break;
315         }
316
317         gss_release_name(&minor_status, &serv_name);
318
319         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
320                             &conf_state,NULL);
321         if (gss_rc) {
322                 status = ADS_ERROR_GSS(gss_rc, minor_status);
323                 goto failed;
324         }
325
326         gss_release_buffer(&minor_status, &input_token);
327
328         p = (uint8 *)output_token.value;
329
330         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
331
332         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
333         sec_layer = *p;
334
335         gss_release_buffer(&minor_status, &output_token);
336
337         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
338         p = output_token.value;
339
340         *p++ = 1; /* no sign & seal selection */
341         /* choose the same size as the server gave us */
342         *p++ = max_msg_size>>16;
343         *p++ = max_msg_size>>8;
344         *p++ = max_msg_size;
345         snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
346         p += strlen(p);
347
348         output_token.length = PTR_DIFF(p, output_token.value);
349
350         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
351                           &output_token, &conf_state,
352                           &input_token);
353         if (gss_rc) {
354                 status = ADS_ERROR_GSS(gss_rc, minor_status);
355                 goto failed;
356         }
357
358         free(output_token.value);
359
360         cred.bv_val = input_token.value;
361         cred.bv_len = input_token.length;
362
363         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
364                               &scred);
365         status = ADS_ERROR(rc);
366
367         gss_release_buffer(&minor_status, &input_token);
368
369 failed:
370         return status;
371 }
372 #endif
373
374 /* mapping between SASL mechanisms and functions */
375 static struct {
376         const char *name;
377         ADS_STATUS (*fn)(ADS_STRUCT *);
378 } sasl_mechanisms[] = {
379         {"GSS-SPNEGO", ads_sasl_spnego_bind},
380 #ifdef HAVE_GSSAPI
381         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
382 #endif
383         {NULL, NULL}
384 };
385
386 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
387 {
388         const char *attrs[] = {"supportedSASLMechanisms", NULL};
389         char **values;
390         ADS_STATUS status;
391         int i, j;
392         void *res;
393
394         /* get a list of supported SASL mechanisms */
395         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
396         if (!ADS_ERR_OK(status)) return status;
397
398         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
399
400         /* try our supported mechanisms in order */
401         for (i=0;sasl_mechanisms[i].name;i++) {
402                 /* see if the server supports it */
403                 for (j=0;values && values[j];j++) {
404                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
405                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
406                                 status = sasl_mechanisms[i].fn(ads);
407                                 ldap_value_free(values);
408                                 ldap_msgfree(res);
409                                 return status;
410                         }
411                 }
412         }
413
414         ldap_value_free(values);
415         ldap_msgfree(res);
416         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
417 }
418
419 #endif
420