libads/sasl.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003    ads sasl code
00004    Copyright (C) Andrew Tridgell 2001
00005    
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015    
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 */
00020 
00021 #include "includes.h"
00022 
00023 #ifdef HAVE_LDAP
00024 
00025 /* 
00026    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
00027    we fit on one socket??)
00028 */
00029 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
00030 {
00031         DATA_BLOB msg1 = data_blob(NULL, 0);
00032         DATA_BLOB blob = data_blob(NULL, 0);
00033         DATA_BLOB blob_in = data_blob(NULL, 0);
00034         DATA_BLOB blob_out = data_blob(NULL, 0);
00035         struct berval cred, *scred = NULL;
00036         int rc;
00037         NTSTATUS nt_status;
00038         int turn = 1;
00039 
00040         struct ntlmssp_state *ntlmssp_state;
00041 
00042         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
00043                 return ADS_ERROR_NT(nt_status);
00044         }
00045         ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
00046 
00047         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
00048                 return ADS_ERROR_NT(nt_status);
00049         }
00050         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
00051                 return ADS_ERROR_NT(nt_status);
00052         }
00053         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
00054                 return ADS_ERROR_NT(nt_status);
00055         }
00056 
00057         blob_in = data_blob(NULL, 0);
00058 
00059         do {
00060                 nt_status = ntlmssp_update(ntlmssp_state, 
00061                                            blob_in, &blob_out);
00062                 data_blob_free(&blob_in);
00063                 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
00064                      || NT_STATUS_IS_OK(nt_status))
00065                     && blob_out.length) {
00066                         if (turn == 1) {
00067                                 /* and wrap it in a SPNEGO wrapper */
00068                                 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
00069                         } else {
00070                                 /* wrap it in SPNEGO */
00071                                 msg1 = spnego_gen_auth(blob_out);
00072                         }
00073 
00074                         data_blob_free(&blob_out);
00075 
00076                         cred.bv_val = (char *)msg1.data;
00077                         cred.bv_len = msg1.length;
00078                         scred = NULL;
00079                         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
00080                         data_blob_free(&msg1);
00081                         if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
00082                                 if (scred) {
00083                                         ber_bvfree(scred);
00084                                 }
00085 
00086                                 ntlmssp_end(&ntlmssp_state);
00087                                 return ADS_ERROR(rc);
00088                         }
00089                         if (scred) {
00090                                 blob = data_blob(scred->bv_val, scred->bv_len);
00091                                 ber_bvfree(scred);
00092                         } else {
00093                                 blob = data_blob(NULL, 0);
00094                         }
00095 
00096                 } else {
00097 
00098                         ntlmssp_end(&ntlmssp_state);
00099                         data_blob_free(&blob_out);
00100                         return ADS_ERROR_NT(nt_status);
00101                 }
00102                 
00103                 if ((turn == 1) && 
00104                     (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
00105                         DATA_BLOB tmp_blob = data_blob(NULL, 0);
00106                         /* the server might give us back two challenges */
00107                         if (!spnego_parse_challenge(blob, &blob_in, 
00108                                                     &tmp_blob)) {
00109 
00110                                 ntlmssp_end(&ntlmssp_state);
00111                                 data_blob_free(&blob);
00112                                 DEBUG(3,("Failed to parse challenges\n"));
00113                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
00114                         }
00115                         data_blob_free(&tmp_blob);
00116                 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
00117                         if (!spnego_parse_auth_response(blob, nt_status, 
00118                                                         &blob_in)) {
00119 
00120                                 ntlmssp_end(&ntlmssp_state);
00121                                 data_blob_free(&blob);
00122                                 DEBUG(3,("Failed to parse auth response\n"));
00123                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
00124                         }
00125                 }
00126                 data_blob_free(&blob);
00127                 data_blob_free(&blob_out);
00128                 turn++;
00129         } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
00130         
00131         /* we have a reference conter on ntlmssp_state, if we are signing
00132            then the state will be kept by the signing engine */
00133 
00134         ntlmssp_end(&ntlmssp_state);
00135 
00136         return ADS_ERROR(rc);
00137 }
00138 
00139 #ifdef HAVE_KRB5
00140 struct ads_service_principal {
00141          char *string;
00142 #ifdef HAVE_GSSAPI
00143          gss_name_t name;
00144 #endif
00145 };
00146 
00147 static void ads_free_service_principal(struct ads_service_principal *p)
00148 {
00149         SAFE_FREE(p->string);
00150 
00151 #ifdef HAVE_GSSAPI
00152         if (p->name) {
00153                 uint32 minor_status;
00154                 gss_release_name(&minor_status, &p->name);
00155         }
00156 #endif
00157         ZERO_STRUCTP(p);
00158 }
00159 
00160 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
00161                                                  const char *given_principal,
00162                                                  struct ads_service_principal *p)
00163 {
00164         ADS_STATUS status;
00165 #ifdef HAVE_GSSAPI
00166         gss_buffer_desc input_name;
00167         /* GSS_KRB5_NT_PRINCIPAL_NAME */
00168         gss_OID_desc nt_principal =
00169         {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
00170         uint32 minor_status;
00171         int gss_rc;
00172 #endif
00173 
00174         ZERO_STRUCTP(p);
00175 
00176         /* I've seen a child Windows 2000 domain not send
00177            the principal name back in the first round of
00178            the SASL bind reply.  So we guess based on server
00179            name and realm.  --jerry  */
00180         /* Also try best guess when we get the w2k8 ignore
00181            principal back - gd */
00182 
00183         if (!given_principal ||
00184             strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
00185 
00186                 status = ads_guess_service_principal(ads, &p->string);
00187                 if (!ADS_ERR_OK(status)) {
00188                         return status;
00189                 }
00190         } else {
00191                 p->string = SMB_STRDUP(given_principal);
00192                 if (!p->string) {
00193                         return ADS_ERROR(LDAP_NO_MEMORY);
00194                 }
00195         }
00196 
00197 #ifdef HAVE_GSSAPI
00198         input_name.value = p->string;
00199         input_name.length = strlen(p->string);
00200 
00201         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
00202         if (gss_rc) {
00203                 ads_free_service_principal(p);
00204                 return ADS_ERROR_GSS(gss_rc, minor_status);
00205         }
00206 #endif
00207 
00208         return ADS_SUCCESS;
00209 }
00210 
00211 /* 
00212    perform a LDAP/SASL/SPNEGO/KRB5 bind
00213 */
00214 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
00215 {
00216         DATA_BLOB blob = data_blob(NULL, 0);
00217         struct berval cred, *scred = NULL;
00218         DATA_BLOB session_key = data_blob(NULL, 0);
00219         int rc;
00220 
00221         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
00222                                      &ads->auth.tgs_expire);
00223 
00224         if (rc) {
00225                 return ADS_ERROR_KRB5(rc);
00226         }
00227 
00228         /* now send the auth packet and we should be done */
00229         cred.bv_val = (char *)blob.data;
00230         cred.bv_len = blob.length;
00231 
00232         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
00233 
00234         data_blob_free(&blob);
00235         data_blob_free(&session_key);
00236         if(scred)
00237                 ber_bvfree(scred);
00238 
00239         return ADS_ERROR(rc);
00240 }
00241 
00242 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
00243                                             struct ads_service_principal *p)
00244 {
00245         return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
00246 }
00247 
00248 #endif
00249 
00250 /* 
00251    this performs a SASL/SPNEGO bind
00252 */
00253 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
00254 {
00255         struct berval *scred=NULL;
00256         int rc, i;
00257         ADS_STATUS status;
00258         DATA_BLOB blob;
00259         char *given_principal = NULL;
00260         char *OIDs[ASN1_MAX_OIDS];
00261 #ifdef HAVE_KRB5
00262         BOOL got_kerberos_mechanism = False;
00263 #endif
00264 
00265         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
00266 
00267         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
00268                 status = ADS_ERROR(rc);
00269                 goto failed;
00270         }
00271 
00272         blob = data_blob(scred->bv_val, scred->bv_len);
00273 
00274         ber_bvfree(scred);
00275 
00276 #if 0
00277         file_save("sasl_spnego.dat", blob.data, blob.length);
00278 #endif
00279 
00280         /* the server sent us the first part of the SPNEGO exchange in the negprot 
00281            reply */
00282         if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) {
00283                 data_blob_free(&blob);
00284                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
00285                 goto failed;
00286         }
00287         data_blob_free(&blob);
00288 
00289         /* make sure the server understands kerberos */
00290         for (i=0;OIDs[i];i++) {
00291                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
00292 #ifdef HAVE_KRB5
00293                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
00294                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
00295                         got_kerberos_mechanism = True;
00296                 }
00297 #endif
00298                 free(OIDs[i]);
00299         }
00300         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
00301 
00302 #ifdef HAVE_KRB5
00303         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
00304             got_kerberos_mechanism) 
00305         {
00306                 struct ads_service_principal p;
00307 
00308                 status = ads_generate_service_principal(ads, given_principal, &p);
00309                 SAFE_FREE(given_principal);
00310                 if (!ADS_ERR_OK(status)) {
00311                         return status;
00312                 }
00313 
00314                 status = ads_sasl_spnego_krb5_bind(ads, &p);
00315                 if (ADS_ERR_OK(status)) {
00316                         ads_free_service_principal(&p);
00317                         return status;
00318                 }
00319 
00320                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
00321                           "calling kinit\n", ads_errstr(status)));
00322 
00323                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
00324 
00325                 if (ADS_ERR_OK(status)) {
00326                         status = ads_sasl_spnego_krb5_bind(ads, &p);
00327                         if (!ADS_ERR_OK(status)) {
00328                                 DEBUG(0,("kinit succeeded but "
00329                                         "ads_sasl_spnego_krb5_bind failed: %s\n",
00330                                         ads_errstr(status)));
00331                         }
00332                 }
00333 
00334                 ads_free_service_principal(&p);
00335 
00336                 /* only fallback to NTLMSSP if allowed */
00337                 if (ADS_ERR_OK(status) || 
00338                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
00339                         return status;
00340                 }
00341         } else
00342 #endif
00343         {
00344                 SAFE_FREE(given_principal);
00345         }
00346 
00347         /* lets do NTLMSSP ... this has the big advantage that we don't need
00348            to sync clocks, and we don't rely on special versions of the krb5 
00349            library for HMAC_MD4 encryption */
00350         return ads_sasl_spnego_ntlmssp_bind(ads);
00351 
00352 failed:
00353         return status;
00354 }
00355 
00356 #ifdef HAVE_GSSAPI
00357 #define MAX_GSS_PASSES 3
00358 
00359 /* this performs a SASL/gssapi bind
00360    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
00361    is very dependent on correctly configured DNS whereas
00362    this routine is much less fragile
00363    see RFC2078 and RFC2222 for details
00364 */
00365 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
00366 {
00367         uint32 minor_status;
00368         gss_name_t serv_name;
00369         gss_buffer_desc input_name;
00370         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
00371         gss_OID mech_type = GSS_C_NULL_OID;
00372         gss_buffer_desc output_token, input_token;
00373         uint32 ret_flags, conf_state;
00374         struct berval cred;
00375         struct berval *scred = NULL;
00376         int i=0;
00377         int gss_rc, rc;
00378         uint8 *p;
00379         uint32 max_msg_size = 0;
00380         char *sname = NULL;
00381         ADS_STATUS status;
00382         krb5_principal principal = NULL;
00383         krb5_context ctx = NULL;
00384         krb5_enctype enc_types[] = {
00385 #ifdef ENCTYPE_ARCFOUR_HMAC
00386                         ENCTYPE_ARCFOUR_HMAC,
00387 #endif
00388                         ENCTYPE_DES_CBC_MD5,
00389                         ENCTYPE_NULL};
00390         gss_OID_desc nt_principal = 
00391         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
00392 
00393         /* we need to fetch a service ticket as the ldap user in the
00394            servers realm, regardless of our realm */
00395         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
00396 
00397         initialize_krb5_error_table();
00398         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
00399         if (!ADS_ERR_OK(status)) {
00400                 SAFE_FREE(sname);
00401                 return status;
00402         }
00403         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
00404         if (!ADS_ERR_OK(status)) {
00405                 SAFE_FREE(sname);
00406                 krb5_free_context(ctx); 
00407                 return status;
00408         }
00409         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
00410         if (!ADS_ERR_OK(status)) {
00411                 SAFE_FREE(sname);
00412                 krb5_free_context(ctx); 
00413                 return status;
00414         }
00415 
00416         input_name.value = &principal;
00417         input_name.length = sizeof(principal);
00418 
00419         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
00420 
00421         /*
00422          * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
00423          * to point to the *address* of the krb5_principal, and the gss libraries
00424          * to a shallow copy of the krb5_principal pointer - so we need to keep
00425          * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
00426          * Just one more way in which MIT engineers screwed me over.... JRA.
00427          */
00428 
00429         SAFE_FREE(sname);
00430 
00431         if (gss_rc) {
00432                 krb5_free_principal(ctx, principal);
00433                 krb5_free_context(ctx); 
00434                 return ADS_ERROR_GSS(gss_rc, minor_status);
00435         }
00436 
00437         input_token.value = NULL;
00438         input_token.length = 0;
00439 
00440         for (i=0; i < MAX_GSS_PASSES; i++) {
00441                 gss_rc = gss_init_sec_context(&minor_status,
00442                                           GSS_C_NO_CREDENTIAL,
00443                                           &context_handle,
00444                                           serv_name,
00445                                           mech_type,
00446                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
00447                                           0,
00448                                           NULL,
00449                                           &input_token,
00450                                           NULL,
00451                                           &output_token,
00452                                           &ret_flags,
00453                                           NULL);
00454 
00455                 if (input_token.value) {
00456                         gss_release_buffer(&minor_status, &input_token);
00457                 }
00458 
00459                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
00460                         status = ADS_ERROR_GSS(gss_rc, minor_status);
00461                         goto failed;
00462                 }
00463 
00464                 cred.bv_val = (char *)output_token.value;
00465                 cred.bv_len = output_token.length;
00466 
00467                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
00468                                       &scred);
00469                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
00470                         status = ADS_ERROR(rc);
00471                         goto failed;
00472                 }
00473 
00474                 if (output_token.value) {
00475                         gss_release_buffer(&minor_status, &output_token);
00476                 }
00477 
00478                 if (scred) {
00479                         input_token.value = scred->bv_val;
00480                         input_token.length = scred->bv_len;
00481                 } else {
00482                         input_token.value = NULL;
00483                         input_token.length = 0;
00484                 }
00485 
00486                 if (gss_rc == 0) break;
00487         }
00488 
00489         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
00490                             (int *)&conf_state,NULL);
00491         if (gss_rc) {
00492                 status = ADS_ERROR_GSS(gss_rc, minor_status);
00493                 goto failed;
00494         }
00495 
00496         gss_release_buffer(&minor_status, &input_token);
00497 
00498         p = (uint8 *)output_token.value;
00499 
00500 #if 0
00501         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
00502 #endif
00503 
00504         if (p) {
00505                 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
00506         }
00507 
00508         gss_release_buffer(&minor_status, &output_token);
00509 
00510         output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
00511         p = (uint8 *)output_token.value;
00512 
00513         *p++ = 1; /* no sign & seal selection */
00514         /* choose the same size as the server gave us */
00515         *p++ = max_msg_size>>16;
00516         *p++ = max_msg_size>>8;
00517         *p++ = max_msg_size;
00518         snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
00519         p += strlen((const char *)p);
00520 
00521         output_token.length = PTR_DIFF(p, output_token.value);
00522 
00523         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
00524                           &output_token, (int *)&conf_state,
00525                           &input_token);
00526         if (gss_rc) {
00527                 status = ADS_ERROR_GSS(gss_rc, minor_status);
00528                 goto failed;
00529         }
00530 
00531         free(output_token.value);
00532 
00533         cred.bv_val = (char *)input_token.value;
00534         cred.bv_len = input_token.length;
00535 
00536         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
00537                               &scred);
00538         status = ADS_ERROR(rc);
00539 
00540         gss_release_buffer(&minor_status, &input_token);
00541 
00542 failed:
00543 
00544         gss_release_name(&minor_status, &serv_name);
00545         if (context_handle != GSS_C_NO_CONTEXT)
00546                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
00547         krb5_free_principal(ctx, principal);
00548         krb5_free_context(ctx); 
00549 
00550         if(scred)
00551                 ber_bvfree(scred);
00552         return status;
00553 }
00554 #endif /* HAVE_GGSAPI */
00555 
00556 /* mapping between SASL mechanisms and functions */
00557 static struct {
00558         const char *name;
00559         ADS_STATUS (*fn)(ADS_STRUCT *);
00560 } sasl_mechanisms[] = {
00561         {"GSS-SPNEGO", ads_sasl_spnego_bind},
00562 #ifdef HAVE_GSSAPI
00563         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
00564 #endif
00565         {NULL, NULL}
00566 };
00567 
00568 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
00569 {
00570         const char *attrs[] = {"supportedSASLMechanisms", NULL};
00571         char **values;
00572         ADS_STATUS status;
00573         int i, j;
00574         LDAPMessage *res;
00575 
00576         /* get a list of supported SASL mechanisms */
00577         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
00578         if (!ADS_ERR_OK(status)) return status;
00579 
00580         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
00581 
00582         /* try our supported mechanisms in order */
00583         for (i=0;sasl_mechanisms[i].name;i++) {
00584                 /* see if the server supports it */
00585                 for (j=0;values && values[j];j++) {
00586                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
00587                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
00588                                 status = sasl_mechanisms[i].fn(ads);
00589                                 ldap_value_free(values);
00590                                 ldap_msgfree(res);
00591                                 return status;
00592                         }
00593                 }
00594         }
00595 
00596         ldap_value_free(values);
00597         ldap_msgfree(res);
00598         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
00599 }
00600 
00601 #endif /* HAVE_LDAP */
00602 

Sambaに対してSat Aug 29 21:23:01 2009に生成されました。  doxygen 1.4.7