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*/ 36struct 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 52static krb5_error_code smb_krb5_context_destroy_1(struct smb_krb5_context *ctx) 53{ 54 krb5_free_context(ctx->krb5_context); 55 return 0; 56} 57 58static krb5_error_code smb_krb5_context_destroy_2(struct smb_krb5_context *ctx) 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 */ 68static void smb_krb5_debug_close(void *private_data) { 69 return; 70} 71 72static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private_data) 73{ 74 DEBUG(2, ("Kerberos: %s\n", msg)); 75} 76 77/* 78 handle recv events on a smb_krb5 socket 79*/ 80static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5) 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 120static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data) 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*/ 133static void smb_krb5_request_timeout(struct tevent_context *event_ctx, 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 142static void smb_krb5_error_handler(void *private_data, NTSTATUS status) 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*/ 151static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5) 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*/ 172static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde, 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 206krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, 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 363krb5_error_code smb_krb5_init_context(void *parent_ctx, 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 478