r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[vlendec/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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21
22 #ifdef HAVE_LDAP
23
24 /* 
25    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
26    we fit on one socket??)
27 */
28 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
29 {
30         DATA_BLOB msg1 = data_blob_null;
31         DATA_BLOB blob = data_blob_null;
32         DATA_BLOB blob_in = data_blob_null;
33         DATA_BLOB blob_out = data_blob_null;
34         struct berval cred, *scred = NULL;
35         int rc;
36         NTSTATUS nt_status;
37         int turn = 1;
38
39         struct ntlmssp_state *ntlmssp_state;
40
41         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
42                 return ADS_ERROR_NT(nt_status);
43         }
44         ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
45
46         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
47                 return ADS_ERROR_NT(nt_status);
48         }
49         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
50                 return ADS_ERROR_NT(nt_status);
51         }
52         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
53                 return ADS_ERROR_NT(nt_status);
54         }
55
56         blob_in = data_blob_null;
57
58         do {
59                 nt_status = ntlmssp_update(ntlmssp_state, 
60                                            blob_in, &blob_out);
61                 data_blob_free(&blob_in);
62                 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
63                      || NT_STATUS_IS_OK(nt_status))
64                     && blob_out.length) {
65                         if (turn == 1) {
66                                 /* and wrap it in a SPNEGO wrapper */
67                                 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
68                         } else {
69                                 /* wrap it in SPNEGO */
70                                 msg1 = spnego_gen_auth(blob_out);
71                         }
72
73                         data_blob_free(&blob_out);
74
75                         cred.bv_val = (char *)msg1.data;
76                         cred.bv_len = msg1.length;
77                         scred = NULL;
78                         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
79                         data_blob_free(&msg1);
80                         if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
81                                 if (scred) {
82                                         ber_bvfree(scred);
83                                 }
84
85                                 ntlmssp_end(&ntlmssp_state);
86                                 return ADS_ERROR(rc);
87                         }
88                         if (scred) {
89                                 blob = data_blob(scred->bv_val, scred->bv_len);
90                                 ber_bvfree(scred);
91                         } else {
92                                 blob = data_blob_null;
93                         }
94
95                 } else {
96
97                         ntlmssp_end(&ntlmssp_state);
98                         data_blob_free(&blob_out);
99                         return ADS_ERROR_NT(nt_status);
100                 }
101                 
102                 if ((turn == 1) && 
103                     (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
104                         DATA_BLOB tmp_blob = data_blob_null;
105                         /* the server might give us back two challenges */
106                         if (!spnego_parse_challenge(blob, &blob_in, 
107                                                     &tmp_blob)) {
108
109                                 ntlmssp_end(&ntlmssp_state);
110                                 data_blob_free(&blob);
111                                 DEBUG(3,("Failed to parse challenges\n"));
112                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
113                         }
114                         data_blob_free(&tmp_blob);
115                 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
116                         if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, 
117                                                         &blob_in)) {
118
119                                 ntlmssp_end(&ntlmssp_state);
120                                 data_blob_free(&blob);
121                                 DEBUG(3,("Failed to parse auth response\n"));
122                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
123                         }
124                 }
125                 data_blob_free(&blob);
126                 data_blob_free(&blob_out);
127                 turn++;
128         } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
129         
130         /* we have a reference conter on ntlmssp_state, if we are signing
131            then the state will be kept by the signing engine */
132
133         ntlmssp_end(&ntlmssp_state);
134
135         return ADS_ERROR(rc);
136 }
137
138 #ifdef HAVE_KRB5
139 /* 
140    perform a LDAP/SASL/SPNEGO/KRB5 bind
141 */
142 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
143 {
144         DATA_BLOB blob = data_blob_null;
145         struct berval cred, *scred = NULL;
146         DATA_BLOB session_key = data_blob_null;
147         int rc;
148
149         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
150                                      &ads->auth.tgs_expire);
151
152         if (rc) {
153                 return ADS_ERROR_KRB5(rc);
154         }
155
156         /* now send the auth packet and we should be done */
157         cred.bv_val = (char *)blob.data;
158         cred.bv_len = blob.length;
159
160         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
161
162         data_blob_free(&blob);
163         data_blob_free(&session_key);
164         if(scred)
165                 ber_bvfree(scred);
166
167         return ADS_ERROR(rc);
168 }
169 #endif
170
171 /* 
172    this performs a SASL/SPNEGO bind
173 */
174 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
175 {
176         struct berval *scred=NULL;
177         int rc, i;
178         ADS_STATUS status;
179         DATA_BLOB blob;
180         char *principal = NULL;
181         char *OIDs[ASN1_MAX_OIDS];
182 #ifdef HAVE_KRB5
183         BOOL got_kerberos_mechanism = False;
184 #endif
185
186         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
187
188         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
189                 status = ADS_ERROR(rc);
190                 goto failed;
191         }
192
193         blob = data_blob(scred->bv_val, scred->bv_len);
194
195         ber_bvfree(scred);
196
197 #if 0
198         file_save("sasl_spnego.dat", blob.data, blob.length);
199 #endif
200
201         /* the server sent us the first part of the SPNEGO exchange in the negprot 
202            reply */
203         if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
204                 data_blob_free(&blob);
205                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
206                 goto failed;
207         }
208         data_blob_free(&blob);
209
210         /* make sure the server understands kerberos */
211         for (i=0;OIDs[i];i++) {
212                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
213 #ifdef HAVE_KRB5
214                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
215                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
216                         got_kerberos_mechanism = True;
217                 }
218 #endif
219                 free(OIDs[i]);
220         }
221         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
222
223 #ifdef HAVE_KRB5
224         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
225             got_kerberos_mechanism) 
226         {
227                 /* I've seen a child Windows 2000 domain not send 
228                    the principal name back in the first round of 
229                    the SASL bind reply.  So we guess based on server
230                    name and realm.  --jerry  */
231                 if ( !principal ) {
232                         if ( ads->server.realm && ads->server.ldap_server ) {
233                                 char *server, *server_realm;
234                                 
235                                 server = SMB_STRDUP( ads->server.ldap_server );
236                                 server_realm = SMB_STRDUP( ads->server.realm );
237                                 
238                                 if ( !server || !server_realm )
239                                         return ADS_ERROR(LDAP_NO_MEMORY);
240
241                                 strlower_m( server );
242                                 strupper_m( server_realm );                             
243                                 asprintf( &principal, "ldap/%s@%s", server, server_realm );
244
245                                 SAFE_FREE( server );
246                                 SAFE_FREE( server_realm );
247
248                                 if ( !principal )
249                                         return ADS_ERROR(LDAP_NO_MEMORY);                               
250                         }
251                         
252                 }
253                 
254                 status = ads_sasl_spnego_krb5_bind(ads, principal);
255                 if (ADS_ERR_OK(status)) {
256                         SAFE_FREE(principal);
257                         return status;
258                 }
259
260                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
261                           "calling kinit\n", ads_errstr(status)));
262
263                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
264
265                 if (ADS_ERR_OK(status)) {
266                         status = ads_sasl_spnego_krb5_bind(ads, principal);
267                 }
268
269                 /* only fallback to NTLMSSP if allowed */
270                 if (ADS_ERR_OK(status) || 
271                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
272                         SAFE_FREE(principal);
273                         return status;
274                 }
275         }
276 #endif
277
278         SAFE_FREE(principal);
279
280         /* lets do NTLMSSP ... this has the big advantage that we don't need
281            to sync clocks, and we don't rely on special versions of the krb5 
282            library for HMAC_MD4 encryption */
283         return ads_sasl_spnego_ntlmssp_bind(ads);
284
285 failed:
286         return status;
287 }
288
289 #ifdef HAVE_GSSAPI
290 #define MAX_GSS_PASSES 3
291
292 /* this performs a SASL/gssapi bind
293    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
294    is very dependent on correctly configured DNS whereas
295    this routine is much less fragile
296    see RFC2078 and RFC2222 for details
297 */
298 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
299 {
300         uint32 minor_status;
301         gss_name_t serv_name;
302         gss_buffer_desc input_name;
303         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
304         gss_OID mech_type = GSS_C_NULL_OID;
305         gss_buffer_desc output_token, input_token;
306         uint32 ret_flags, conf_state;
307         struct berval cred;
308         struct berval *scred = NULL;
309         int i=0;
310         int gss_rc, rc;
311         uint8 *p;
312         uint32 max_msg_size = 0;
313         char *sname = NULL;
314         ADS_STATUS status;
315         krb5_principal principal = NULL;
316         krb5_context ctx = NULL;
317         krb5_enctype enc_types[] = {
318 #ifdef ENCTYPE_ARCFOUR_HMAC
319                         ENCTYPE_ARCFOUR_HMAC,
320 #endif
321                         ENCTYPE_DES_CBC_MD5,
322                         ENCTYPE_NULL};
323         gss_OID_desc nt_principal = 
324         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
325
326         /* we need to fetch a service ticket as the ldap user in the
327            servers realm, regardless of our realm */
328         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
329
330         initialize_krb5_error_table();
331         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
332         if (!ADS_ERR_OK(status)) {
333                 SAFE_FREE(sname);
334                 return status;
335         }
336         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
337         if (!ADS_ERR_OK(status)) {
338                 SAFE_FREE(sname);
339                 krb5_free_context(ctx); 
340                 return status;
341         }
342         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
343         if (!ADS_ERR_OK(status)) {
344                 SAFE_FREE(sname);
345                 krb5_free_context(ctx); 
346                 return status;
347         }
348
349         input_name.value = &principal;
350         input_name.length = sizeof(principal);
351
352         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
353
354         /*
355          * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
356          * to point to the *address* of the krb5_principal, and the gss libraries
357          * to a shallow copy of the krb5_principal pointer - so we need to keep
358          * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
359          * Just one more way in which MIT engineers screwed me over.... JRA.
360          */
361
362         SAFE_FREE(sname);
363
364         if (gss_rc) {
365                 krb5_free_principal(ctx, principal);
366                 krb5_free_context(ctx); 
367                 return ADS_ERROR_GSS(gss_rc, minor_status);
368         }
369
370         input_token.value = NULL;
371         input_token.length = 0;
372
373         for (i=0; i < MAX_GSS_PASSES; i++) {
374                 gss_rc = gss_init_sec_context(&minor_status,
375                                           GSS_C_NO_CREDENTIAL,
376                                           &context_handle,
377                                           serv_name,
378                                           mech_type,
379                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
380                                           0,
381                                           NULL,
382                                           &input_token,
383                                           NULL,
384                                           &output_token,
385                                           &ret_flags,
386                                           NULL);
387
388                 if (input_token.value) {
389                         gss_release_buffer(&minor_status, &input_token);
390                 }
391
392                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
393                         status = ADS_ERROR_GSS(gss_rc, minor_status);
394                         goto failed;
395                 }
396
397                 cred.bv_val = (char *)output_token.value;
398                 cred.bv_len = output_token.length;
399
400                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
401                                       &scred);
402                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
403                         status = ADS_ERROR(rc);
404                         goto failed;
405                 }
406
407                 if (output_token.value) {
408                         gss_release_buffer(&minor_status, &output_token);
409                 }
410
411                 if (scred) {
412                         input_token.value = scred->bv_val;
413                         input_token.length = scred->bv_len;
414                 } else {
415                         input_token.value = NULL;
416                         input_token.length = 0;
417                 }
418
419                 if (gss_rc == 0) break;
420         }
421
422         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
423                             (int *)&conf_state,NULL);
424         if (gss_rc) {
425                 status = ADS_ERROR_GSS(gss_rc, minor_status);
426                 goto failed;
427         }
428
429         gss_release_buffer(&minor_status, &input_token);
430
431         p = (uint8 *)output_token.value;
432
433 #if 0
434         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
435 #endif
436
437         if (p) {
438                 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
439         }
440
441         gss_release_buffer(&minor_status, &output_token);
442
443         output_token.length = 4;
444         output_token.value = SMB_MALLOC(output_token.length);
445         p = (uint8 *)output_token.value;
446
447         *p++ = 1; /* no sign & seal selection */
448         /* choose the same size as the server gave us */
449         *p++ = max_msg_size>>16;
450         *p++ = max_msg_size>>8;
451         *p++ = max_msg_size;
452         /*
453          * we used to add sprintf("dn:%s", ads->config.bind_path) here.
454          * but using ads->config.bind_path is the wrong! It should be
455          * the DN of the user object!
456          *
457          * w2k3 gives an error when we send an incorrect DN, but sending nothing
458          * is ok and matches the information flow used in GSS-SPNEGO.
459          */
460
461         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
462                           &output_token, (int *)&conf_state,
463                           &input_token);
464         if (gss_rc) {
465                 status = ADS_ERROR_GSS(gss_rc, minor_status);
466                 goto failed;
467         }
468
469         free(output_token.value);
470
471         cred.bv_val = (char *)input_token.value;
472         cred.bv_len = input_token.length;
473
474         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
475                               &scred);
476         status = ADS_ERROR(rc);
477
478         gss_release_buffer(&minor_status, &input_token);
479
480 failed:
481
482         gss_release_name(&minor_status, &serv_name);
483         if (context_handle != GSS_C_NO_CONTEXT)
484                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
485         krb5_free_principal(ctx, principal);
486         krb5_free_context(ctx); 
487
488         if(scred)
489                 ber_bvfree(scred);
490         return status;
491 }
492 #endif /* HAVE_GGSAPI */
493
494 /* mapping between SASL mechanisms and functions */
495 static struct {
496         const char *name;
497         ADS_STATUS (*fn)(ADS_STRUCT *);
498 } sasl_mechanisms[] = {
499         {"GSS-SPNEGO", ads_sasl_spnego_bind},
500 #ifdef HAVE_GSSAPI
501         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
502 #endif
503         {NULL, NULL}
504 };
505
506 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
507 {
508         const char *attrs[] = {"supportedSASLMechanisms", NULL};
509         char **values;
510         ADS_STATUS status;
511         int i, j;
512         LDAPMessage *res;
513
514         /* get a list of supported SASL mechanisms */
515         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
516         if (!ADS_ERR_OK(status)) return status;
517
518         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
519
520         /* try our supported mechanisms in order */
521         for (i=0;sasl_mechanisms[i].name;i++) {
522                 /* see if the server supports it */
523                 for (j=0;values && values[j];j++) {
524                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
525                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
526                                 status = sasl_mechanisms[i].fn(ads);
527                                 ldap_value_free(values);
528                                 ldap_msgfree(res);
529                                 return status;
530                         }
531                 }
532         }
533
534         ldap_value_free(values);
535         ldap_msgfree(res);
536         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
537 }
538
539 #endif /* HAVE_LDAP */
540