krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, void *data, krb5_krbhst_info *hi, time_t timeout, const krb5_data *send_buf, krb5_data *recv_buf) { krb5_error_code ret; NTSTATUS status; struct socket_address *remote_addr; const char *name; struct addrinfo *ai, *a; struct smb_krb5_socket *smb_krb5; struct tevent_context *ev = talloc_get_type(data, struct tevent_context); DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length); ret = krb5_krbhst_get_addrinfo(context, hi, &ai); if (ret) { return ret; } for (a = ai; a; a = ai->ai_next) { smb_krb5 = talloc(NULL, struct smb_krb5_socket); if (!smb_krb5) { return ENOMEM; } smb_krb5->hi = hi; switch (a->ai_family) { case PF_INET: name = "ipv4"; break; #ifdef HAVE_IPV6 case PF_INET6: name = "ipv6"; break; #endif default: talloc_free(smb_krb5); return EINVAL; } status = NT_STATUS_INVALID_PARAMETER; switch (hi->proto) { case KRB5_KRBHST_UDP: status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_TCP: status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_HTTP: talloc_free(smb_krb5); return EINVAL; } if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } talloc_steal(smb_krb5, smb_krb5->sock); remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen); if (!remote_addr) { talloc_free(smb_krb5); continue; } status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev); if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } talloc_free(remote_addr); /* Setup the FDE, start listening for read events * from the start (otherwise we may miss a socket * drop) and mark as AUTOCLOSE along with the fde */ /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */ smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock, socket_get_fd(smb_krb5->sock), TEVENT_FD_READ, smb_krb5_socket_handler, smb_krb5); /* its now the job of the event layer to close the socket */ tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn); socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE); tevent_add_timer(ev, smb_krb5, timeval_current_ofs(timeout, 0), smb_krb5_request_timeout, smb_krb5); smb_krb5->status = NT_STATUS_OK; smb_krb5->reply = data_blob(NULL, 0); switch (hi->proto) { case KRB5_KRBHST_UDP: TEVENT_FD_WRITEABLE(smb_krb5->fde); smb_krb5->request = send_blob; break; case KRB5_KRBHST_TCP: smb_krb5->packet = packet_init(smb_krb5); if (smb_krb5->packet == NULL) { talloc_free(smb_krb5); return ENOMEM; } packet_set_private(smb_krb5->packet, smb_krb5); packet_set_socket(smb_krb5->packet, smb_krb5->sock); packet_set_callback(smb_krb5->packet, smb_krb5_full_packet); packet_set_full_request(smb_krb5->packet, packet_full_request_u32); packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler); packet_set_event_context(smb_krb5->packet, ev); packet_set_fde(smb_krb5->packet, smb_krb5->fde); smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4); RSIVAL(smb_krb5->request.data, 0, send_blob.length); memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length); packet_send(smb_krb5->packet, smb_krb5->request); break; case KRB5_KRBHST_HTTP: talloc_free(smb_krb5); return EINVAL; } while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) { if (tevent_loop_once(ev) != 0) { talloc_free(smb_krb5); return EINVAL; } } if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) { talloc_free(smb_krb5); continue; } if (!NT_STATUS_IS_OK(smb_krb5->status)) { DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status))); talloc_free(smb_krb5); continue; } ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length); if (ret) { talloc_free(smb_krb5); return ret; } talloc_free(smb_krb5); break; } if (a) { return 0; } return KRB5_KDC_UNREACH; }
/* basic testing of tcp routines */ static bool test_tcp(struct torture_context *tctx) { struct socket_context *sock1, *sock2, *sock3; NTSTATUS status; struct socket_address *srv_addr, *from_addr, *localhost; size_t size = 100 + (random() % 100); DATA_BLOB blob, blob2; size_t sent, nread; TALLOC_CTX *mem_ctx = tctx; struct tevent_context *ev = tctx->ev; struct interface *ifaces; status = socket_create("ip", SOCKET_TYPE_STREAM, &sock1, 0); torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1"); talloc_steal(mem_ctx, sock1); status = socket_create("ip", SOCKET_TYPE_STREAM, &sock2, 0); torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1"); talloc_steal(mem_ctx, sock2); load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces); localhost = socket_address_from_strings(sock1, sock1->backend_name, iface_best_ip(ifaces, "127.0.0.1"), 0); torture_assert(tctx, localhost, "Localhost not found"); status = socket_listen(sock1, localhost, 0, 0); torture_assert_ntstatus_ok(tctx, status, "listen on socket 1"); srv_addr = socket_get_my_addr(sock1, mem_ctx); torture_assert(tctx, srv_addr && srv_addr->addr, "Unexpected socket_get_my_addr NULL\n"); torture_assert_str_equal(tctx, srv_addr->addr, iface_best_ip(ifaces, "127.0.0.1"), "Unexpected server address"); torture_comment(tctx, "server port is %d\n", srv_addr->port); status = socket_connect_ev(sock2, NULL, srv_addr, 0, ev); torture_assert_ntstatus_ok(tctx, status, "connect() on socket 2"); status = socket_accept(sock1, &sock3); torture_assert_ntstatus_ok(tctx, status, "accept() on socket 1"); talloc_steal(mem_ctx, sock3); talloc_free(sock1); blob = data_blob_talloc(mem_ctx, NULL, size); blob2 = data_blob_talloc(mem_ctx, NULL, size); generate_random_buffer(blob.data, blob.length); sent = size; status = socket_send(sock2, &blob, &sent); torture_assert_ntstatus_ok(tctx, status, "send() on socket 2"); status = socket_recv(sock3, blob2.data, size, &nread); torture_assert_ntstatus_ok(tctx, status, "recv() on socket 3"); from_addr = socket_get_peer_addr(sock3, mem_ctx); torture_assert(tctx, from_addr && from_addr->addr, "Unexpected recvfrom addr NULL"); torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr, "Unexpected recvfrom addr"); torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size"); torture_assert_mem_equal(tctx, blob2.data, blob.data, size, "Bad data in recv"); return true; }
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, void *data, krb5_krbhst_info *hi, time_t timeout, const krb5_data *send_buf, krb5_data *recv_buf) { krb5_error_code ret; NTSTATUS status; const char *name; struct addrinfo *ai, *a; struct smb_krb5_socket *smb_krb5; DATA_BLOB send_blob; struct tevent_context *ev; TALLOC_CTX *tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { return ENOMEM; } if (!data) { /* If no event context was available, then create one for this loop */ ev = samba_tevent_context_init(tmp_ctx); if (!ev) { talloc_free(tmp_ctx); return ENOMEM; } } else { ev = talloc_get_type_abort(data, struct tevent_context); } send_blob = data_blob_const(send_buf->data, send_buf->length); ret = krb5_krbhst_get_addrinfo(context, hi, &ai); if (ret) { talloc_free(tmp_ctx); return ret; } for (a = ai; a; a = a->ai_next) { struct socket_address *remote_addr; smb_krb5 = talloc(tmp_ctx, struct smb_krb5_socket); if (!smb_krb5) { talloc_free(tmp_ctx); return ENOMEM; } smb_krb5->hi = hi; switch (a->ai_family) { case PF_INET: name = "ipv4"; break; #ifdef HAVE_IPV6 case PF_INET6: name = "ipv6"; break; #endif default: talloc_free(tmp_ctx); return EINVAL; } status = NT_STATUS_INVALID_PARAMETER; switch (hi->proto) { case KRB5_KRBHST_UDP: status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_TCP: status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_HTTP: talloc_free(tmp_ctx); return EINVAL; } if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } talloc_steal(smb_krb5, smb_krb5->sock); remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen); if (!remote_addr) { talloc_free(smb_krb5); continue; } status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev); if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } /* Setup the FDE, start listening for read events * from the start (otherwise we may miss a socket * drop) and mark as AUTOCLOSE along with the fde */ /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */ smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock, socket_get_fd(smb_krb5->sock), TEVENT_FD_READ, smb_krb5_socket_handler, smb_krb5); /* its now the job of the event layer to close the socket */ tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn); socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE); tevent_add_timer(ev, smb_krb5, timeval_current_ofs(timeout, 0), smb_krb5_request_timeout, smb_krb5); smb_krb5->status = NT_STATUS_OK; smb_krb5->reply = data_blob(NULL, 0); switch (hi->proto) { case KRB5_KRBHST_UDP: TEVENT_FD_WRITEABLE(smb_krb5->fde); smb_krb5->request = send_blob; break; case KRB5_KRBHST_TCP: smb_krb5->packet = packet_init(smb_krb5); if (smb_krb5->packet == NULL) { talloc_free(smb_krb5); return ENOMEM; } packet_set_private(smb_krb5->packet, smb_krb5); packet_set_socket(smb_krb5->packet, smb_krb5->sock); packet_set_callback(smb_krb5->packet, smb_krb5_full_packet); packet_set_full_request(smb_krb5->packet, packet_full_request_u32); packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler); packet_set_event_context(smb_krb5->packet, ev); packet_set_fde(smb_krb5->packet, smb_krb5->fde); smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4); RSIVAL(smb_krb5->request.data, 0, send_blob.length); memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length); packet_send(smb_krb5->packet, smb_krb5->request); break; case KRB5_KRBHST_HTTP: talloc_free(tmp_ctx); return EINVAL; } while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) { if (tevent_loop_once(ev) != 0) { talloc_free(tmp_ctx); return EINVAL; } /* After each and every event loop, reset the * send_to_kdc pointers to what they were when * we entered this loop. That way, if a * nested event has invalidated them, we put * it back before we return to the heimdal * code */ ret = krb5_set_send_to_kdc_func(context, smb_krb5_send_and_recv_func, data); if (ret != 0) { talloc_free(tmp_ctx); return ret; } } if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) { talloc_free(smb_krb5); continue; } if (!NT_STATUS_IS_OK(smb_krb5->status)) { struct tsocket_address *addr = socket_address_to_tsocket_address(smb_krb5, remote_addr); const char *addr_string = NULL; if (addr) { addr_string = tsocket_address_inet_addr_string(addr, smb_krb5); } else { addr_string = NULL; } DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5->status), addr_string)); talloc_free(smb_krb5); continue; } ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length); if (ret) { talloc_free(tmp_ctx); return ret; } talloc_free(smb_krb5); break; } talloc_free(tmp_ctx); if (a) { return 0; } return KRB5_KDC_UNREACH; }