root/source4/libcli/ldap/ldap_bind.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ldap_rebind
  2. new_ldap_simple_bind_msg
  3. ldap_bind_simple
  4. new_ldap_sasl_bind_msg
  5. ldap_bind_sasl

   1 /* 
   2    Unix SMB/CIFS mplementation.
   3 
   4    LDAP bind calls
   5    
   6    Copyright (C) Andrew Tridgell  2005
   7    Copyright (C) Volker Lendecke  2004
   8     
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 3 of the License, or
  12    (at your option) any later version.
  13    
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18    
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21    
  22 */
  23 
  24 #include "includes.h"
  25 #include "libcli/ldap/ldap.h"
  26 #include "libcli/ldap/ldap_proto.h"
  27 #include "libcli/ldap/ldap_client.h"
  28 #include "lib/tls/tls.h"
  29 #include "auth/gensec/gensec.h"
  30 #include "auth/credentials/credentials.h"
  31 #include "lib/stream/packet.h"
  32 #include "param/param.h"
  33 
  34 struct ldap_simple_creds {
  35         const char *dn;
  36         const char *pw;
  37 };
  38 
  39 _PUBLIC_ NTSTATUS ldap_rebind(struct ldap_connection *conn)
     /* [<][>][^][v][top][bottom][index][help] */
  40 {
  41         NTSTATUS status;
  42         struct ldap_simple_creds *creds;
  43 
  44         switch (conn->bind.type) {
  45         case LDAP_BIND_SASL:
  46                 status = ldap_bind_sasl(conn, (struct cli_credentials *)conn->bind.creds,
  47                                         conn->lp_ctx);
  48                 break;
  49                 
  50         case LDAP_BIND_SIMPLE:
  51                 creds = (struct ldap_simple_creds *)conn->bind.creds;
  52 
  53                 if (creds == NULL) {
  54                         return NT_STATUS_UNSUCCESSFUL;
  55                 }
  56 
  57                 status = ldap_bind_simple(conn, creds->dn, creds->pw);
  58                 break;
  59 
  60         default:
  61                 return NT_STATUS_UNSUCCESSFUL;
  62         }
  63 
  64         return status;
  65 }
  66 
  67 
  68 static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn, 
     /* [<][>][^][v][top][bottom][index][help] */
  69                                                      const char *dn, const char *pw)
  70 {
  71         struct ldap_message *res;
  72 
  73         res = new_ldap_message(conn);
  74         if (!res) {
  75                 return NULL;
  76         }
  77 
  78         res->type = LDAP_TAG_BindRequest;
  79         res->r.BindRequest.version = 3;
  80         res->r.BindRequest.dn = talloc_strdup(res, dn);
  81         res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
  82         res->r.BindRequest.creds.password = talloc_strdup(res, pw);
  83         res->controls = NULL;
  84 
  85         return res;
  86 }
  87 
  88 
  89 /*
  90   perform a simple username/password bind
  91 */
  92 _PUBLIC_ NTSTATUS ldap_bind_simple(struct ldap_connection *conn, 
     /* [<][>][^][v][top][bottom][index][help] */
  93                           const char *userdn, const char *password)
  94 {
  95         struct ldap_request *req;
  96         struct ldap_message *msg;
  97         const char *dn, *pw;
  98         NTSTATUS status;
  99 
 100         if (conn == NULL) {
 101                 return NT_STATUS_INVALID_CONNECTION;
 102         }
 103 
 104         if (userdn) {
 105                 dn = userdn;
 106         } else {
 107                 if (conn->auth_dn) {
 108                         dn = conn->auth_dn;
 109                 } else {
 110                         dn = "";
 111                 }
 112         }
 113 
 114         if (password) {
 115                 pw = password;
 116         } else {
 117                 if (conn->simple_pw) {
 118                         pw = conn->simple_pw;
 119                 } else {
 120                         pw = "";
 121                 }
 122         }
 123 
 124         msg = new_ldap_simple_bind_msg(conn, dn, pw);
 125         NT_STATUS_HAVE_NO_MEMORY(msg);
 126 
 127         /* send the request */
 128         req = ldap_request_send(conn, msg);
 129         talloc_free(msg);
 130         NT_STATUS_HAVE_NO_MEMORY(req);
 131 
 132         /* wait for replies */
 133         status = ldap_request_wait(req);
 134         if (!NT_STATUS_IS_OK(status)) {
 135                 talloc_free(req);
 136                 return status;
 137         }
 138 
 139         /* check its a valid reply */
 140         msg = req->replies[0];
 141         if (msg->type != LDAP_TAG_BindResponse) {
 142                 talloc_free(req);
 143                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
 144         }
 145 
 146         status = ldap_check_response(conn, &msg->r.BindResponse.response);
 147 
 148         talloc_free(req);
 149 
 150         if (NT_STATUS_IS_OK(status)) {
 151                 struct ldap_simple_creds *creds = talloc(conn, struct ldap_simple_creds);
 152                 if (creds == NULL) {
 153                         return NT_STATUS_NO_MEMORY;
 154                 }
 155                 creds->dn = talloc_strdup(creds, dn);
 156                 creds->pw = talloc_strdup(creds, pw);
 157                 if (creds->dn == NULL || creds->pw == NULL) {
 158                         return NT_STATUS_NO_MEMORY;
 159                 }
 160                 conn->bind.type = LDAP_BIND_SIMPLE;
 161                 conn->bind.creds = creds;
 162         }
 163 
 164         return status;
 165 }
 166 
 167 
 168 static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn, 
     /* [<][>][^][v][top][bottom][index][help] */
 169                                                    const char *sasl_mechanism, 
 170                                                    DATA_BLOB *secblob)
 171 {
 172         struct ldap_message *res;
 173 
 174         res = new_ldap_message(conn);
 175         if (!res) {
 176                 return NULL;
 177         }
 178 
 179         res->type = LDAP_TAG_BindRequest;
 180         res->r.BindRequest.version = 3;
 181         res->r.BindRequest.dn = "";
 182         res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
 183         res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism);
 184         if (secblob) {
 185                 res->r.BindRequest.creds.SASL.secblob = talloc(res, DATA_BLOB);
 186                 if (!res->r.BindRequest.creds.SASL.secblob) {
 187                         talloc_free(res);
 188                         return NULL;
 189                 }
 190                 *res->r.BindRequest.creds.SASL.secblob = *secblob;
 191         } else {
 192                 res->r.BindRequest.creds.SASL.secblob = NULL;
 193         }
 194         res->controls = NULL;
 195 
 196         return res;
 197 }
 198 
 199 
 200 /*
 201   perform a sasl bind using the given credentials
 202 */
 203 _PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
     /* [<][>][^][v][top][bottom][index][help] */
 204                         struct cli_credentials *creds,
 205                         struct loadparm_context *lp_ctx)
 206 {
 207         NTSTATUS status;
 208         TALLOC_CTX *tmp_ctx = NULL;
 209 
 210         DATA_BLOB input = data_blob(NULL, 0);
 211         DATA_BLOB output = data_blob(NULL, 0);
 212 
 213         struct ldap_message **sasl_mechs_msgs;
 214         struct ldap_SearchResEntry *search;
 215         int count, i;
 216 
 217         const char **sasl_names;
 218         uint32_t old_gensec_features;
 219         static const char *supported_sasl_mech_attrs[] = {
 220                 "supportedSASLMechanisms", 
 221                 NULL 
 222         };
 223 
 224         gensec_init(lp_ctx);
 225 
 226         status = gensec_client_start(conn, &conn->gensec,
 227                                      conn->event.event_ctx, 
 228                                      lp_gensec_settings(conn, lp_ctx));
 229         if (!NT_STATUS_IS_OK(status)) {
 230                 DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
 231                 goto failed;
 232         }
 233 
 234         /* require Kerberos SIGN/SEAL only if we don't use SSL
 235          * Windows seem not to like double encryption */
 236         old_gensec_features = cli_credentials_get_gensec_features(creds);
 237         if (tls_enabled(conn->sock)) {
 238                 cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL));
 239         }
 240 
 241         /* this call also sets the gensec_want_features */
 242         status = gensec_set_credentials(conn->gensec, creds);
 243         if (!NT_STATUS_IS_OK(status)) {
 244                 DEBUG(1, ("Failed to set GENSEC creds: %s\n", 
 245                           nt_errstr(status)));
 246                 goto failed;
 247         }
 248 
 249         /* reset the original gensec_features (on the credentials
 250          * context, so we don't tatoo it ) */
 251         cli_credentials_set_gensec_features(creds, old_gensec_features);
 252 
 253         if (conn->host) {
 254                 status = gensec_set_target_hostname(conn->gensec, conn->host);
 255                 if (!NT_STATUS_IS_OK(status)) {
 256                         DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
 257                                   nt_errstr(status)));
 258                         goto failed;
 259                 }
 260         }
 261 
 262         status = gensec_set_target_service(conn->gensec, "ldap");
 263         if (!NT_STATUS_IS_OK(status)) {
 264                 DEBUG(1, ("Failed to set GENSEC target service: %s\n", 
 265                           nt_errstr(status)));
 266                 goto failed;
 267         }
 268 
 269         status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs, 
 270                               false, NULL, NULL, &sasl_mechs_msgs);
 271         if (!NT_STATUS_IS_OK(status)) {
 272                 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n", 
 273                           nt_errstr(status)));
 274                 goto failed;
 275         }
 276         
 277         count = ildap_count_entries(conn, sasl_mechs_msgs);
 278         if (count != 1) {
 279                 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
 280                           count));
 281                 goto failed;
 282         }
 283 
 284         tmp_ctx = talloc_new(conn);
 285         if (tmp_ctx == NULL) goto failed;
 286 
 287         search = &sasl_mechs_msgs[0]->r.SearchResultEntry;
 288         if (search->num_attributes != 1) {
 289                 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
 290                           search->num_attributes));
 291                 goto failed;
 292         }
 293 
 294         sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1);
 295         if (!sasl_names) {
 296                 DEBUG(1, ("talloc_arry(char *, %d) failed\n",
 297                           count));
 298                 goto failed;
 299         }
 300                 
 301         for (i=0; i<search->attributes[0].num_values; i++) {
 302                 sasl_names[i] = (const char *)search->attributes[0].values[i].data;
 303         }
 304         sasl_names[i] = NULL;
 305         
 306         status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names);
 307         if (!NT_STATUS_IS_OK(status)) {
 308                 DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
 309                           count, nt_errstr(status)));
 310                 goto failed;
 311         }
 312 
 313         while (1) {
 314                 NTSTATUS gensec_status;
 315                 struct ldap_message *response;
 316                 struct ldap_message *msg;
 317                 struct ldap_request *req;
 318                 int result = LDAP_OTHER;
 319         
 320                 status = gensec_update(conn->gensec, tmp_ctx,
 321                                        input,
 322                                        &output);
 323                 /* The status value here, from GENSEC is vital to the security
 324                  * of the system.  Even if the other end accepts, if GENSEC
 325                  * claims 'MORE_PROCESSING_REQUIRED' then you must keep
 326                  * feeding it blobs, or else the remote host/attacker might
 327                  * avoid mutal authentication requirements.
 328                  *
 329                  * Likewise, you must not feed GENSEC too much (after the OK),
 330                  * it doesn't like that either
 331                  */
 332 
 333                 gensec_status = status;
 334 
 335                 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
 336                     !NT_STATUS_IS_OK(status)) {
 337                         break;
 338                 }
 339                 if (NT_STATUS_IS_OK(status) && output.length == 0) {
 340                         break;
 341                 }
 342 
 343                 /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
 344                 msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL));
 345                 if (msg == NULL) {
 346                         status = NT_STATUS_NO_MEMORY;
 347                         goto failed;
 348                 }
 349 
 350                 req = ldap_request_send(conn, msg);
 351                 if (req == NULL) {
 352                         status = NT_STATUS_NO_MEMORY;
 353                         goto failed;
 354                 }
 355                 talloc_steal(tmp_ctx, req);
 356 
 357                 status = ldap_result_n(req, 0, &response);
 358                 if (!NT_STATUS_IS_OK(status)) {
 359                         goto failed;
 360                 }
 361                 
 362                 if (response->type != LDAP_TAG_BindResponse) {
 363                         status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
 364                         goto failed;
 365                 }
 366 
 367                 result = response->r.BindResponse.response.resultcode;
 368 
 369                 if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
 370                         status = ldap_check_response(conn, 
 371                                                      &response->r.BindResponse.response);
 372                         break;
 373                 }
 374 
 375                 /* This is where we check if GENSEC wanted to be fed more data */
 376                 if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 377                         break;
 378                 }
 379                 if (response->r.BindResponse.SASL.secblob) {
 380                         input = *response->r.BindResponse.SASL.secblob;
 381                 } else {
 382                         input = data_blob(NULL, 0);
 383                 }
 384         }
 385 
 386         talloc_free(tmp_ctx);
 387 
 388         if (NT_STATUS_IS_OK(status)) {
 389                 struct socket_context *sasl_socket;
 390                 status = gensec_socket_init(conn->gensec, 
 391                                             conn,
 392                                             conn->sock,
 393                                             conn->event.event_ctx, 
 394                                             ldap_read_io_handler,
 395                                             conn,
 396                                             &sasl_socket);
 397                 if (!NT_STATUS_IS_OK(status)) goto failed;
 398 
 399                 conn->sock = sasl_socket;
 400                 packet_set_socket(conn->packet, conn->sock);
 401 
 402                 conn->bind.type = LDAP_BIND_SASL;
 403                 conn->bind.creds = creds;
 404         }
 405 
 406         return status;
 407 
 408 failed:
 409         talloc_free(tmp_ctx);
 410         talloc_free(conn->gensec);
 411         conn->gensec = NULL;
 412         return status;
 413 }

/* [<][>][^][v][top][bottom][index][help] */