root/source4/auth/kerberos/krb5_init_context.c

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

DEFINITIONS

This source file includes following definitions.
  1. smb_krb5_context_destroy_1
  2. smb_krb5_context_destroy_2
  3. smb_krb5_debug_close
  4. smb_krb5_debug_wrapper
  5. smb_krb5_socket_recv
  6. smb_krb5_full_packet
  7. smb_krb5_request_timeout
  8. smb_krb5_error_handler
  9. smb_krb5_socket_send
  10. smb_krb5_socket_handler
  11. smb_krb5_send_and_recv_func
  12. smb_krb5_init_context

   1 /* 
   2    Unix SMB/CIFS implementation.
   3    Wrapper for krb5_init_context
   4 
   5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
   6    Copyright (C) Andrew Tridgell 2005
   7    Copyright (C) Stefan Metzmacher 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 #include "includes.h"
  24 #include "system/kerberos.h"
  25 #include <tevent.h>
  26 #include "auth/kerberos/kerberos.h"
  27 #include "lib/socket/socket.h"
  28 #include "lib/stream/packet.h"
  29 #include "system/network.h"
  30 #include "param/param.h"
  31 #include "libcli/resolve/resolve.h"
  32 
  33 /*
  34   context structure for operations on cldap packets
  35 */
  36 struct smb_krb5_socket {
  37         struct socket_context *sock;
  38 
  39         /* the fd event */
  40         struct tevent_fd *fde;
  41 
  42         NTSTATUS status;
  43         DATA_BLOB request, reply;
  44         
  45         struct packet_context *packet;
  46 
  47         size_t partial_read;
  48 
  49         krb5_krbhst_info *hi;
  50 };
  51 
  52 static krb5_error_code smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
  53 {
  54         krb5_free_context(ctx->krb5_context); 
  55         return 0;
  56 }
  57 
  58 static krb5_error_code smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
  59 {
  60         /* Otherwise krb5_free_context will try and close what we have already free()ed */
  61         krb5_set_warn_dest(ctx->krb5_context, NULL);
  62         krb5_closelog(ctx->krb5_context, ctx->logf);
  63         smb_krb5_context_destroy_1(ctx);
  64         return 0;
  65 }
  66 
  67 /* We never close down the DEBUG system, and no need to unreference the use */
  68 static void smb_krb5_debug_close(void *private_data) {
     /* [<][>][^][v][top][bottom][index][help] */
  69         return;
  70 }
  71 
  72 static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private_data)
     /* [<][>][^][v][top][bottom][index][help] */
  73 {
  74         DEBUG(2, ("Kerberos: %s\n", msg));
  75 }
  76 
  77 /*
  78   handle recv events on a smb_krb5 socket
  79 */
  80 static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
     /* [<][>][^][v][top][bottom][index][help] */
  81 {
  82         TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
  83         DATA_BLOB blob;
  84         size_t nread, dsize;
  85 
  86         smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
  87         if (!NT_STATUS_IS_OK(smb_krb5->status)) {
  88                 talloc_free(tmp_ctx);
  89                 return;
  90         }
  91         
  92         blob = data_blob_talloc(tmp_ctx, NULL, dsize);
  93         if (blob.data == NULL && dsize != 0) {
  94                 smb_krb5->status = NT_STATUS_NO_MEMORY;
  95                 talloc_free(tmp_ctx);
  96                 return;
  97         }
  98         
  99         smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
 100         if (!NT_STATUS_IS_OK(smb_krb5->status)) {
 101                 talloc_free(tmp_ctx);
 102                 return;
 103         }
 104         blob.length = nread;
 105         
 106         if (nread == 0) {
 107                 smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
 108                 talloc_free(tmp_ctx);
 109                 return;
 110         }
 111         
 112         DEBUG(2,("Received smb_krb5 packet of length %d\n", 
 113                  (int)blob.length));
 114         
 115         talloc_steal(smb_krb5, blob.data);
 116         smb_krb5->reply = blob;
 117         talloc_free(tmp_ctx);
 118 }
 119 
 120 static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
     /* [<][>][^][v][top][bottom][index][help] */
 121 {
 122         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
 123         talloc_steal(smb_krb5, data.data);
 124         smb_krb5->reply = data;
 125         smb_krb5->reply.length -= 4;
 126         smb_krb5->reply.data += 4;
 127         return NT_STATUS_OK;
 128 }
 129 
 130 /*
 131   handle request timeouts
 132 */
 133 static void smb_krb5_request_timeout(struct tevent_context *event_ctx, 
     /* [<][>][^][v][top][bottom][index][help] */
 134                                   struct tevent_timer *te, struct timeval t,
 135                                   void *private_data)
 136 {
 137         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
 138         DEBUG(5,("Timed out smb_krb5 packet\n"));
 139         smb_krb5->status = NT_STATUS_IO_TIMEOUT;
 140 }
 141 
 142 static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
     /* [<][>][^][v][top][bottom][index][help] */
 143 {
 144         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
 145         smb_krb5->status = status;
 146 }
 147 
 148 /*
 149   handle send events on a smb_krb5 socket
 150 */
 151 static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
     /* [<][>][^][v][top][bottom][index][help] */
 152 {
 153         NTSTATUS status;
 154 
 155         size_t len;
 156         
 157         len = smb_krb5->request.length;
 158         status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
 159 
 160         if (!NT_STATUS_IS_OK(status)) return;
 161         
 162         TEVENT_FD_READABLE(smb_krb5->fde);
 163 
 164         TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
 165         return;
 166 }
 167 
 168 
 169 /*
 170   handle fd events on a smb_krb5_socket
 171 */
 172 static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
     /* [<][>][^][v][top][bottom][index][help] */
 173                                  uint16_t flags, void *private_data)
 174 {
 175         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
 176         switch (smb_krb5->hi->proto) {
 177         case KRB5_KRBHST_UDP:
 178                 if (flags & TEVENT_FD_READ) {
 179                         smb_krb5_socket_recv(smb_krb5);
 180                         return;
 181                 }
 182                 if (flags & TEVENT_FD_WRITE) {
 183                         smb_krb5_socket_send(smb_krb5);
 184                         return;
 185                 }
 186                 /* not reached */
 187                 return;
 188         case KRB5_KRBHST_TCP:
 189                 if (flags & TEVENT_FD_READ) {
 190                         packet_recv(smb_krb5->packet);
 191                         return;
 192                 }
 193                 if (flags & TEVENT_FD_WRITE) {
 194                         packet_queue_run(smb_krb5->packet);
 195                         return;
 196                 }
 197                 /* not reached */
 198                 return;
 199         case KRB5_KRBHST_HTTP:
 200                 /* can't happen */
 201                 break;
 202         }
 203 }
 204 
 205 
 206 krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 207                                             void *data,
 208                                             krb5_krbhst_info *hi,
 209                                             time_t timeout,
 210                                             const krb5_data *send_buf,
 211                                             krb5_data *recv_buf)
 212 {
 213         krb5_error_code ret;
 214         NTSTATUS status;
 215         struct socket_address *remote_addr;
 216         const char *name;
 217         struct addrinfo *ai, *a;
 218         struct smb_krb5_socket *smb_krb5;
 219 
 220         struct tevent_context *ev = talloc_get_type(data, struct tevent_context);
 221 
 222         DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
 223 
 224         ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
 225         if (ret) {
 226                 return ret;
 227         }
 228 
 229         for (a = ai; a; a = ai->ai_next) {
 230                 smb_krb5 = talloc(NULL, struct smb_krb5_socket);
 231                 if (!smb_krb5) {
 232                         return ENOMEM;
 233                 }
 234                 smb_krb5->hi = hi;
 235                 
 236                 switch (a->ai_family) {
 237                 case PF_INET:
 238                         name = "ipv4";
 239                         break;
 240 #ifdef HAVE_IPV6
 241                 case PF_INET6:
 242                         name = "ipv6";
 243                         break;
 244 #endif
 245                 default:
 246                         talloc_free(smb_krb5);
 247                         return EINVAL;
 248                 }
 249                 
 250                 status = NT_STATUS_INVALID_PARAMETER;
 251                 switch (hi->proto) {
 252                 case KRB5_KRBHST_UDP:
 253                         status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
 254                         break;
 255                 case KRB5_KRBHST_TCP:
 256                         status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
 257                         break;
 258                 case KRB5_KRBHST_HTTP:
 259                         talloc_free(smb_krb5);
 260                         return EINVAL;
 261                 }
 262                 if (!NT_STATUS_IS_OK(status)) {
 263                         talloc_free(smb_krb5);
 264                         continue;
 265                 }
 266 
 267                 talloc_steal(smb_krb5, smb_krb5->sock);
 268                 
 269                 remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen); 
 270                 if (!remote_addr) {
 271                         talloc_free(smb_krb5);
 272                         continue;
 273                 }
 274 
 275                 status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
 276                 if (!NT_STATUS_IS_OK(status)) {
 277                         talloc_free(smb_krb5);
 278                         continue;
 279                 }
 280                 talloc_free(remote_addr);
 281 
 282                 /* Setup the FDE, start listening for read events
 283                  * from the start (otherwise we may miss a socket
 284                  * drop) and mark as AUTOCLOSE along with the fde */
 285 
 286                 /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
 287                 smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
 288                                               socket_get_fd(smb_krb5->sock),
 289                                               TEVENT_FD_READ,
 290                                               smb_krb5_socket_handler, smb_krb5);
 291                 /* its now the job of the event layer to close the socket */
 292                 tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
 293                 socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
 294 
 295                 tevent_add_timer(ev, smb_krb5,
 296                                  timeval_current_ofs(timeout, 0),
 297                                  smb_krb5_request_timeout, smb_krb5);
 298 
 299                 smb_krb5->status = NT_STATUS_OK;
 300                 smb_krb5->reply = data_blob(NULL, 0);
 301 
 302                 switch (hi->proto) {
 303                 case KRB5_KRBHST_UDP:
 304                         TEVENT_FD_WRITEABLE(smb_krb5->fde);
 305                         smb_krb5->request = send_blob;
 306                         break;
 307                 case KRB5_KRBHST_TCP:
 308 
 309                         smb_krb5->packet = packet_init(smb_krb5);
 310                         if (smb_krb5->packet == NULL) {
 311                                 talloc_free(smb_krb5);
 312                                 return ENOMEM;
 313                         }
 314                         packet_set_private(smb_krb5->packet, smb_krb5);
 315                         packet_set_socket(smb_krb5->packet, smb_krb5->sock);
 316                         packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
 317                         packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
 318                         packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
 319                         packet_set_event_context(smb_krb5->packet, ev);
 320                         packet_set_fde(smb_krb5->packet, smb_krb5->fde);
 321 
 322                         smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
 323                         RSIVAL(smb_krb5->request.data, 0, send_blob.length);
 324                         memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
 325                         packet_send(smb_krb5->packet, smb_krb5->request);
 326                         break;
 327                 case KRB5_KRBHST_HTTP:
 328                         talloc_free(smb_krb5);
 329                         return EINVAL;
 330                 }
 331                 while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
 332                         if (tevent_loop_once(ev) != 0) {
 333                                 talloc_free(smb_krb5);
 334                                 return EINVAL;
 335                         }
 336                 }
 337                 if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
 338                         talloc_free(smb_krb5);
 339                         continue;
 340                 }
 341 
 342                 if (!NT_STATUS_IS_OK(smb_krb5->status)) {
 343                         DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
 344                         talloc_free(smb_krb5);
 345                         continue;
 346                 }
 347 
 348                 ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
 349                 if (ret) {
 350                         talloc_free(smb_krb5);
 351                         return ret;
 352                 }
 353                 talloc_free(smb_krb5);
 354                 
 355                 break;
 356         }
 357         if (a) {
 358                 return 0;
 359         }
 360         return KRB5_KDC_UNREACH;
 361 }
 362 
 363 krb5_error_code smb_krb5_init_context(void *parent_ctx, 
     /* [<][>][^][v][top][bottom][index][help] */
 364                                       struct tevent_context *ev,
 365                                       struct loadparm_context *lp_ctx,
 366                                        struct smb_krb5_context **smb_krb5_context) 
 367 {
 368         krb5_error_code ret;
 369         TALLOC_CTX *tmp_ctx;
 370         char **config_files;
 371         const char *config_file;
 372         
 373         initialize_krb5_error_table();
 374         
 375         tmp_ctx = talloc_new(parent_ctx);
 376         *smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
 377 
 378         if (!*smb_krb5_context || !tmp_ctx) {
 379                 talloc_free(tmp_ctx);
 380                 return ENOMEM;
 381         }
 382 
 383         ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
 384         if (ret) {
 385                 DEBUG(1,("krb5_init_context failed (%s)\n", 
 386                          error_message(ret)));
 387                 talloc_free(tmp_ctx);
 388                 return ret;
 389         }
 390 
 391         talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
 392 
 393         config_file = config_path(tmp_ctx, lp_ctx, "krb5.conf");
 394         if (!config_file) {
 395                 talloc_free(tmp_ctx);
 396                 return ENOMEM;
 397         }
 398                 
 399         /* Use our local krb5.conf file by default */
 400         ret = krb5_prepend_config_files_default(config_file == NULL?"":config_file, &config_files);
 401         if (ret) {
 402                 DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n", 
 403                          smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
 404                 talloc_free(tmp_ctx);
 405                 return ret;
 406         }
 407 
 408         ret = krb5_set_config_files((*smb_krb5_context)->krb5_context, 
 409                                     config_files);
 410         krb5_free_config_files(config_files);
 411         if (ret) {
 412                 DEBUG(1,("krb5_set_config_files failed (%s)\n", 
 413                          smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
 414                 talloc_free(tmp_ctx);
 415                 return ret;
 416         }
 417                                                 
 418         if (lp_realm(lp_ctx) && *lp_realm(lp_ctx)) {
 419                 char *upper_realm = strupper_talloc(tmp_ctx, lp_realm(lp_ctx));
 420                 if (!upper_realm) {
 421                         DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm(lp_ctx)));
 422                         talloc_free(tmp_ctx);
 423                         return ENOMEM;
 424                 }
 425                 ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
 426                 if (ret) {
 427                         DEBUG(1,("krb5_set_default_realm failed (%s)\n", 
 428                                  smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
 429                         talloc_free(tmp_ctx);
 430                         return ret;
 431                 }
 432         }
 433 
 434         /* TODO: Should we have a different name here? */
 435         ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
 436         
 437         if (ret) {
 438                 DEBUG(1,("krb5_initlog failed (%s)\n", 
 439                          smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
 440                 talloc_free(tmp_ctx);
 441                 return ret;
 442         }
 443 
 444         talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
 445 
 446         ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */, 
 447                                smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
 448         if (ret) {
 449                 DEBUG(1,("krb5_addlog_func failed (%s)\n", 
 450                          smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
 451                 talloc_free(tmp_ctx);
 452                 return ret;
 453         }
 454         krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
 455 
 456         /* Set use of our socket lib */
 457         ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context, 
 458                                         smb_krb5_send_and_recv_func, 
 459                                         ev);
 460         if (ret) {
 461                 DEBUG(1,("krb5_set_send_recv_func failed (%s)\n", 
 462                          smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
 463                 talloc_free(tmp_ctx);
 464                 return ret;
 465         }
 466 
 467         talloc_steal(parent_ctx, *smb_krb5_context);
 468         talloc_free(tmp_ctx);
 469 
 470         /* Set options in kerberos */
 471 
 472         krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context,
 473                                            lp_parm_bool(lp_ctx, NULL, "krb5", "set_dns_canonicalize", false));
 474 
 475         return 0;
 476 }
 477 

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