From 2bd081638d74726f88f8b243bfe324adff652432 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 8 May 2024 15:04:45 +0200 Subject: Merging upstream version 2:4.20.1+dfsg. Signed-off-by: Daniel Baumann --- VERSION | 2 +- WHATSNEW.txt | 55 +++ auth/kerberos/kerberos_pac.c | 47 ++- docs-xml/manpages/idmap_ad.8.xml | 10 + lib/krb5_wrap/krb5_samba.h | 28 ++ libcli/http/http.c | 309 +++++++++++++++- libcli/http/http_internal.h | 4 + nsswitch/tests/test_idmap_ad.sh | 22 ++ packaging/systemd/samba-bgqd.service.in | 16 + packaging/wscript_build | 3 +- python/samba/tests/blackbox/http_chunk.py | 129 +++++++ python/samba/tests/blackbox/http_content.py | 95 +++++ .../blackbox/smbcacls_propagate_inhertance.py | 108 ++++++ selftest/target/Samba3.pm | 1 + selftest/tests.py | 2 + source3/utils/smbcacls.c | 4 + source3/winbindd/winbindd_ads.c | 11 +- source4/client/http_test.c | 401 +++++++++++++++++++++ source4/dns_server/dnsserver_common.c | 9 +- source4/wscript_build | 5 + 20 files changed, 1212 insertions(+), 49 deletions(-) create mode 100644 packaging/systemd/samba-bgqd.service.in create mode 100644 python/samba/tests/blackbox/http_chunk.py create mode 100644 python/samba/tests/blackbox/http_content.py create mode 100644 source4/client/http_test.c diff --git a/VERSION b/VERSION index 482360b..cfa7539 100644 --- a/VERSION +++ b/VERSION @@ -27,7 +27,7 @@ SAMBA_COPYRIGHT_STRING="Copyright Andrew Tridgell and the Samba Team 1992-2024" ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=20 -SAMBA_VERSION_RELEASE=0 +SAMBA_VERSION_RELEASE=1 ######################################################## # If a official release has a serious bug # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 5c97836..8249e93 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,3 +1,58 @@ + ============================== + Release Notes for Samba 4.20.1 + May 08, 2024 + ============================== + + +This is the latest stable release of the Samba 4.20 release series. + + +Changes since 4.20.0 +-------------------- + +o Douglas Bagnall + * BUG 15630: dns update debug message is too noisy. + +o Alexander Bokovoy + * BUG 15635: Do not fail PAC validation for RFC8009 checksums types. + +o Pavel Filipenský + * BUG 15605: Improve performance of lookup_groupmem() in idmap_ad. + +o Anna Popova + * BUG 15636: Smbcacls incorrectly propagates inheritance with Inherit-Only + flag. + +o Noel Power + * BUG 15611: http library doesn't support 'chunked transfer encoding'. + +o Andreas Schneider + * BUG 15600: Provide a systemd service file for the background queue daemon. + + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical:matrix.org matrix room, or +#samba-technical IRC channel on irc.libera.chat. + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. All bug reports should +be filed under the Samba 4.1 and newer product in the project's Bugzilla +database (https://bugzilla.samba.org/). + + +====================================================================== +== Our Code, Our Bugs, Our Responsibility. +== The Samba Team +====================================================================== + + +Release notes for older releases follow: +---------------------------------------- ============================== Release Notes for Samba 4.20.0 March 27, 2024 diff --git a/auth/kerberos/kerberos_pac.c b/auth/kerberos/kerberos_pac.c index ae4557b..b6272ac 100644 --- a/auth/kerberos/kerberos_pac.c +++ b/auth/kerberos/kerberos_pac.c @@ -33,6 +33,7 @@ #include "librpc/gen_ndr/auth.h" #include "auth/common_auth.h" #include "auth/kerberos/pac_utils.h" +#include "lib/krb5_wrap/krb5_samba.h" krb5_error_code check_pac_checksum(DATA_BLOB pac_data, struct PAC_SIGNATURE_DATA *sig, @@ -44,26 +45,34 @@ krb5_error_code check_pac_checksum(DATA_BLOB pac_data, krb5_keyusage usage = 0; krb5_boolean checksum_valid = false; krb5_data input; - - switch (sig->type) { - case CKSUMTYPE_HMAC_MD5: - /* ignores the key type */ - break; - case CKSUMTYPE_HMAC_SHA1_96_AES_256: - if (KRB5_KEY_TYPE(keyblock) != ENCTYPE_AES256_CTS_HMAC_SHA1_96) { - return EINVAL; - } - /* ok */ - break; - case CKSUMTYPE_HMAC_SHA1_96_AES_128: - if (KRB5_KEY_TYPE(keyblock) != ENCTYPE_AES128_CTS_HMAC_SHA1_96) { - return EINVAL; + size_t idx = 0; + struct { + krb5_cksumtype cksum_type; + krb5_enctype enc_type; + } supported_types[] = { + {CKSUMTYPE_HMAC_SHA1_96_AES_256, ENCTYPE_AES256_CTS_HMAC_SHA1_96}, + {CKSUMTYPE_HMAC_SHA1_96_AES_128, ENCTYPE_AES128_CTS_HMAC_SHA1_96}, + /* RFC8009 types. Not supported by AD yet but used by FreeIPA and MIT Kerberos */ + {CKSUMTYPE_HMAC_SHA256_128_AES128, ENCTYPE_AES128_CTS_HMAC_SHA256_128}, + {CKSUMTYPE_HMAC_SHA384_192_AES256, ENCTYPE_AES256_CTS_HMAC_SHA384_192}, + {0, 0}, + }; + + for(idx = 0; supported_types[idx].cksum_type != 0; idx++) { + if (sig->type == supported_types[idx].cksum_type) { + if (KRB5_KEY_TYPE(keyblock) != supported_types[idx].enc_type) { + return EINVAL; + } + /* ok */ + break; } - /* ok */ - break; - default: - DEBUG(2,("check_pac_checksum: Checksum Type %"PRIu32" is not supported\n", - sig->type)); + } + + /* do not do key type check for HMAC-MD5 */ + if ((sig->type != CKSUMTYPE_HMAC_MD5) && + (supported_types[idx].cksum_type == 0)) { + DEBUG(2,("check_pac_checksum: Checksum Type %d is not supported\n", + (int)sig->type)); return EINVAL; } diff --git a/docs-xml/manpages/idmap_ad.8.xml b/docs-xml/manpages/idmap_ad.8.xml index 32df8d0..c7fcc65 100644 --- a/docs-xml/manpages/idmap_ad.8.xml +++ b/docs-xml/manpages/idmap_ad.8.xml @@ -105,6 +105,16 @@ + all_groupmem = yes/no + + If set to yes winbind will retrieve all + group members for getgrnam(3), getgrgid(3) and getgrent(3) calls, + including those with missing uidNumber. + + Default: no + + + deny ous This parameter is a list of OUs from which objects will not be mapped via the ad idmap diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index e158a40..7951064 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -88,6 +88,34 @@ #define CKSUMTYPE_HMAC_SHA1_96_AES_256 CKSUMTYPE_HMAC_SHA1_96_AES256 #endif +/* + * RFC8009 encryption types' defines have different names: + * + * KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128 in Heimdal + * ENCTYPE_AES128_CTS_HMAC_SHA256_128 in MIT + * + * and + * + * KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192 in Heimdal + * ENCTYPE_AES256_CTS_HMAC_SHA384_192 in MIT + */ +#if !defined(ENCTYPE_AES128_CTS_HMAC_SHA256_128) +#define ENCTYPE_AES128_CTS_HMAC_SHA256_128 KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128 +#endif +#if !defined(ENCTYPE_AES256_CTS_HMAC_SHA384_192) +#define ENCTYPE_AES256_CTS_HMAC_SHA384_192 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192 +#endif + +/* + * Same for older encryption types, rename to have the same defines + */ +#if !defined(ENCTYPE_AES128_CTS_HMAC_SHA1_96) +#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 +#endif +#if !defined(ENCTYPE_AES256_CTS_HMAC_SHA1_96) +#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 +#endif + /* * KRB5_KU_OTHER_ENCRYPTED in Heimdal * KRB5_KEYUSAGE_APP_DATA_ENCRYPT in MIT diff --git a/libcli/http/http.c b/libcli/http/http.c index 96c573a..6f22214 100644 --- a/libcli/http/http.c +++ b/libcli/http/http.c @@ -28,16 +28,28 @@ #undef strcasecmp +enum http_body_type { + BODY_NONE = 0, + BODY_CONTENT_LENGTH, + BODY_CHUNKED, + BODY_ERROR = -1 +}; + /** * Determines if a response should have a body. - * @return 1 if the response MUST have a body; 0 if the response MUST NOT have - * a body. Returns -1 on error. + * @return 2 if response MUST use chunked encoding, + * 1 if the response MUST have a body; + * 0 if the response MUST NOT have a body. + * Returns -1 on error. */ -static int http_response_needs_body(struct http_request *req) +static enum http_body_type http_response_needs_body( + struct http_request *req) { struct http_header *h = NULL; - if (!req) return -1; + if (!req) { + return BODY_ERROR; + } for (h = req->headers; h != NULL; h = h->next) { int cmp; @@ -45,6 +57,18 @@ static int http_response_needs_body(struct http_request *req) char c; unsigned long long v; + cmp = strcasecmp(h->key, "Transfer-Encoding"); + if (cmp == 0) { + cmp = strcasecmp(h->value, "chunked"); + if (cmp == 0) { + return BODY_CHUNKED; + } + /* unsupported Transfer-Encoding type */ + DBG_ERR("Unsupported transfer encoding type %s\n", + h->value); + return BODY_ERROR; + } + cmp = strcasecmp(h->key, "Content-Length"); if (cmp != 0) { continue; @@ -52,20 +76,25 @@ static int http_response_needs_body(struct http_request *req) n = sscanf(h->value, "%llu%c", &v, &c); if (n != 1) { - return -1; + return BODY_ERROR; } req->remaining_content_length = v; if (v != 0) { - return 1; + return BODY_CONTENT_LENGTH; } - return 0; + return BODY_NONE; } - return 0; + return BODY_NONE; } +struct http_chunk +{ + struct http_chunk *prev, *next; + DATA_BLOB blob; +}; struct http_read_response_state { enum http_parser_state parser_state; @@ -73,6 +102,7 @@ struct http_read_response_state { uint64_t max_content_length; DATA_BLOB buffer; struct http_request *response; + struct http_chunk *chunks; }; /** @@ -86,7 +116,7 @@ static enum http_read_status http_parse_headers(struct http_read_response_state char *key = NULL; char *value = NULL; int n = 0; - int ret; + enum http_body_type ret; /* Sanity checks */ if (!state || !state->response) { @@ -119,19 +149,24 @@ static enum http_read_status http_parse_headers(struct http_read_response_state ret = http_response_needs_body(state->response); switch (ret) { - case 1: + case BODY_CHUNKED: + DEBUG(11, ("%s: need to process chunks... %d\n", __func__, + state->response->response_code)); + state->parser_state = HTTP_READING_CHUNK_SIZE; + break; + case BODY_CONTENT_LENGTH: if (state->response->remaining_content_length <= state->max_content_length) { DEBUG(11, ("%s: Start of read body\n", __func__)); state->parser_state = HTTP_READING_BODY; break; } FALL_THROUGH; - case 0: + case BODY_NONE: DEBUG(11, ("%s: Skipping body for code %d\n", __func__, state->response->response_code)); state->parser_state = HTTP_READING_DONE; break; - case -1: + case BODY_ERROR: DEBUG(0, ("%s_: Error in http_response_needs_body\n", __func__)); TALLOC_FREE(line); return HTTP_DATA_CORRUPTED; @@ -162,6 +197,141 @@ error: return status; } +static bool http_response_process_chunks(struct http_read_response_state *state) +{ + struct http_chunk *chunk = NULL; + struct http_request *resp = state->response; + + for (chunk = state->chunks; chunk; chunk = chunk->next) { + DBG_DEBUG("processing chunk of size %zi\n", + chunk->blob.length); + if (resp->body.data == NULL) { + resp->body = chunk->blob; + chunk->blob = data_blob_null; + talloc_steal(resp, resp->body.data); + continue; + } + + resp->body.data = + talloc_realloc(resp, + resp->body.data, + uint8_t, + resp->body.length + chunk->blob.length); + if (!resp->body.data) { + return false; + } + memcpy(resp->body.data + resp->body.length, + chunk->blob.data, + chunk->blob.length); + + resp->body.length += chunk->blob.length; + + TALLOC_FREE(chunk->blob.data); + chunk->blob = data_blob_null; + } + return true; +} + +static enum http_read_status http_read_chunk_term(struct http_read_response_state *state) +{ + enum http_read_status status = HTTP_ALL_DATA_READ; + char *ptr = NULL; + char *line = NULL; + + /* Sanity checks */ + if (!state || !state->response) { + DBG_ERR("%s: Invalid Parameter\n", __func__); + return HTTP_DATA_CORRUPTED; + } + + line = talloc_strndup(state, (char *)state->buffer.data, state->buffer.length); + if (!line) { + DBG_ERR("%s: Memory error\n", __func__); + return HTTP_DATA_CORRUPTED; + } + ptr = strstr(line, "\r\n"); + if (ptr == NULL) { + TALLOC_FREE(line); + return HTTP_MORE_DATA_EXPECTED; + } + + if (strncmp(line, "\r\n", 2) == 0) { + /* chunk terminator */ + if (state->parser_state == HTTP_READING_FINAL_CHUNK_TERM) { + if (http_response_process_chunks(state) == false) { + status = HTTP_DATA_CORRUPTED; + goto out; + } + state->parser_state = HTTP_READING_DONE; + } else { + state->parser_state = HTTP_READING_CHUNK_SIZE; + } + status = HTTP_ALL_DATA_READ; + goto out; + } + + status = HTTP_DATA_CORRUPTED; +out: + TALLOC_FREE(line); + return status; +} + +static enum http_read_status http_read_chunk_size(struct http_read_response_state *state) +{ + enum http_read_status status = HTTP_ALL_DATA_READ; + char *ptr = NULL; + char *line = NULL; + char *value = NULL; + int n = 0; + unsigned long long v; + + /* Sanity checks */ + if (!state || !state->response) { + DBG_ERR("%s: Invalid Parameter\n", __func__); + return HTTP_DATA_CORRUPTED; + } + + line = talloc_strndup(state, (char *)state->buffer.data, state->buffer.length); + if (!line) { + DBG_ERR("%s: Memory error\n", __func__); + return HTTP_DATA_CORRUPTED; + } + ptr = strstr(line, "\r\n"); + if (ptr == NULL) { + TALLOC_FREE(line); + return HTTP_MORE_DATA_EXPECTED; + } + + n = sscanf(line, "%m[^\r\n]\r\n", &value); + if (n != 1) { + DBG_ERR("%s: Error parsing chunk size '%s'\n", __func__, line); + status = HTTP_DATA_CORRUPTED; + goto out; + } + + DBG_DEBUG("Got chunk size string %s\n", value); + n = sscanf(value, "%llx", &v); + if (n != 1) { + DBG_ERR("%s: Error parsing chunk size '%s'\n", __func__, line); + status = HTTP_DATA_CORRUPTED; + goto out; + } + DBG_DEBUG("Got chunk size %llu 0x%llx\n", v, v); + if (v == 0) { + state->parser_state = HTTP_READING_FINAL_CHUNK_TERM; + } else { + state->parser_state = HTTP_READING_CHUNK; + } + state->response->remaining_content_length = v; + status = HTTP_ALL_DATA_READ; +out: + if (value) { + free(value); + } + TALLOC_FREE(line); + return status; +} + /** * Parses the first line of a HTTP response */ @@ -301,6 +471,55 @@ static enum http_read_status http_read_body(struct http_read_response_state *sta return HTTP_ALL_DATA_READ; } +static enum http_read_status http_read_chunk(struct http_read_response_state *state) +{ + struct http_request *resp = state->response; + struct http_chunk *chunk = NULL; + size_t total = 0; + size_t prev = 0; + + if (state->buffer.length < resp->remaining_content_length) { + return HTTP_MORE_DATA_EXPECTED; + } + + for (chunk = state->chunks; chunk; chunk = chunk->next) { + total += chunk->blob.length; + } + + prev = total; + total = total + state->buffer.length; + if (total < prev) { + DBG_ERR("adding chunklen %zu to buf len %zu " + "will overflow\n", + state->buffer.length, + prev); + return HTTP_DATA_CORRUPTED; + } + if (total > state->max_content_length) { + DBG_DEBUG("size %zu exceeds " + "max content len %"PRIu64" skipping body\n", + total, + state->max_content_length); + state->parser_state = HTTP_READING_DONE; + goto out; + } + + /* chunk read */ + chunk = talloc_zero(state, struct http_chunk); + if (chunk == NULL) { + DBG_ERR("%s: Memory error\n", __func__); + return HTTP_DATA_CORRUPTED; + } + chunk->blob = state->buffer; + talloc_steal(chunk, chunk->blob.data); + DLIST_ADD_END(state->chunks, chunk); + state->parser_state = HTTP_READING_CHUNK_TERM; +out: + state->buffer = data_blob_null; + resp->remaining_content_length = 0; + return HTTP_ALL_DATA_READ; +} + static enum http_read_status http_read_trailer(struct http_read_response_state *state) { enum http_read_status status = HTTP_DATA_CORRUPTED; @@ -323,6 +542,16 @@ static enum http_read_status http_parse_buffer(struct http_read_response_state * case HTTP_READING_BODY: return http_read_body(state); break; + case HTTP_READING_FINAL_CHUNK_TERM: + case HTTP_READING_CHUNK_TERM: + return http_read_chunk_term(state); + break; + case HTTP_READING_CHUNK_SIZE: + return http_read_chunk_size(state); + break; + case HTTP_READING_CHUNK: + return http_read_chunk(state); + break; case HTTP_READING_TRAILER: return http_read_trailer(state); break; @@ -527,20 +756,60 @@ static int http_read_response_next_vector(struct tstream_context *stream, *_count = 1; } break; - case HTTP_MORE_DATA_EXPECTED: - /* TODO Optimize, allocating byte by byte */ - state->buffer.data = talloc_realloc(state, state->buffer.data, - uint8_t, state->buffer.length + 1); + case HTTP_MORE_DATA_EXPECTED: { + size_t toread = 1; + size_t total; + if (state->parser_state == HTTP_READING_BODY || + state->parser_state == HTTP_READING_CHUNK) { + struct http_request *resp = state->response; + toread = resp->remaining_content_length - + state->buffer.length; + } + + total = toread + state->buffer.length; + + if (total < state->buffer.length) { + DBG_ERR("adding %zu to buf len %zu " + "will overflow\n", + toread, + state->buffer.length); + return -1; + } + + /* + * test if content-length message exceeds the + * specified max_content_length + * Note: This check won't be hit at the moment + * due to an existing check in parse_headers + * which will skip the body. Check is here + * for completeness and to cater for future + * code changes. + */ + if (state->parser_state == HTTP_READING_BODY) { + if (total > state->max_content_length) { + DBG_ERR("content size %zu exceeds " + "max content len %"PRIu64"\n", + total, + state->max_content_length); + return -1; + } + } + + state->buffer.data = + talloc_realloc(state, state->buffer.data, + uint8_t, + state->buffer.length + toread); if (!state->buffer.data) { return -1; } - state->buffer.length++; + state->buffer.length += toread; vector[0].iov_base = (void *)(state->buffer.data + - state->buffer.length - 1); - vector[0].iov_len = 1; + state->buffer.length - toread); + vector[0].iov_len = toread; *_vector = vector; *_count = 1; break; + } case HTTP_DATA_CORRUPTED: case HTTP_REQUEST_CANCELED: case HTTP_DATA_TOO_LONG: @@ -603,7 +872,7 @@ static void http_read_response_done(struct tevent_req *subreq) { NTSTATUS status; struct tevent_req *req; - int ret; + enum http_body_type ret; int sys_errno; if (!subreq) { diff --git a/libcli/http/http_internal.h b/libcli/http/http_internal.h index ec17f7e..786ace6 100644 --- a/libcli/http/http_internal.h +++ b/libcli/http/http_internal.h @@ -28,6 +28,10 @@ enum http_parser_state { HTTP_READING_BODY, HTTP_READING_TRAILER, HTTP_READING_DONE, + HTTP_READING_CHUNK_SIZE, + HTTP_READING_CHUNK, + HTTP_READING_CHUNK_TERM, + HTTP_READING_FINAL_CHUNK_TERM, }; enum http_read_status { diff --git a/nsswitch/tests/test_idmap_ad.sh b/nsswitch/tests/test_idmap_ad.sh index 7ae112a..1d4bd39 100755 --- a/nsswitch/tests/test_idmap_ad.sh +++ b/nsswitch/tests/test_idmap_ad.sh @@ -94,6 +94,14 @@ gidNumber: 2000001 unixHomeDirectory: /home/forbidden loginShell: /bin/tcsh gecos: User in forbidden OU + +dn: CN=no_posix_id,CN=Users,$BASE_DN +changetype: add +objectClass: user +samaccountName: no_posix_id +unixHomeDirectory: /home/no_posix_id +loginShell: /bin/sh +gecos: User without uidNumber and gidNumber EOF # @@ -171,6 +179,17 @@ then failed=$(($failed + 1)) fi +# +# Test 6: Make sure that with the default "all_groups=no" +# the group "domain users" will not show user "no_posix_id" +# but will show "SAMBA2008R2/administrator" +# + +dom_users="$DOMAIN/domain users" # Extra step to make sure that all is one word +out="$($wbinfo --group-info "$dom_users")" +testit_grep_count "no_posix_id1" "no_posix_id" 0 echo "$out" || failed=$(expr $failed + 1) +testit_grep "no_posix_id2" "SAMBA2008R2/administrator" echo "$out" || failed=$(expr $failed + 1) + # # Trusted domain test 1: Test uid of Administrator, should be 2500000 # @@ -241,6 +260,9 @@ gidNumber: 2000002 dn: cn=forbidden,ou=sub,$BASE_DN changetype: delete +dn: CN=no_posix_id,CN=Users,$BASE_DN +changetype: delete + dn: ou=sub,$BASE_DN changetype: delete EOF diff --git a/packaging/systemd/samba-bgqd.service.in b/packaging/systemd/samba-bgqd.service.in new file mode 100644 index 0000000..0254ebd --- /dev/null +++ b/packaging/systemd/samba-bgqd.service.in @@ -0,0 +1,16 @@ +[Unit] +Description=Samba Background Queue Daemon for printing-related jobs +Documentation=man:samba-bgqd(8) man:smb.conf(5) +Wants=network-online.target +After=network.target network-online.target + +[Service] +Type=notify +LimitNOFILE=16384 +PIDFile=@PIDDIR@/samba-bgqd.pid +EnvironmentFile=-@SYSCONFDIR@/sysconfig/samba +ExecStart=@LIBEXECDIR@/samba/samba-bgqd --foreground --no-process-group $SAMBAOPTIONS +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target diff --git a/packaging/wscript_build b/packaging/wscript_build index 217bd99..dc95beb 100644 --- a/packaging/wscript_build +++ b/packaging/wscript_build @@ -4,7 +4,8 @@ systemd_services = [ 'systemd/smb.service', 'systemd/nmb.service', 'systemd/winbind.service', - 'systemd/samba.service' + 'systemd/samba.service', + 'systemd/samba-bgqd.service', ] for srv in systemd_services: diff --git a/python/samba/tests/blackbox/http_chunk.py b/python/samba/tests/blackbox/http_chunk.py new file mode 100644 index 0000000..6745c8c --- /dev/null +++ b/python/samba/tests/blackbox/http_chunk.py @@ -0,0 +1,129 @@ +# Blackbox tests for http_test +# +# Copyright (C) Noel Power noel.power@suse.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import time +import threading +import logging +import json +from http.server import HTTPServer, BaseHTTPRequestHandler +from samba.logger import get_samba_logger +from samba.tests import BlackboxTestCase, BlackboxProcessError + +logger = get_samba_logger(name=__name__) +COMMAND = "bin/http_test" +def make_chunks(msg, chunk_size): + chunks = [] + while len(msg) > chunk_size: + chunk = msg[:chunk_size] + chunks.append(chunk) + msg = msg[chunk_size:] + if len(msg): + chunks.append(msg) + return chunks + +# simple handler, spits back the 'path' passed in +# GET or POST and a chunked encoded http response +# where the chunk size is 10 octets +class ChunkHTTPRequestHandler(BaseHTTPRequestHandler): + def handle_req(self): + msg = bytes(self.path, encoding="utf-8") + chunks = make_chunks(msg, 10) + + self.send_response(200) + self.send_header('content-type', 'application/json; charset=UTF-8') + if self.path == "usegziptransferencoding": + self.send_header('Transfer-Encoding', 'gzip') + else: + self.send_header('Transfer-Encoding', 'chunked') + self.end_headers() + resp = bytes() + for chunk in chunks: + resp = resp + ("%x" % len(chunk)).encode("utf-8") + b'\r\n' + chunk + b'\r\n' + resp += b'0\r\n\r\n' + self.wfile.write(resp) + + def do_POST(self): + self.handle_req() + def do_GET(self): + self.handle_req() + +class HttpChunkBlackboxTests(BlackboxTestCase): + def setUp(self): + self.server = HTTPServer((os.getenv("SERVER_IP", "localhost"), 8080), + ChunkHTTPRequestHandler, + bind_and_activate=False) + self.t = threading.Thread(target=HttpChunkBlackboxTests.http_server, args=(self,)) + self.t.setDaemon(True) + self.t.start() + time.sleep(1) + + def tearDown(self): + super().tearDown() + + def http_server(self): + self.server.server_bind() + self.server.server_activate() + self.server.serve_forever() + + def test_single_chunk(self): + try: + msg = "one_chunk" + resp = self.check_output("%s -U%% -I%s --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.assertEqual(msg,resp.decode('utf-8')) + except BlackboxProcessError as e: + print("Failed with: %s" % e) + self.fail(str(e)) + + def test_multi_chunks(self): + try: + msg = "snglechunksnglechunksnglechunksnglechunksnglechunk" + resp = self.check_output("%s -U%% -I%s --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.assertEqual(msg, resp.decode('utf-8')) + except BlackboxProcessError as e: + print("Failed with: %s" % e) + self.fail(str(e)) + + def test_exceed_request_size(self): + try: + msg = "snglechunksnglechunksnglechunksnglechunksnglechunk" + resp = self.check_output("%s -d11 -U%% -I%s --rsize 49 --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.fail("unexpected success") + except BlackboxProcessError as e: + if "http_read_chunk: size 50 exceeds max content len 49 skipping body" not in e.stderr.decode('utf-8'): + self.fail(str(e)) + if "unexpected 0 len response" not in e.stdout.decode('utf-8'): + self.fail(str(e)) + + def test_exact_request_size(self): + try: + msg = "snglechunksnglechunksnglechunksnglechunksnglechunk" + resp = self.check_output("%s -U%% -I%s --rsize 50 --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.assertEqual(msg, resp.decode('utf-8')) + except BlackboxProcessError as e: + print("Failed with: %s" % e) + self.fail(str(e)) + + def test_gzip_transfer_encoding(self): + try: + msg = "usegziptransferencoding" + resp = self.check_output("%s -U%% -I%s --rsize 50 --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.assertEqual(msg, resp.decode('utf-8')) + self.fail("unexpected success") + except BlackboxProcessError as e: + if "http_response_needs_body: Unsupported transfer encoding type gzip" not in e.stderr.decode('utf-8'): + self.fail(str(e)) diff --git a/python/samba/tests/blackbox/http_content.py b/python/samba/tests/blackbox/http_content.py new file mode 100644 index 0000000..3d674aa --- /dev/null +++ b/python/samba/tests/blackbox/http_content.py @@ -0,0 +1,95 @@ +# Blackbox tests for http_test +# +# Copyright (C) Noel Power noel.power@suse.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import time +import threading +import logging +import json +from http.server import HTTPServer, BaseHTTPRequestHandler +from samba.logger import get_samba_logger +from samba.tests import BlackboxTestCase, BlackboxProcessError + +logger = get_samba_logger(name=__name__) +COMMAND = "bin/http_test" + +# simple handler, spits back the 'path' passed in +# GET or POST and a chunked encoded http response +# where the chunk size is 10 octets +class ContentHTTPRequestHandler(BaseHTTPRequestHandler): + def handle_req(self): + msg = bytes(self.path, encoding="utf-8") + + self.send_response(200) + self.send_header('content-type', 'application/json; charset=UTF-8') + self.send_header('content-length', len(msg)) + self.end_headers() + self.wfile.write(msg) + + def do_POST(self): + self.handle_req() + def do_GET(self): + self.handle_req() + +class HttpContentBlackboxTests(BlackboxTestCase): + def setUp(self): + self.server = HTTPServer((os.getenv("SERVER_IP", "localhost"), 8080), + ContentHTTPRequestHandler, + bind_and_activate=False) + self.t = threading.Thread(target=HttpContentBlackboxTests.http_server, args=(self,)) + self.t.setDaemon(True) + self.t.start() + time.sleep(1) + + def tearDown(self): + super().tearDown() + + def http_server(self): + self.server.server_bind() + self.server.server_activate() + self.server.serve_forever() + + def notest_simple_msg(self): + try: + msg = "simplemessage" + resp = self.check_output("%s -U%% -I%s --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.assertEqual(msg, resp.decode('utf-8')) + except BlackboxProcessError as e: + print("Failed with: %s" % e) + self.fail(str(e)) + + def test_exceed_request_size(self): + try: + msg = "012345678" # 9 bytes + # limit response to 8 bytes + resp = self.check_output("%s -d11 -U%% -I%s --rsize 8 --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.fail("unexpected success") + except BlackboxProcessError as e: + if "unexpected 0 len response" not in e.stdout.decode('utf-8'): + self.fail(str(e)) + if "http_parse_headers: Skipping body for code 200" not in e.stderr.decode('utf-8'): + self.fail(str(e)) + + def test_exact_request_size(self): + try: + msg = "012345678" # 9 bytes + # limit response to 9 bytes + resp = self.check_output("%s -U%% -I%s --rsize 9 --uri %s" % (COMMAND, os.getenv("SERVER_IP", "localhost"), msg)) + self.assertEqual(msg, resp.decode('utf-8')) + except BlackboxProcessError as e: + print("Failed with: %s" % e) + self.fail(str(e)) diff --git a/python/samba/tests/blackbox/smbcacls_propagate_inhertance.py b/python/samba/tests/blackbox/smbcacls_propagate_inhertance.py index cc13727..5b3a271 100644 --- a/python/samba/tests/blackbox/smbcacls_propagate_inhertance.py +++ b/python/samba/tests/blackbox/smbcacls_propagate_inhertance.py @@ -1288,3 +1288,111 @@ class InheritanceSmbCaclsTests(SmbCaclsBlockboxTestBase): except BlackboxProcessError as e: self.fail(str(e)) + + def test_simple_iocioi_add(self): + """test smbcacls '--propagate-inheritance --add' which attempts to add the ACL + for the file and additionally use inheritance rules to propagate appropriate + changes to children + + This test adds an ACL with (IO)(CI)(OI)(READ) + + before: + + +-tar_test_dir/ (OI)(CI)(I)(F) + +-oi_dir/ (OI)(CI)(I)(F) + | +-file.1 (I)(F) + | +-nested/ (OI)(CI)(I)(F) + | +-file.2 (I)(F) + | +-nested_again/ (OI)(CI)(I)(F) + | +-file.3 (I)(F) + + after/expected: + + +-tar_test_dir/ (OI)(CI)(I)(F) + +-oi_dir/ (OI)(CI)(I)(F), (IO)(CI)(OI)(READ) + | +-file.1 (I)(F), (I)(READ) + | +-nested/ (OI)(CI)(I)(F), (I)(CI)(OI)(READ) + | +-file.2 (I)(F), (I)(READ) + | +-nested_again/ (OI)(CI)(I)(F), (I)(CI)(OI)(READ) + | +-file.3 (I)(F), (I)(READ)""" + + dir_add_acl_str = "ACL:%s:ALLOWED/OI|CI|IO/READ" % self.user + obj_inherited_ace_str = "ACL:%s:ALLOWED/I/READ" % self.user + dir_inherited_ace_str = "ACL:%s:ALLOWED/OI|CI|I/READ" % self.user + + try: + + self.smb_cacls(["--propagate-inheritance", "--add", + dir_add_acl_str, self.oi_dir]) + + # check top level container 'oi_dir' has IO|CI|OI/READ + dir_ace = self.ace_parse_str(dir_add_acl_str) + self.assertTrue(self.file_ace_check(self.oi_dir, dir_ace)) + + # file 'oi_dir/file-1' should have inherited I/READ + child_file_ace = self.ace_parse_str(obj_inherited_ace_str) + self.assertTrue(self.file_ace_check(self.f1, child_file_ace)) + + # nested dir 'oi_dir/nested/' should have I|CI|OI/READ + child_dir_ace = self.ace_parse_str(dir_inherited_ace_str) + self.assertTrue(self.file_ace_check(self.nested_dir, child_dir_ace)) + + # nested file 'oi_dir/nested/file-2' should have inherited I/READ + self.assertTrue(self.file_ace_check(self.f2, child_file_ace)) + + # nested_again dir 'oi_dir/nested/nested_again' should have I|CI|OI/READ + child_dir_ace = self.ace_parse_str(dir_inherited_ace_str) + self.assertTrue(self.file_ace_check(self.nested_again_dir, child_dir_ace)) + # nested_again file 'oi_dir/nested/nested_again/file-3' should have inherited I/READ + self.assertTrue(self.file_ace_check(self.f3, child_file_ace)) + except BlackboxProcessError as e: + self.fail(str(e)) + + def test_simple_ioci_add(self): + """test smbcacls '--propagate-inheritance --add' which attempts to add the ACL + for the file and additionally use inheritance rules to propagate appropriate + changes to children + + This test adds an ACL with (IO)(CI)(READ) + + before: + + +-tar_test_dir/ (OI)(CI)(I)(F) + +-oi_dir/ (OI)(CI)(I)(F) + | +-file.1 (I)(F) + | +-nested/ (OI)(CI)(I)(F) + | +-file.2 (I)(F) + | +-nested_again/ (OI)(CI)(I)(F) + | +-file.3 (I)(F) + + after/expected: + + +-tar_test_dir/ (OI)(CI)(I)(F) + +-oi_dir/ (OI)(CI)(I)(F), (IO)(CI)(READ) + | +-file.1 (I)(F) + | +-nested/ (OI)(CI)(I)(F), (I)(CI)(READ) + | +-file.2 (I)(F) + | +-nested_again/ (OI)(CI)(I)(F), (I)(CI)(READ) + | +-file.3 (I)(F)""" + + dir_add_acl_str = "ACL:%s:ALLOWED/CI|IO/READ" % self.user + dir_inherited_ace_str = "ACL:%s:ALLOWED/CI|I/READ" % self.user + + try: + + self.smb_cacls(["--propagate-inheritance", "--add", + dir_add_acl_str, self.oi_dir]) + + # check top level container 'oi_dir' has IO|CI/READ + dir_ace = self.ace_parse_str(dir_add_acl_str) + self.assertTrue(self.file_ace_check(self.oi_dir, dir_ace)) + + # nested dir 'oi_dir/nested/' should have I|CI/READ + child_dir_ace = self.ace_parse_str(dir_inherited_ace_str) + self.assertTrue(self.file_ace_check(self.nested_dir, child_dir_ace)) + + # nested_again dir 'oi_dir/nested/nested_again' should have I|CI/READ + child_dir_ace = self.ace_parse_str(dir_inherited_ace_str) + self.assertTrue(self.file_ace_check(self.nested_again_dir, child_dir_ace)) + except BlackboxProcessError as e: + self.fail(str(e)) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index bbce55e..cf40633 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -1420,6 +1420,7 @@ sub setup_ad_member_idmap_ad idmap config $dcvars->{TRUST_DOMAIN} : range = 2000000-2999999 gensec_gssapi:requested_life_time = 5 winbind scan trusted domains = yes + winbind expand groups = 1 "; my $ret = $self->provision( diff --git a/selftest/tests.py b/selftest/tests.py index 10d5536..0d5db68 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -221,6 +221,8 @@ plantestsuite( "samba4.blackbox.test_special_group", "none", cmdline('test_special_group.sh', '$PREFIX_ABS/provision')) +planpythontestsuite("fileserver", "samba.tests.blackbox.http_content") +planpythontestsuite("fileserver", "samba.tests.blackbox.http_chunk") planpythontestsuite("none", "samba.tests.upgradeprovision") planpythontestsuite("none", "samba.tests.xattr") planpythontestsuite("none", "samba.tests.ntacls") diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c index ff11ba4..e0591ac 100644 --- a/source3/utils/smbcacls.c +++ b/source3/utils/smbcacls.c @@ -914,6 +914,10 @@ static uint8_t get_flags_to_propagate(bool is_container, /* Assume we are not propagating the ACE */ newflags &= ~SEC_ACE_FLAG_INHERITED_ACE; + + /* Inherit-only flag is not propagated to children */ + + newflags &= ~SEC_ACE_FLAG_INHERIT_ONLY; /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */ if (acl_cntrinherit || acl_objinherit) { /* diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c index 7e572e5..7d63240 100644 --- a/source3/winbindd/winbindd_ads.c +++ b/source3/winbindd/winbindd_ads.c @@ -1039,7 +1039,7 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, } static NTSTATUS add_primary_group_members( - ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, uint32_t rid, + ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, uint32_t rid, const char *domname, char ***all_members, size_t *num_all_members) { char *filter; @@ -1051,10 +1051,13 @@ static NTSTATUS add_primary_group_members( char **members; size_t num_members; ads_control args; + bool all_groupmem = idmap_config_bool(domname, "all_groupmem", false); filter = talloc_asprintf( - mem_ctx, "(&(objectCategory=user)(primaryGroupID=%u))", - (unsigned)rid); + mem_ctx, + "(&(objectCategory=user)(primaryGroupID=%u)%s)", + (unsigned)rid, + all_groupmem ? "" : "(uidNumber=*)(!(uidNumber=0))"); if (filter == NULL) { goto done; } @@ -1206,7 +1209,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members)); - status = add_primary_group_members(ads, mem_ctx, rid, + status = add_primary_group_members(ads, mem_ctx, rid, domain->name, &members, &num_members); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("%s: add_primary_group_members failed: %s\n", diff --git a/source4/client/http_test.c b/source4/client/http_test.c new file mode 100644 index 0000000..ca542ba --- /dev/null +++ b/source4/client/http_test.c @@ -0,0 +1,401 @@ +#include "includes.h" +#include "version.h" +#include "libcli/libcli.h" +#include "lib/events/events.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/http/http.h" +#include "credentials.h" +#include "util/tevent_ntstatus.h" +#include "lib/tls/tls.h" +#include "lib/cmdline/cmdline.h" + + +struct http_client_info { + struct http_conn *http_conn; + uint16_t server_port; + const char *server_addr; + struct tstream_tls_params *tls_params; + struct cli_credentials *creds; + struct loadparm_context *lp_ctx; + const char *uri; +}; + +static bool send_http_request(TALLOC_CTX *mem_ctx, + struct tevent_context *ev_ctx, + struct http_client_info* es, + size_t response_size, + NTSTATUS *pstatus) +{ + struct http_request *http_req = NULL; + struct tevent_req *req = NULL; + char *uri = NULL; + struct http_request *http_response = NULL; + NTSTATUS status; + + http_req = talloc_zero(mem_ctx, struct http_request); + if (!http_req) { + DBG_ERR("no memory\n"); + return false; + } + + uri = talloc_strdup(mem_ctx, es->uri); + + http_req->type = HTTP_REQ_POST; + http_req->uri = uri; + http_req->body = data_blob_null; + http_req->major = '1'; + http_req->minor = '1'; + + http_add_header(mem_ctx, &http_req->headers, + "User-Agent", "Samba/http_test"); + http_add_header(mem_ctx, &http_req->headers, + "Accept", "*/*"); + + req = http_send_auth_request_send(mem_ctx, + ev_ctx, + es->http_conn, + http_req, + es->creds, + es->lp_ctx, + HTTP_AUTH_BASIC); + if (!tevent_req_set_endtime(req, ev_ctx, timeval_current_ofs(10, 0))) { + DBG_ERR("Failed to set timeout\n"); + return false; + } + + if (!tevent_req_poll_ntstatus(req, ev_ctx, pstatus)) { + DBG_ERR("Failed to connect: %s\n", nt_errstr(*pstatus)); + return false; + } + + status = http_send_auth_request_recv(req); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Auth request failed: %s\n", nt_errstr(status)); + return false; + } + + req = http_read_response_send(mem_ctx, + ev_ctx, + es->http_conn, + response_size); + if (!req) { + DBG_ERR("no memory\n"); + return -1; + } + + if (!tevent_req_set_endtime(req, ev_ctx, timeval_current_ofs(10, 0))) { + DBG_ERR("Failed to set timeout\n"); + return false; + } + + if (!tevent_req_poll_ntstatus(req, ev_ctx, pstatus)) { + DBG_ERR("Failed to read_resonse: %s\n", nt_errstr(*pstatus)); + return false; + } + + *pstatus = http_read_response_recv(req, mem_ctx, &http_response); + + if (!NT_STATUS_IS_OK(*pstatus)) { + DBG_ERR("Failed to receive response: %s\n", nt_errstr(*pstatus)); + return false; + } + /* following are not 'hard' errors */ + if (http_response->response_code != 200) { + fprintf(stdout, "HTTP server response: %u\n", + http_response->response_code); + fflush(stdout); + return false; + + } + if (http_response->body.length == 0) { + fprintf(stdout, "unexpected 0 len response\n"); + fflush(stdout); + return false; + } + DBG_ERR("response: len (%d)\n%s\n", + (int)http_response->body.length, + talloc_strndup(mem_ctx, + (char *)http_response->body.data, + http_response->body.length)); + fprintf(stdout,"%s", talloc_strndup(mem_ctx, + (char *)http_response->body.data, + http_response->body.length)); + fflush(stdout); + return true; +} + +int main(int argc, const char *argv[]) + +{ + TALLOC_CTX *mem_ctx; + struct tevent_context *ev_ctx; + int retries = 4; + int count = 0; + struct http_client_info *http_info = NULL; + bool use_tls = false; + int res; + NTSTATUS status; + struct tevent_req *req = NULL; + bool connected = false; + poptContext pc; + const char **const_argv = discard_const_p(const char *, argv); + int opt; + bool ok; + const char *ca_file = NULL; + int port = 0; + size_t response_size = 8192000; + struct cli_credentials *cli_creds; + + struct poptOption long_options[] = { + POPT_AUTOHELP + + { + .longName = "usetls", + .shortName = 't', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 't', + .descrip = "Use tls", + .argDescrip = "enable tls", + }, + { + .longName = "ip-address", + .shortName = 'I', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'I', + .descrip = "Use this IP to connect to", + .argDescrip = "IP", + }, + { + .longName = "port", + .shortName = 'p', + .argInfo = POPT_ARG_INT, + .arg = &port, + .val = 'p', + .descrip = "port to connect to", + .argDescrip = "port", + }, + { + .longName = "cacart", + .shortName = 'c', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'c', + .descrip = "CA certificate to verify peer against", + .argDescrip = "ca cert", + }, + { + .longName = "uri", + .shortName = 'u', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'u', + .descrip = "uri to send as part of http request", + .argDescrip = "uri", + }, + { + .longName = "rsize", + .argInfo = POPT_ARG_LONG, + .arg = &response_size, + .descrip = "response size", + }, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_TABLEEND + }; + + mem_ctx = talloc_init("http_test"); + + if (!mem_ctx) { + DBG_ERR("Not enough memory\n"); + res = -1; + goto done; + } + + http_info = talloc_zero(mem_ctx, struct http_client_info); + + if (http_info == NULL) { + DBG_ERR("Not enough memory\n"); + res = -1; + goto done; + } + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + res = -1; + goto done; + } + + pc = samba_popt_get_context(getprogname(), + argc, + const_argv, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + res = -1; + goto done; + } + + /* some defaults */ + + http_info->server_addr = "localhost"; + http_info->uri = "/_search?pretty"; + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 't': + use_tls = true; + break; + case 'c': { + ca_file = talloc_strdup(mem_ctx, + poptGetOptArg(pc)); + if (ca_file == NULL) { + DBG_ERR("Not enough memory\n"); + res = -1; + goto done; + } + break; + } + case 'I': { + http_info->server_addr = talloc_strdup(mem_ctx, + poptGetOptArg(pc)); + if (http_info->server_addr == NULL) { + DBG_ERR("Not enough memory\n"); + res = -1; + goto done; + } + break; + } + case 'u': { + http_info->uri = talloc_strdup(mem_ctx, + poptGetOptArg(pc)); + if (http_info->uri == NULL) { + DBG_ERR("Not enough memory\n"); + res = -1; + goto done; + } + break; + } + } + } + + if (use_tls && ca_file == NULL) { + DBG_ERR("No cacert\n"); + res = -1; + poptPrintUsage(pc, stderr, 0); + goto done; + } + + if (!port) { + port = 8080; + } + http_info->server_port = port; + + ev_ctx = s4_event_context_init(mem_ctx); + if (!ev_ctx) { + DBG_ERR("Not enough memory\n"); + res = -1; + goto done; + } + + + cli_creds = samba_cmdline_get_creds(); + if (!cli_credentials_is_anonymous(cli_creds)) { + http_info->creds = cli_credentials_init(mem_ctx); + cli_credentials_set_username( + http_info->creds, + cli_credentials_get_username(cli_creds), + CRED_SPECIFIED); + cli_credentials_set_password(http_info->creds, + cli_credentials_get_password(cli_creds), + CRED_SPECIFIED); + } else { + DBG_DEBUG("Anonymous creds!!!\n"); + http_info->creds = cli_creds; + } + if (http_info->creds == NULL) { + DBG_ERR("Failed to create creds\n"); + res = -1; + goto done; + } + http_info->lp_ctx = samba_cmdline_get_lp_ctx(); + + DBG_ERR("retries = %d/%d, Using server %s, port %d, using tls %s\n", + count, retries, + http_info->server_addr, + http_info->server_port, + use_tls ? "true" : "false"); + + while (count < retries) { + int error; + DBG_ERR("Connecting to HTTP [%s] port [%"PRIu16"]%s\n", + http_info->server_addr, http_info->server_port, + use_tls ? " with tls" : " without tls"); + if (use_tls) { + const char *crl_file = NULL; + const char *tls_priority = "NORMAL:-VERS-SSL3.0"; + enum tls_verify_peer_state verify_peer = + TLS_VERIFY_PEER_CA_ONLY; + + status = tstream_tls_params_client(mem_ctx, + ca_file, + crl_file, + tls_priority, + verify_peer, + http_info->server_addr, + &http_info->tls_params); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed tstream_tls_params_client - %s\n", + nt_errstr(status)); + res = -1; + goto done; + } + } + + req = http_connect_send(mem_ctx, + ev_ctx, + http_info->server_addr, + http_info->server_port, + http_info->creds, + http_info->tls_params); + if (!tevent_req_poll_ntstatus(req, ev_ctx, &status)) { + res = -1; + goto done; + } + + error = http_connect_recv(req, + mem_ctx, + &http_info->http_conn); + if (error != 0) { + count++; + DBG_ERR("HTTP connection failed retry %d/%d: %s\n", count, retries, strerror(error)); + } else { + DBG_ERR("HTTP connection succeeded\n"); + connected = true; + break; + } + } + + if (!connected) { + DBG_ERR("Leaving early\n"); + res = -1; + goto done; + } + + if (!send_http_request(mem_ctx, ev_ctx, http_info, response_size, &status)) { + DBG_ERR("Failure\n"); + res = -1; + goto done; + } + res = 0; +done: + TALLOC_FREE(mem_ctx); + return res; +} diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index fbe39d9..aba7f41 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -1034,10 +1034,11 @@ WERROR dns_common_replace(struct ldb_context *samdb, * record. */ if (records[i].data.EntombedTime != 0) { - if (rec_count != 1) { - DBG_ERR("tombstone record has %u neighbour " - "records.\n", - rec_count - 1); + if (rec_count != 1 && DEBUGLVL(DBGLVL_NOTICE)) { + DBG_NOTICE("tombstone record [%u] has " + "%u neighbour records.\n", + i, rec_count - 1); + NDR_PRINT_DEBUG(dnsp_DnssrvRpcRecord, &records[i]); } was_tombstoned = true; } diff --git a/source4/wscript_build b/source4/wscript_build index d204441..db2ad3e 100644 --- a/source4/wscript_build +++ b/source4/wscript_build @@ -6,6 +6,11 @@ bld.SAMBA_BINARY('client/smbclient' + bld.env.suffix4, install=False ) +bld.SAMBA_BINARY('client/http_test', + source='client/http_test.c', + deps='samba-hostconfig SMBREADLINE samba-util LIBCLI_SMB RPC_NDR_SRVSVC LIBCLI_LSA popt CMDLINE_S4', + for_selftest=True, + ) bld.SAMBA_BINARY('client/cifsdd', source='client/cifsdd.c client/cifsddio.c', -- cgit v1.2.3