diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/lib | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/lib')
260 files changed, 83303 insertions, 0 deletions
diff --git a/source3/lib/ABI/smbldap-0.sigs b/source3/lib/ABI/smbldap-0.sigs new file mode 100644 index 0000000..d0e1d2f --- /dev/null +++ b/source3/lib/ABI/smbldap-0.sigs @@ -0,0 +1,28 @@ +smbldap_add: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_delete: int (struct smbldap_state *, const char *) +smbldap_extended_operation: int (struct smbldap_state *, const char *, struct berval *, LDAPControl **, LDAPControl **, char **, struct berval **) +smbldap_free_struct: void (struct smbldap_state **) +smbldap_get_single_attribute: bool (LDAP *, LDAPMessage *, const char *, char *, int) +smbldap_has_control: bool (LDAP *, const char *) +smbldap_has_extension: bool (LDAP *, const char *) +smbldap_has_naming_context: bool (LDAP *, const char *) +smbldap_init: NTSTATUS (TALLOC_CTX *, struct tevent_context *, const char *, bool, const char *, const char *, struct smbldap_state **) +smbldap_make_mod: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const char *) +smbldap_make_mod_blob: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const DATA_BLOB *) +smbldap_modify: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_pull_sid: bool (LDAP *, LDAPMessage *, const char *, struct dom_sid *) +smbldap_search: int (struct smbldap_state *, const char *, int, const char *, const char **, int, LDAPMessage **) +smbldap_search_paged: int (struct smbldap_state *, const char *, int, const char *, const char **, int, int, LDAPMessage **, void **) +smbldap_search_suffix: int (struct smbldap_state *, const char *, const char **, LDAPMessage **) +smbldap_set_creds: bool (struct smbldap_state *, bool, const char *, const char *) +smbldap_set_mod: void (LDAPMod ***, int, const char *, const char *) +smbldap_set_mod_blob: void (LDAPMod ***, int, const char *, const DATA_BLOB *) +smbldap_setup_full_conn: int (LDAP **, const char *) +smbldap_start_tls: int (LDAP *, int) +smbldap_talloc_autofree_ldapmod: void (TALLOC_CTX *, LDAPMod **) +smbldap_talloc_autofree_ldapmsg: void (TALLOC_CTX *, LDAPMessage *) +smbldap_talloc_dn: char *(TALLOC_CTX *, LDAP *, LDAPMessage *) +smbldap_talloc_first_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_blob: bool (TALLOC_CTX *, LDAP *, LDAPMessage *, const char *, DATA_BLOB *) +smbldap_talloc_smallest_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) diff --git a/source3/lib/ABI/smbldap-1.sigs b/source3/lib/ABI/smbldap-1.sigs new file mode 100644 index 0000000..cacc56d --- /dev/null +++ b/source3/lib/ABI/smbldap-1.sigs @@ -0,0 +1,31 @@ +smbldap_add: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_delete: int (struct smbldap_state *, const char *) +smbldap_extended_operation: int (struct smbldap_state *, const char *, struct berval *, LDAPControl **, LDAPControl **, char **, struct berval **) +smbldap_free_struct: void (struct smbldap_state **) +smbldap_get_ldap: LDAP *(struct smbldap_state *) +smbldap_get_paged_results: bool (struct smbldap_state *) +smbldap_get_single_attribute: bool (LDAP *, LDAPMessage *, const char *, char *, int) +smbldap_has_control: bool (LDAP *, const char *) +smbldap_has_extension: bool (LDAP *, const char *) +smbldap_has_naming_context: bool (LDAP *, const char *) +smbldap_init: NTSTATUS (TALLOC_CTX *, struct tevent_context *, const char *, bool, const char *, const char *, struct smbldap_state **) +smbldap_make_mod: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const char *) +smbldap_make_mod_blob: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const DATA_BLOB *) +smbldap_modify: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_pull_sid: bool (LDAP *, LDAPMessage *, const char *, struct dom_sid *) +smbldap_search: int (struct smbldap_state *, const char *, int, const char *, const char **, int, LDAPMessage **) +smbldap_search_paged: int (struct smbldap_state *, const char *, int, const char *, const char **, int, int, LDAPMessage **, void **) +smbldap_search_suffix: int (struct smbldap_state *, const char *, const char **, LDAPMessage **) +smbldap_set_creds: bool (struct smbldap_state *, bool, const char *, const char *) +smbldap_set_mod: void (LDAPMod ***, int, const char *, const char *) +smbldap_set_mod_blob: void (LDAPMod ***, int, const char *, const DATA_BLOB *) +smbldap_set_paged_results: void (struct smbldap_state *, bool) +smbldap_setup_full_conn: int (LDAP **, const char *) +smbldap_start_tls: int (LDAP *, int) +smbldap_talloc_autofree_ldapmod: void (TALLOC_CTX *, LDAPMod **) +smbldap_talloc_autofree_ldapmsg: void (TALLOC_CTX *, LDAPMessage *) +smbldap_talloc_dn: char *(TALLOC_CTX *, LDAP *, LDAPMessage *) +smbldap_talloc_first_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_blob: bool (TALLOC_CTX *, LDAP *, LDAPMessage *, const char *, DATA_BLOB *) +smbldap_talloc_smallest_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) diff --git a/source3/lib/ABI/smbldap-2.1.0.sigs b/source3/lib/ABI/smbldap-2.1.0.sigs new file mode 100644 index 0000000..67dcc9a --- /dev/null +++ b/source3/lib/ABI/smbldap-2.1.0.sigs @@ -0,0 +1,33 @@ +smbldap_add: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_delete: int (struct smbldap_state *, const char *) +smbldap_extended_operation: int (struct smbldap_state *, const char *, struct berval *, LDAPControl **, LDAPControl **, char **, struct berval **) +smbldap_free_struct: void (struct smbldap_state **) +smbldap_get_ldap: LDAP *(struct smbldap_state *) +smbldap_get_paged_results: bool (struct smbldap_state *) +smbldap_get_single_attribute: bool (LDAP *, LDAPMessage *, const char *, char *, int) +smbldap_has_control: bool (LDAP *, const char *) +smbldap_has_extension: bool (LDAP *, const char *) +smbldap_has_naming_context: bool (LDAP *, const char *) +smbldap_init: NTSTATUS (TALLOC_CTX *, struct tevent_context *, const char *, bool, const char *, const char *, struct smbldap_state **) +smbldap_make_mod: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const char *) +smbldap_make_mod_blob: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const DATA_BLOB *) +smbldap_modify: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_pull_sid: bool (LDAP *, LDAPMessage *, const char *, struct dom_sid *) +smbldap_search: int (struct smbldap_state *, const char *, int, const char *, const char **, int, LDAPMessage **) +smbldap_search_paged: int (struct smbldap_state *, const char *, int, const char *, const char **, int, int, LDAPMessage **, void **) +smbldap_search_suffix: int (struct smbldap_state *, const char *, const char **, LDAPMessage **) +smbldap_set_bind_callback: void (struct smbldap_state *, smbldap_bind_callback_fn, void *) +smbldap_set_creds: bool (struct smbldap_state *, bool, const char *, const char *) +smbldap_set_mod: void (LDAPMod ***, int, const char *, const char *) +smbldap_set_mod_blob: void (LDAPMod ***, int, const char *, const DATA_BLOB *) +smbldap_set_paged_results: void (struct smbldap_state *, bool) +smbldap_setup_full_conn: int (LDAP **, const char *) +smbldap_start_tls: int (LDAP *, int) +smbldap_start_tls_start: int (LDAP *, int) +smbldap_talloc_autofree_ldapmod: void (TALLOC_CTX *, LDAPMod **) +smbldap_talloc_autofree_ldapmsg: void (TALLOC_CTX *, LDAPMessage *) +smbldap_talloc_dn: char *(TALLOC_CTX *, LDAP *, LDAPMessage *) +smbldap_talloc_first_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_blob: bool (TALLOC_CTX *, LDAP *, LDAPMessage *, const char *, DATA_BLOB *) +smbldap_talloc_smallest_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) diff --git a/source3/lib/ABI/smbldap-2.sigs b/source3/lib/ABI/smbldap-2.sigs new file mode 100644 index 0000000..8abefec --- /dev/null +++ b/source3/lib/ABI/smbldap-2.sigs @@ -0,0 +1,32 @@ +smbldap_add: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_delete: int (struct smbldap_state *, const char *) +smbldap_extended_operation: int (struct smbldap_state *, const char *, struct berval *, LDAPControl **, LDAPControl **, char **, struct berval **) +smbldap_free_struct: void (struct smbldap_state **) +smbldap_get_ldap: LDAP *(struct smbldap_state *) +smbldap_get_paged_results: bool (struct smbldap_state *) +smbldap_get_single_attribute: bool (LDAP *, LDAPMessage *, const char *, char *, int) +smbldap_has_control: bool (LDAP *, const char *) +smbldap_has_extension: bool (LDAP *, const char *) +smbldap_has_naming_context: bool (LDAP *, const char *) +smbldap_init: NTSTATUS (TALLOC_CTX *, struct tevent_context *, const char *, bool, const char *, const char *, struct smbldap_state **) +smbldap_make_mod: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const char *) +smbldap_make_mod_blob: void (LDAP *, LDAPMessage *, LDAPMod ***, const char *, const DATA_BLOB *) +smbldap_modify: int (struct smbldap_state *, const char *, LDAPMod **) +smbldap_pull_sid: bool (LDAP *, LDAPMessage *, const char *, struct dom_sid *) +smbldap_search: int (struct smbldap_state *, const char *, int, const char *, const char **, int, LDAPMessage **) +smbldap_search_paged: int (struct smbldap_state *, const char *, int, const char *, const char **, int, int, LDAPMessage **, void **) +smbldap_search_suffix: int (struct smbldap_state *, const char *, const char **, LDAPMessage **) +smbldap_set_bind_callback: void (struct smbldap_state *, smbldap_bind_callback_fn, void *) +smbldap_set_creds: bool (struct smbldap_state *, bool, const char *, const char *) +smbldap_set_mod: void (LDAPMod ***, int, const char *, const char *) +smbldap_set_mod_blob: void (LDAPMod ***, int, const char *, const DATA_BLOB *) +smbldap_set_paged_results: void (struct smbldap_state *, bool) +smbldap_setup_full_conn: int (LDAP **, const char *) +smbldap_start_tls: int (LDAP *, int) +smbldap_talloc_autofree_ldapmod: void (TALLOC_CTX *, LDAPMod **) +smbldap_talloc_autofree_ldapmsg: void (TALLOC_CTX *, LDAPMessage *) +smbldap_talloc_dn: char *(TALLOC_CTX *, LDAP *, LDAPMessage *) +smbldap_talloc_first_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) +smbldap_talloc_single_blob: bool (TALLOC_CTX *, LDAP *, LDAPMessage *, const char *, DATA_BLOB *) +smbldap_talloc_smallest_attribute: char *(LDAP *, LDAPMessage *, const char *, TALLOC_CTX *) diff --git a/source3/lib/addrchange.c b/source3/lib/addrchange.c new file mode 100644 index 0000000..9a64e8d --- /dev/null +++ b/source3/lib/addrchange.c @@ -0,0 +1,314 @@ +/* + * Samba Unix/Linux SMB client library + * Copyright (C) Volker Lendecke 2011 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/addrchange.h" +#include "../lib/util/tevent_ntstatus.h" + +#ifdef HAVE_LINUX_RTNETLINK_H + +#include "asm/types.h" +#include "linux/netlink.h" +#include "linux/rtnetlink.h" +#include "lib/tsocket/tsocket.h" + +struct addrchange_context { + struct tdgram_context *sock; +}; + +NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx, + struct addrchange_context **pctx) +{ + struct addrchange_context *ctx; + struct sockaddr_nl addr; + NTSTATUS status; + int sock = -1; + int res; + bool ok; + + ctx = talloc(mem_ctx, struct addrchange_context); + if (ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + ok = smb_set_close_on_exec(sock); + if (!ok) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + res = set_blocking(sock, false); + if (res == -1) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + /* + * We're interested in address changes + */ + ZERO_STRUCT(addr); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_IFADDR; + + res = bind(sock, (struct sockaddr *)(void *)&addr, sizeof(addr)); + if (res == -1) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + res = tdgram_bsd_existing_socket(ctx, sock, &ctx->sock); + if (res == -1) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + *pctx = ctx; + return NT_STATUS_OK; +fail: + if (sock != -1) { + close(sock); + } + TALLOC_FREE(ctx); + return status; +} + +struct addrchange_state { + struct tevent_context *ev; + struct addrchange_context *ctx; + uint8_t *buf; + struct tsocket_address *fromaddr; + + enum addrchange_type type; + struct sockaddr_storage addr; +}; + +static void addrchange_done(struct tevent_req *subreq); + +struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct addrchange_context *ctx) +{ + struct tevent_req *req, *subreq; + struct addrchange_state *state; + + req = tevent_req_create(mem_ctx, &state, struct addrchange_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = ctx; + + subreq = tdgram_recvfrom_send(state, state->ev, state->ctx->sock); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, state->ev); + } + tevent_req_set_callback(subreq, addrchange_done, req); + return req; +} + +static void addrchange_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct addrchange_state *state = tevent_req_data( + req, struct addrchange_state); + union { + struct sockaddr sa; + struct sockaddr_nl nl; + struct sockaddr_storage ss; + } fromaddr; + struct nlmsghdr *h; + struct ifaddrmsg *ifa; + struct rtattr *rta; + ssize_t received; + int len; + int err; + bool found; + + received = tdgram_recvfrom_recv(subreq, &err, state, + &state->buf, + &state->fromaddr); + TALLOC_FREE(subreq); + if (received == -1) { + DEBUG(10, ("tdgram_recvfrom_recv returned %s\n", strerror(err))); + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + len = tsocket_address_bsd_sockaddr(state->fromaddr, + &fromaddr.sa, + sizeof(fromaddr)); + + if ((len != sizeof(fromaddr.nl) || + fromaddr.sa.sa_family != AF_NETLINK)) + { + DEBUG(10, ("Got message from wrong addr\n")); + goto retry; + } + + if (fromaddr.nl.nl_pid != 0) { + DEBUG(10, ("Got msg from pid %d, not from the kernel\n", + (int)fromaddr.nl.nl_pid)); + goto retry; + } + + if (received < sizeof(struct nlmsghdr)) { + DEBUG(10, ("received %d, expected at least %d\n", + (int)received, (int)sizeof(struct nlmsghdr))); + goto retry; + } + + h = (struct nlmsghdr *)state->buf; + if (h->nlmsg_len < sizeof(struct nlmsghdr)) { + DEBUG(10, ("nlmsg_len=%d, expected at least %d\n", + (int)h->nlmsg_len, (int)sizeof(struct nlmsghdr))); + goto retry; + } + if (h->nlmsg_len > received) { + DEBUG(10, ("nlmsg_len=%d, expected at most %d\n", + (int)h->nlmsg_len, (int)received)); + goto retry; + } + switch (h->nlmsg_type) { + case RTM_NEWADDR: + state->type = ADDRCHANGE_ADD; + break; + case RTM_DELADDR: + state->type = ADDRCHANGE_DEL; + break; + default: + DEBUG(10, ("Got unexpected type %d - ignoring\n", h->nlmsg_type)); + goto retry; + } + + if (h->nlmsg_len < sizeof(struct nlmsghdr)+sizeof(struct ifaddrmsg)) { + DEBUG(10, ("nlmsg_len=%d, expected at least %d\n", + (int)h->nlmsg_len, + (int)(sizeof(struct nlmsghdr) + +sizeof(struct ifaddrmsg)))); + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } + + ifa = (struct ifaddrmsg *)NLMSG_DATA(h); + + state->addr.ss_family = ifa->ifa_family; + + len = h->nlmsg_len - sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg); + + found = false; + + for (rta = IFA_RTA(ifa); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + + if ((rta->rta_type != IFA_LOCAL) + && (rta->rta_type != IFA_ADDRESS)) { + continue; + } + + switch (ifa->ifa_family) { + case AF_INET: { + struct sockaddr_in *v4_addr; + v4_addr = (struct sockaddr_in *)(void *)&state->addr; + + if (RTA_PAYLOAD(rta) != sizeof(uint32_t)) { + continue; + } + v4_addr->sin_addr.s_addr = *(uint32_t *)RTA_DATA(rta); + found = true; + break; + } + case AF_INET6: { + struct sockaddr_in6 *v6_addr; + v6_addr = (struct sockaddr_in6 *)(void *)&state->addr; + + if (RTA_PAYLOAD(rta) != + sizeof(v6_addr->sin6_addr.s6_addr)) { + continue; + } + memcpy(v6_addr->sin6_addr.s6_addr, RTA_DATA(rta), + sizeof(v6_addr->sin6_addr.s6_addr)); + found = true; + break; + } + } + } + + if (!found) { + tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS); + return; + } + + tevent_req_done(req); + return; + +retry: + TALLOC_FREE(state->buf); + TALLOC_FREE(state->fromaddr); + + subreq = tdgram_recvfrom_send(state, state->ev, state->ctx->sock); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, addrchange_done, req); +} + +NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type, + struct sockaddr_storage *addr) +{ + struct addrchange_state *state = tevent_req_data( + req, struct addrchange_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + *type = state->type; + *addr = state->addr; + tevent_req_received(req); + return NT_STATUS_OK; +} + +#else + +NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx, + struct addrchange_context **pctx) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct addrchange_context *ctx) +{ + return NULL; +} + +NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type, + struct sockaddr_storage *addr) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +#endif diff --git a/source3/lib/addrchange.h b/source3/lib/addrchange.h new file mode 100644 index 0000000..1106380 --- /dev/null +++ b/source3/lib/addrchange.h @@ -0,0 +1,45 @@ +/* + * Samba Unix/Linux SMB client library + * Copyright (C) Volker Lendecke 2011 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __ADDRCHANGE_H__ +#define __ADDRCHANGE_H__ + +#include "replace.h" +#include "system/network.h" +#include <talloc.h> +#include <tevent.h> +#include "libcli/util/ntstatus.h" + +struct addrchange_context; + +NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx, + struct addrchange_context **pctx); + +struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct addrchange_context *ctx); + +enum addrchange_type { + ADDRCHANGE_ADD, + ADDRCHANGE_DEL +}; + +NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type, + struct sockaddr_storage *addr); + +#endif diff --git a/source3/lib/adouble.c b/source3/lib/adouble.c new file mode 100644 index 0000000..1e8dbc4 --- /dev/null +++ b/source3/lib/adouble.c @@ -0,0 +1,2856 @@ +/* + * Samba AppleDouble helpers + * + * Copyright (C) Ralph Boehme, 2019 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "adouble.h" +#include "MacExtensions.h" +#include "string_replace.h" +#include "smbd/smbd.h" +#include "system/filesys.h" +#include "libcli/security/security.h" +#include "lib/util_macstreams.h" +#include "auth.h" + +/* + "._" AppleDouble Header File Layout: + + MAGIC 0x00051607 + VERSION 0x00020000 + FILLER 0 + COUNT 2 + .-- AD ENTRY[0] Finder Info Entry (must be first) + .--+-- AD ENTRY[1] Resource Fork Entry (must be last) + | | ///////////// + | '-> FINDER INFO Fixed Size Data (32 Bytes) + | ~~~~~~~~~~~~~ 2 Bytes Padding + | EXT ATTR HDR Fixed Size Data (36 Bytes) + | ///////////// + | ATTR ENTRY[0] --. + | ATTR ENTRY[1] --+--. + | ATTR ENTRY[2] --+--+--. + | ... | | | + | ATTR ENTRY[N] --+--+--+--. + | ATTR DATA 0 <-' | | | + | //////////// | | | + | ATTR DATA 1 <----' | | + | ///////////// | | + | ATTR DATA 2 <-------' | + | ///////////// | + | ... | + | ATTR DATA N <----------' + | ///////////// + | ... Attribute Free Space + | + '----> RESOURCE FORK + ... Variable Sized Data + ... +*/ + +/* Number of actually used entries */ +#define ADEID_NUM_XATTR 8 +#define ADEID_NUM_DOT_UND 2 +#define ADEID_NUM_RSRC_XATTR 1 + +/* Sizes of relevant entry bits */ +#define ADEDLEN_MAGIC 4 +#define ADEDLEN_VERSION 4 +#define ADEDLEN_FILLER 16 +#define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */ +#define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */ +#define ADEDLEN_NENTRIES 2 +#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \ + ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */ +#define AD_ENTRY_LEN_EID 4 +#define AD_ENTRY_LEN_OFF 4 +#define AD_ENTRY_LEN_LEN 4 +#define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN) + +/* Offsets */ +#define ADEDOFF_MAGIC 0 +#define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC) +#define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION) +#define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER) + +#define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \ + (ADEID_NUM_XATTR * AD_ENTRY_LEN)) +#define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI) +#define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT) +#define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \ + ADEDLEN_FILEDATESI) +#define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI) +#define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV) +#define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO) +#define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN) + +#define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \ + (ADEID_NUM_DOT_UND * AD_ENTRY_LEN)) +#define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI) + +#define AD_DATASZ_XATTR (AD_HEADER_LEN + \ + (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \ + ADEDLEN_FINDERI + ADEDLEN_COMMENT + \ + ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \ + ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \ + ADEDLEN_PRIVSYN + ADEDLEN_PRIVID) + +#if AD_DATASZ_XATTR != 402 +#error bad size for AD_DATASZ_XATTR +#endif + +#define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \ + (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \ + ADEDLEN_FINDERI) +#if AD_DATASZ_DOT_UND != 82 +#error bad size for AD_DATASZ_DOT_UND +#endif + +#define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */ +#define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */ +#define AD_XATTR_HDR_SIZE 36 +#define AD_XATTR_MAX_HDR_SIZE 65536 +#define ADX_ENTRY_FIXED_SIZE (4+4+2+1) + +/* + * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory + * representation as well as the on-disk format. + * + * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if + * the length of the FinderInfo entry is larger then 32 bytes. It is then + * preceded with 2 bytes padding. + * + * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c + */ + +struct ad_xattr_header { + uint32_t adx_magic; /* ATTR_HDR_MAGIC */ + uint32_t adx_debug_tag; /* for debugging == file id of owning file */ + uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */ + uint32_t adx_data_start; /* file offset to attribute data area */ + uint32_t adx_data_length; /* length of attribute data area */ + uint32_t adx_reserved[3]; + uint16_t adx_flags; + uint16_t adx_num_attrs; +}; + +/* On-disk entries are aligned on 4 byte boundaries */ +struct ad_xattr_entry { + uint32_t adx_offset; /* file offset to data */ + uint32_t adx_length; /* size of attribute data */ + uint16_t adx_flags; + uint8_t adx_namelen; /* included the NULL terminator */ + char *adx_name; /* NULL-terminated UTF-8 name */ +}; + +struct ad_entry { + size_t ade_off; + size_t ade_len; +}; + +struct adouble { + files_struct *ad_fsp; + bool ad_opened; + adouble_type_t ad_type; + uint32_t ad_magic; + uint32_t ad_version; + uint8_t ad_filler[ADEDLEN_FILLER]; + struct ad_entry ad_eid[ADEID_MAX]; + char *ad_data; + char *ad_rsrc_data; + struct ad_xattr_header adx_header; + struct ad_xattr_entry *adx_entries; + char *adx_data; +}; + +struct ad_entry_order { + uint32_t id, offset, len; +}; + +/* Netatalk AppleDouble metadata xattr */ +static const +struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = { + {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI}, + {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0}, + {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI}, + {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI}, + {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0}, + {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0}, + {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0}, + {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0}, + {0, 0, 0} +}; + +/* AppleDouble resource fork file (the ones prefixed by "._") */ +static const +struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = { + {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI}, + {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0}, + {0, 0, 0} +}; + +/* Conversion from enumerated id to on-disk AppleDouble id */ +#define AD_EID_DISK(a) (set_eid[a]) +static const uint32_t set_eid[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + AD_DEV, AD_INO, AD_SYN, AD_ID +}; + +static char empty_resourcefork[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73, + 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F, + 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E, + 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79, + 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C, + 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF +}; + +size_t ad_getentrylen(const struct adouble *ad, int eid) +{ + return ad->ad_eid[eid].ade_len; +} + +size_t ad_getentryoff(const struct adouble *ad, int eid) +{ + return ad->ad_eid[eid].ade_off; +} + +size_t ad_setentrylen(struct adouble *ad, int eid, size_t len) +{ + return ad->ad_eid[eid].ade_len = len; +} + +size_t ad_setentryoff(struct adouble *ad, int eid, size_t off) +{ + return ad->ad_eid[eid].ade_off = off; +} + +/* + * All entries besides FinderInfo and resource fork must fit into the + * buffer. FinderInfo is special as it may be larger then the default 32 bytes + * if it contains marshalled xattrs, which we will fixup that in + * ad_convert(). The first 32 bytes however must also be part of the buffer. + * + * The resource fork is never accessed directly by the ad_data buf. + */ +static bool ad_entry_check_size(uint32_t eid, + size_t bufsize, + uint32_t off, + uint32_t got_len) +{ + struct { + off_t expected_len; + bool fixed_size; + bool minimum_size; + } ad_checks[] = { + [ADEID_DFORK] = {-1, false, false}, /* not applicable */ + [ADEID_RFORK] = {-1, false, false}, /* no limit */ + [ADEID_NAME] = {ADEDLEN_NAME, false, false}, + [ADEID_COMMENT] = {ADEDLEN_COMMENT, false, false}, + [ADEID_ICONBW] = {ADEDLEN_ICONBW, true, false}, + [ADEID_ICONCOL] = {ADEDLEN_ICONCOL, false, false}, + [ADEID_FILEI] = {ADEDLEN_FILEI, true, false}, + [ADEID_FILEDATESI] = {ADEDLEN_FILEDATESI, true, false}, + [ADEID_FINDERI] = {ADEDLEN_FINDERI, false, true}, + [ADEID_MACFILEI] = {ADEDLEN_MACFILEI, true, false}, + [ADEID_PRODOSFILEI] = {ADEDLEN_PRODOSFILEI, true, false}, + [ADEID_MSDOSFILEI] = {ADEDLEN_MSDOSFILEI, true, false}, + [ADEID_SHORTNAME] = {ADEDLEN_SHORTNAME, false, false}, + [ADEID_AFPFILEI] = {ADEDLEN_AFPFILEI, true, false}, + [ADEID_DID] = {ADEDLEN_DID, true, false}, + [ADEID_PRIVDEV] = {ADEDLEN_PRIVDEV, true, false}, + [ADEID_PRIVINO] = {ADEDLEN_PRIVINO, true, false}, + [ADEID_PRIVSYN] = {ADEDLEN_PRIVSYN, true, false}, + [ADEID_PRIVID] = {ADEDLEN_PRIVID, true, false}, + }; + + if (eid >= ADEID_MAX) { + return false; + } + if (got_len == 0) { + /* Entry present, but empty, allow */ + return true; + } + if (ad_checks[eid].expected_len == 0) { + /* + * Shouldn't happen: implicitly initialized to zero because + * explicit initializer missing. + */ + return false; + } + if (ad_checks[eid].expected_len == -1) { + /* Unused or no limit */ + return true; + } + if (ad_checks[eid].fixed_size) { + if (ad_checks[eid].expected_len != got_len) { + /* Wrong size for fixed size entry. */ + return false; + } + } else { + if (ad_checks[eid].minimum_size) { + if (got_len < ad_checks[eid].expected_len) { + /* + * Too small for variable sized entry with + * minimum size. + */ + return false; + } + } else { + if (got_len > ad_checks[eid].expected_len) { + /* Too big for variable sized entry. */ + return false; + } + } + } + if (off + got_len < off) { + /* wrap around */ + return false; + } + if (off + got_len > bufsize) { + /* overflow */ + return false; + } + return true; +} + +/** + * Return a pointer to an AppleDouble entry + * + * Returns NULL if the entry is not present + **/ +char *ad_get_entry(const struct adouble *ad, int eid) +{ + size_t bufsize = talloc_get_size(ad->ad_data); + off_t off = ad_getentryoff(ad, eid); + size_t len = ad_getentrylen(ad, eid); + bool valid; + + valid = ad_entry_check_size(eid, bufsize, off, len); + if (!valid) { + return NULL; + } + + if (off == 0 || len == 0) { + return NULL; + } + + return ad->ad_data + off; +} + +/** + * Get a date + **/ +int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date) +{ + bool xlate = (dateoff & AD_DATE_UNIX); + char *p = NULL; + + dateoff &= AD_DATE_MASK; + p = ad_get_entry(ad, ADEID_FILEDATESI); + if (p == NULL) { + return -1; + } + + if (dateoff > AD_DATE_ACCESS) { + return -1; + } + + memcpy(date, p + dateoff, sizeof(uint32_t)); + + if (xlate) { + *date = AD_DATE_TO_UNIX(*date); + } + return 0; +} + +/** + * Set a date + **/ +int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date) +{ + bool xlate = (dateoff & AD_DATE_UNIX); + char *p = NULL; + + p = ad_get_entry(ad, ADEID_FILEDATESI); + if (p == NULL) { + return -1; + } + + dateoff &= AD_DATE_MASK; + if (xlate) { + date = AD_DATE_FROM_UNIX(date); + } + + if (dateoff > AD_DATE_ACCESS) { + return -1; + } + + memcpy(p + dateoff, &date, sizeof(date)); + + return 0; +} + + +/** + * Map on-disk AppleDouble id to enumerated id + **/ +static uint32_t get_eid(uint32_t eid) +{ + if (eid <= 15) { + return eid; + } + + switch (eid) { + case AD_DEV: + return ADEID_PRIVDEV; + case AD_INO: + return ADEID_PRIVINO; + case AD_SYN: + return ADEID_PRIVSYN; + case AD_ID: + return ADEID_PRIVID; + default: + break; + } + + return 0; +} + +/* + * Move resourcefork data in an AppleDouble file + * + * This is supposed to make room in an AppleDouble file by moving the + * resourcefork data behind the space required for packing additional xattr data + * in the extended FinderInfo entry. + * + * When we're called we're expecting an AppleDouble file with just two entries + * (FinderInfo an Resourcefork) and the resourcefork is expected at a fixed + * offset of ADEDOFF_RFORK_DOT_UND. + */ +static bool ad_pack_move_reso(struct vfs_handle_struct *handle, + struct adouble *ad, + files_struct *fsp) +{ + size_t reso_len; + size_t reso_off; + size_t n; + bool ok; + + reso_len = ad_getentrylen(ad, ADEID_RFORK); + reso_off = ad_getentryoff(ad, ADEID_RFORK); + + if (reso_len == 0) { + return true; + } + + if (ad->ad_rsrc_data == NULL) { + /* + * This buffer is already set when converting a resourcefork + * stream from vfs_streams_depot backend via ad_unconvert(). It + * is NULL with vfs_streams_xattr where the resourcefork stream + * is stored in an AppleDouble sidecar file vy vfs_fruit. + */ + ad->ad_rsrc_data = talloc_size(ad, reso_len); + if (ad->ad_rsrc_data == NULL) { + return false; + } + + n = SMB_VFS_NEXT_PREAD(handle, + fsp, + ad->ad_rsrc_data, + reso_len, + ADEDOFF_RFORK_DOT_UND); + if (n != reso_len) { + DBG_ERR("Read on [%s] failed\n", + fsp_str_dbg(fsp)); + ok = false; + goto out; + } + } + + n = SMB_VFS_NEXT_PWRITE(handle, + fsp, + ad->ad_rsrc_data, + reso_len, + reso_off); + if (n != reso_len) { + DBG_ERR("Write on [%s] failed\n", + fsp_str_dbg(fsp)); + ok = false; + goto out; + } + + ok = true; +out: + return ok; +} + +static bool ad_pack_xattrs(struct vfs_handle_struct *handle, + struct adouble *ad, + files_struct *fsp) +{ + struct ad_xattr_header *h = &ad->adx_header; + size_t oldsize; + uint32_t off; + uint32_t data_off; + uint16_t i; + bool ok; + + if (ad->adx_entries == NULL) { + /* No xattrs, nothing to pack */ + return true; + } + + if (fsp == NULL) { + DBG_ERR("fsp unexpectedly NULL\n"); + return false; + } + + oldsize = talloc_get_size(ad->ad_data); + if (oldsize < AD_XATTR_MAX_HDR_SIZE) { + ad->ad_data = talloc_realloc(ad, + ad->ad_data, + char, + AD_XATTR_MAX_HDR_SIZE); + if (ad->ad_data == NULL) { + return false; + } + memset(ad->ad_data + oldsize, + 0, + AD_XATTR_MAX_HDR_SIZE - oldsize); + } + + /* + * First, let's calculate the start of the xattr data area which will be + * after the xattr header + header entries. + */ + + data_off = ad_getentryoff(ad, ADEID_FINDERI); + data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE; + /* 2 bytes padding */ + data_off += 2; + + for (i = 0; i < h->adx_num_attrs; i++) { + struct ad_xattr_entry *e = &ad->adx_entries[i]; + + /* Align on 4 byte boundary */ + data_off = (data_off + 3) & ~3; + + data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE; + if (data_off >= AD_XATTR_MAX_HDR_SIZE) { + return false; + } + } + + off = ad_getentryoff(ad, ADEID_FINDERI); + off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE; + /* 2 bytes padding */ + off += 2; + + for (i = 0; i < h->adx_num_attrs; i++) { + struct ad_xattr_entry *e = &ad->adx_entries[i]; + + /* Align on 4 byte boundary */ + off = (off + 3) & ~3; + + e->adx_offset = data_off; + data_off += e->adx_length; + + DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] " + "adx_data_off [%zu]\n", + (size_t)i, + e->adx_name, + (size_t)e->adx_namelen, + (size_t)off, + (size_t)e->adx_length, + (size_t)e->adx_offset); + + if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) { + return false; + } + RSIVAL(ad->ad_data, off, e->adx_offset); + off += 4; + + if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) { + return false; + } + RSIVAL(ad->ad_data, off, e->adx_length); + off += 4; + + if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) { + return false; + } + RSSVAL(ad->ad_data, off, e->adx_flags); + off += 2; + + if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) { + return false; + } + SCVAL(ad->ad_data, off, e->adx_namelen); + off += 1; + + if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) { + return false; + } + memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen); + off += e->adx_namelen; + } + + h->adx_data_start = off; + h->adx_data_length = talloc_get_size(ad->adx_data); + h->adx_total_size = h->adx_data_start + h->adx_data_length; + + if (talloc_get_size(ad->ad_data) < h->adx_total_size) { + ad->ad_data = talloc_realloc(ad, + ad->ad_data, + char, + h->adx_total_size); + if (ad->ad_data == NULL) { + return false; + } + } + + memcpy(ad->ad_data + h->adx_data_start, + ad->adx_data, + h->adx_data_length); + + ad_setentrylen(ad, + ADEID_FINDERI, + h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI)); + + ad_setentryoff(ad, + ADEID_RFORK, + ad_getentryoff(ad, ADEID_FINDERI) + + ad_getentrylen(ad, ADEID_FINDERI)); + + memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER); + + /* + * Rewind, then update the header fields. + */ + + off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI; + /* 2 bytes padding */ + off += 2; + + RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC); + off += 4; + RSIVAL(ad->ad_data, off, 0); + off += 4; + RSIVAL(ad->ad_data, off, h->adx_total_size); + off += 4; + RSIVAL(ad->ad_data, off, h->adx_data_start); + off += 4; + RSIVAL(ad->ad_data, off, h->adx_data_length); + off += 4; + + /* adx_reserved and adx_flags */ + memset(ad->ad_data + off, 0, 3 * 4 + 2); + off += 3 * 4 + 2; + + RSSVAL(ad->ad_data, off, h->adx_num_attrs); + off += 2; + + ok = ad_pack_move_reso(handle, ad, fsp); + if (!ok) { + DBG_ERR("Moving resourcefork of [%s] failed\n", + fsp_str_dbg(fsp)); + return false; + } + + return true; +} + +/** + * Pack AppleDouble structure into data buffer + **/ +static bool ad_pack(struct vfs_handle_struct *handle, + struct adouble *ad, + files_struct *fsp) +{ + uint32_t eid; + uint16_t nent; + uint32_t bufsize; + uint32_t offset = 0; + bool ok; + + bufsize = talloc_get_size(ad->ad_data); + if (bufsize < AD_DATASZ_DOT_UND) { + DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize); + return false; + } + + if (offset + ADEDLEN_MAGIC < offset || + offset + ADEDLEN_MAGIC >= bufsize) { + return false; + } + RSIVAL(ad->ad_data, offset, ad->ad_magic); + offset += ADEDLEN_MAGIC; + + if (offset + ADEDLEN_VERSION < offset || + offset + ADEDLEN_VERSION >= bufsize) { + return false; + } + RSIVAL(ad->ad_data, offset, ad->ad_version); + offset += ADEDLEN_VERSION; + + if (offset + ADEDLEN_FILLER < offset || + offset + ADEDLEN_FILLER >= bufsize) { + return false; + } + if (ad->ad_type == ADOUBLE_RSRC) { + memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER); + } + offset += ADEDLEN_FILLER; + + if (offset + ADEDLEN_NENTRIES < offset || + offset + ADEDLEN_NENTRIES >= bufsize) { + return false; + } + offset += ADEDLEN_NENTRIES; + + ok = ad_pack_xattrs(handle, ad, fsp); + if (!ok) { + return false; + } + + for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) { + if (ad->ad_eid[eid].ade_off == 0) { + /* + * ade_off is also used as indicator whether a + * specific entry is used or not + */ + continue; + } + + if (offset + AD_ENTRY_LEN_EID < offset || + offset + AD_ENTRY_LEN_EID >= bufsize) { + return false; + } + RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid)); + offset += AD_ENTRY_LEN_EID; + + if (offset + AD_ENTRY_LEN_OFF < offset || + offset + AD_ENTRY_LEN_OFF >= bufsize) { + return false; + } + RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off); + offset += AD_ENTRY_LEN_OFF; + + if (offset + AD_ENTRY_LEN_LEN < offset || + offset + AD_ENTRY_LEN_LEN >= bufsize) { + return false; + } + RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len); + offset += AD_ENTRY_LEN_LEN; + + nent++; + } + + if (ADEDOFF_NENTRIES + 2 >= bufsize) { + return false; + } + RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent); + + return true; +} + +static bool ad_unpack_xattrs(struct adouble *ad) +{ + struct ad_xattr_header *h = &ad->adx_header; + size_t bufsize = talloc_get_size(ad->ad_data); + const char *p = ad->ad_data; + uint32_t hoff; + uint32_t i; + + if (ad->ad_type != ADOUBLE_RSRC) { + return false; + } + + if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) { + return true; + } + + /* + * Ensure the buffer ad->ad_data was allocated by ad_alloc() for an + * ADOUBLE_RSRC type (._ AppleDouble file on-disk). + */ + if (bufsize != AD_XATTR_MAX_HDR_SIZE) { + return false; + } + + /* 2 bytes padding */ + hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2; + + h->adx_magic = RIVAL(p, hoff + 0); + h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */ + h->adx_total_size = RIVAL(p, hoff + 8); + h->adx_data_start = RIVAL(p, hoff + 12); + h->adx_data_length = RIVAL(p, hoff + 16); + h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */ + h->adx_num_attrs = RSVAL(p, hoff + 34); + + if (h->adx_magic != AD_XATTR_HDR_MAGIC) { + DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic); + return false; + } + + if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) { + DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size); + return false; + } + if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) { + DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size); + return false; + } + + if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) { + DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start); + return false; + } + + if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) { + DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length); + return false; + } + if ((h->adx_data_start + h->adx_data_length) > + ad->adx_header.adx_total_size) + { + DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length); + return false; + } + + if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) { + DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs); + return false; + } + + if (h->adx_num_attrs == 0) { + return true; + } + + ad->adx_entries = talloc_zero_array( + ad, struct ad_xattr_entry, h->adx_num_attrs); + if (ad->adx_entries == NULL) { + return false; + } + + hoff += AD_XATTR_HDR_SIZE; + + for (i = 0; i < h->adx_num_attrs; i++) { + struct ad_xattr_entry *e = &ad->adx_entries[i]; + + hoff = (hoff + 3) & ~3; + + e->adx_offset = RIVAL(p, hoff + 0); + e->adx_length = RIVAL(p, hoff + 4); + e->adx_flags = RSVAL(p, hoff + 8); + e->adx_namelen = *(p + hoff + 10); + + if (e->adx_offset >= ad->adx_header.adx_total_size) { + DBG_ERR("Bad adx_offset: %" PRIx32 "\n", + e->adx_offset); + return false; + } + + if ((e->adx_offset + e->adx_length) < e->adx_offset) { + DBG_ERR("Bad adx_length: %" PRIx32 "\n", + e->adx_length); + return false; + } + + if ((e->adx_offset + e->adx_length) > + ad->adx_header.adx_total_size) + { + DBG_ERR("Bad adx_length: %" PRIx32 "\n", + e->adx_length); + return false; + } + + if (e->adx_namelen == 0) { + DBG_ERR("Bad adx_namelen: %" PRIx32 "\n", + e->adx_namelen); + return false; + } + if ((hoff + 11 + e->adx_namelen) < hoff + 11) { + DBG_ERR("Bad adx_namelen: %" PRIx32 "\n", + e->adx_namelen); + return false; + } + if ((hoff + 11 + e->adx_namelen) > + ad->adx_header.adx_data_start) + { + DBG_ERR("Bad adx_namelen: %" PRIx32 "\n", + e->adx_namelen); + return false; + } + + e->adx_name = talloc_strndup(ad->adx_entries, + p + hoff + 11, + e->adx_namelen); + if (e->adx_name == NULL) { + return false; + } + + DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n", + e->adx_name, e->adx_offset, e->adx_length); + dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset), + e->adx_length); + + hoff += 11 + e->adx_namelen; + } + + return true; +} + +/** + * Unpack an AppleDouble blob into a struct adoble + **/ +static bool ad_unpack(struct adouble *ad, const size_t nentries, + size_t filesize) +{ + size_t bufsize = talloc_get_size(ad->ad_data); + size_t adentries, i; + uint32_t eid, len, off; + bool ok; + + /* + * The size of the buffer ad->ad_data is checked when read, so + * we wouldn't have to check our own offsets, a few extra + * checks won't hurt though. We have to check the offsets we + * read from the buffer anyway. + */ + + if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) { + DBG_NOTICE("Bad size\n"); + return false; + } + + ad->ad_magic = RIVAL(ad->ad_data, 0); + ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION); + if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) { + DBG_NOTICE("Wrong magic or version\n"); + return false; + } + + memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER); + + adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES); + if (adentries != nentries) { + DBG_NOTICE("Invalid number of entries: %zu\n", adentries); + return false; + } + + /* now, read in the entry bits */ + for (i = 0; i < adentries; i++) { + eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN)); + eid = get_eid(eid); + off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4); + len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8); + + if (!eid || eid >= ADEID_MAX) { + DBG_NOTICE("Bogus eid %d\n", eid); + return false; + } + + /* + * All entries other than the resource fork are + * expected to be read into the ad_data buffer, so + * ensure the specified offset is within that bound + */ + if ((off > bufsize) && (eid != ADEID_RFORK)) { + DBG_NOTICE("Bogus eid %d: off: %" PRIu32 + ", len: %" PRIu32 "\n", + eid, + off, + len); + return false; + } + + ok = ad_entry_check_size(eid, bufsize, off, len); + if (!ok) { + DBG_NOTICE("Bogus eid [%" PRIu32 "] bufsize [%zu] " + "off [%" PRIu32 "] len [%" PRIu32 "]\n", + eid, + bufsize, + off, + len); + return false; + } + + /* + * That would be obviously broken + */ + if (off > filesize) { + DBG_NOTICE("Bogus eid %d: off: %" PRIu32 + ", len: %" PRIu32 "\n", + eid, + off, + len); + return false; + } + + /* + * Check for any entry that has its end beyond the + * filesize. + */ + if (off + len < off) { + DBG_NOTICE("offset wrap in eid %d: off: %" PRIu32 + ", len: %" PRIu32 "\n", + eid, + off, + len); + return false; + + } + if (off + len > filesize) { + /* + * If this is the resource fork entry, we fix + * up the length, for any other entry we bail + * out. + */ + if (eid != ADEID_RFORK) { + DBG_NOTICE("Bogus eid %d: off: %" PRIu32 + ", len: %" PRIu32 "\n", + eid, + off, + len); + return false; + } + + /* + * Fixup the resource fork entry by limiting + * the size to entryoffset - filesize. + */ + len = filesize - off; + DBG_NOTICE("Limiting ADEID_RFORK: off: %" PRIu32 + ", len: %" PRIu32 "\n", + off, + len); + } + + ad->ad_eid[eid].ade_off = off; + ad->ad_eid[eid].ade_len = len; + } + + if (ad->ad_type == ADOUBLE_RSRC) { + ok = ad_unpack_xattrs(ad); + if (!ok) { + return false; + } + } + + return true; +} + +static bool ad_convert_move_reso(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + char *buf = NULL; + size_t rforklen; + size_t rforkoff; + ssize_t n; + int ret; + + rforklen = ad_getentrylen(ad, ADEID_RFORK); + if (rforklen == 0) { + return true; + } + + buf = talloc_size(ad, rforklen); + if (buf == NULL) { + /* + * This allocates a buffer for reading the resource fork data in + * one big swoop. Resource forks won't be larger then, say, 64 + * MB, I swear, so just doing the allocation with the talloc + * limit as safeguard seems safe. + */ + DBG_ERR("Failed to allocate %zu bytes for rfork\n", + rforklen); + return false; + } + + rforkoff = ad_getentryoff(ad, ADEID_RFORK); + + n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff); + if (n != rforklen) { + DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n", + rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno)); + return false; + } + + rforkoff = ADEDOFF_RFORK_DOT_UND; + + n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff); + if (n != rforklen) { + DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n", + rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno)); + return false; + } + + ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND); + + ret = ad_fset(handle, ad, ad->ad_fsp); + if (ret != 0) { + DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp)); + return false; + } + + return true; +} + +static bool ad_convert_xattr(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname, + const char *catia_mappings, + bool *converted_xattr) +{ + static struct char_mappings **string_replace_cmaps = NULL; + uint16_t i; + int saved_errno = 0; + NTSTATUS status; + int rc; + bool ok; + + *converted_xattr = false; + + if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { + return true; + } + + if (string_replace_cmaps == NULL) { + const char **mappings = NULL; + + mappings = str_list_make_v3_const( + talloc_tos(), catia_mappings, NULL); + if (mappings == NULL) { + return false; + } + string_replace_cmaps = string_replace_init_map( + handle->conn->sconn, mappings); + TALLOC_FREE(mappings); + } + + for (i = 0; i < ad->adx_header.adx_num_attrs; i++) { + struct ad_xattr_entry *e = &ad->adx_entries[i]; + char *mapped_name = NULL; + char *tmp = NULL; + struct smb_filename *stream_name = NULL; + files_struct *fsp = NULL; + ssize_t nwritten; + + status = string_replace_allocate(handle->conn, + e->adx_name, + string_replace_cmaps, + talloc_tos(), + &mapped_name, + vfs_translate_to_windows); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) + { + DBG_ERR("string_replace_allocate failed\n"); + ok = false; + goto fail; + } + + tmp = mapped_name; + mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp); + TALLOC_FREE(tmp); + if (mapped_name == NULL) { + ok = false; + goto fail; + } + + stream_name = synthetic_smb_fname(talloc_tos(), + smb_fname->base_name, + mapped_name, + NULL, + smb_fname->twrp, + smb_fname->flags); + TALLOC_FREE(mapped_name); + if (stream_name == NULL) { + DBG_ERR("synthetic_smb_fname failed\n"); + ok = false; + goto fail; + } + + DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name)); + + status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) + { + ok = false; + goto fail; + } + + status = SMB_VFS_CREATE_FILE( + handle->conn, /* conn */ + NULL, /* req */ + NULL, /* dirfsp */ + stream_name, /* fname */ + FILE_GENERIC_WRITE, /* access_mask */ + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN_IF, /* create_disposition */ + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* psbuf */ + NULL, NULL); /* create context */ + TALLOC_FREE(stream_name); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_CREATE_FILE failed\n"); + ok = false; + goto fail; + } + + nwritten = SMB_VFS_PWRITE(fsp, + ad->ad_data + e->adx_offset, + e->adx_length, + 0); + if (nwritten == -1) { + DBG_ERR("SMB_VFS_PWRITE failed\n"); + saved_errno = errno; + close_file_free(NULL, &fsp, ERROR_CLOSE); + errno = saved_errno; + ok = false; + goto fail; + } + + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + ok = false; + goto fail; + } + fsp = NULL; + } + + ad->adx_header.adx_num_attrs = 0; + TALLOC_FREE(ad->adx_entries); + + ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI); + + rc = ad_fset(handle, ad, ad->ad_fsp); + if (rc != 0) { + DBG_ERR("ad_fset on [%s] failed: %s\n", + fsp_str_dbg(ad->ad_fsp), strerror(errno)); + ok = false; + goto fail; + } + + ok = ad_convert_move_reso(handle, ad, smb_fname); + if (!ok) { + goto fail; + } + + *converted_xattr = true; + ok = true; + +fail: + return ok; +} + +static bool ad_convert_finderinfo(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + char *p_ad = NULL; + AfpInfo *ai = NULL; + DATA_BLOB aiblob; + struct smb_filename *stream_name = NULL; + files_struct *fsp = NULL; + size_t size; + ssize_t nwritten; + NTSTATUS status; + int saved_errno = 0; + int cmp; + + cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER); + if (cmp != 0) { + return true; + } + + p_ad = ad_get_entry(ad, ADEID_FINDERI); + if (p_ad == NULL) { + return false; + } + + ai = afpinfo_new(talloc_tos()); + if (ai == NULL) { + return false; + } + + memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI); + + aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE); + if (aiblob.data == NULL) { + TALLOC_FREE(ai); + return false; + } + + size = afpinfo_pack(ai, (char *)aiblob.data); + TALLOC_FREE(ai); + if (size != AFP_INFO_SIZE) { + return false; + } + + stream_name = synthetic_smb_fname(talloc_tos(), + smb_fname->base_name, + AFPINFO_STREAM, + NULL, + smb_fname->twrp, + smb_fname->flags); + if (stream_name == NULL) { + data_blob_free(&aiblob); + DBG_ERR("synthetic_smb_fname failed\n"); + return false; + } + + DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name)); + + status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) + { + return false; + } + + status = SMB_VFS_CREATE_FILE( + handle->conn, /* conn */ + NULL, /* req */ + NULL, /* dirfsp */ + stream_name, /* fname */ + FILE_GENERIC_WRITE, /* access_mask */ + FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */ + FILE_OPEN_IF, /* create_disposition */ + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* psbuf */ + NULL, NULL); /* create context */ + TALLOC_FREE(stream_name); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_CREATE_FILE failed\n"); + return false; + } + + nwritten = SMB_VFS_PWRITE(fsp, + aiblob.data, + aiblob.length, + 0); + if (nwritten == -1) { + DBG_ERR("SMB_VFS_PWRITE failed\n"); + saved_errno = errno; + close_file_free(NULL, &fsp, ERROR_CLOSE); + errno = saved_errno; + return false; + } + + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + fsp = NULL; + + return true; +} + +static bool ad_convert_truncate(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + int rc; + off_t newlen; + + newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK); + + rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen); + if (rc != 0) { + return false; + } + + return true; +} + +static bool ad_convert_blank_rfork(vfs_handle_struct *handle, + struct adouble *ad, + uint32_t flags, + bool *blank) +{ + size_t rforklen = sizeof(empty_resourcefork); + char buf[rforklen]; + ssize_t nread; + int cmp; + int rc; + + *blank = false; + + if (!(flags & AD_CONV_WIPE_BLANK)) { + return true; + } + + if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) { + return true; + } + + nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND); + if (nread != rforklen) { + DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n", + rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno)); + return false; + } + + cmp = memcmp(buf, empty_resourcefork, rforklen); + if (cmp != 0) { + return true; + } + + ad_setentrylen(ad, ADEID_RFORK, 0); + + rc = ad_fset(handle, ad, ad->ad_fsp); + if (rc != 0) { + DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp)); + return false; + } + + *blank = true; + return true; +} + +static bool ad_convert_delete_adfile(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname, + uint32_t flags) +{ + struct smb_filename *parent_fname = NULL; + struct smb_filename *at_fname = NULL; + struct smb_filename *ad_name = NULL; + NTSTATUS status; + int rc; + + if (ad_getentrylen(ad, ADEID_RFORK) > 0) { + return true; + } + + if (!(flags & AD_CONV_DELETE)) { + return true; + } + + rc = adouble_path(talloc_tos(), smb_fname, &ad_name); + if (rc != 0) { + return false; + } + + status = parent_pathref(talloc_tos(), + handle->conn->cwd_fsp, + ad_name, + &parent_fname, + &at_fname); + TALLOC_FREE(ad_name); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + rc = SMB_VFS_NEXT_UNLINKAT(handle, + parent_fname->fsp, + at_fname, + 0); + if (rc != 0) { + DBG_ERR("Unlinking [%s/%s] failed: %s\n", + smb_fname_str_dbg(parent_fname), + smb_fname_str_dbg(at_fname), strerror(errno)); + TALLOC_FREE(parent_fname); + return false; + } + + DBG_WARNING("Unlinked [%s/%s] after conversion\n", + smb_fname_str_dbg(parent_fname), + smb_fname_str_dbg(at_fname)); + TALLOC_FREE(parent_fname); + + return true; +} + +/** + * Convert from Apple's ._ file to Netatalk + * + * Apple's AppleDouble may contain a FinderInfo entry longer then 32 + * bytes containing packed xattrs. + * + * @return -1 in case an error occurred, 0 if no conversion was done, 1 + * otherwise + **/ +int ad_convert(struct vfs_handle_struct *handle, + const struct smb_filename *smb_fname, + const char *catia_mappings, + uint32_t flags) +{ + struct adouble *ad = NULL; + bool ok; + bool converted_xattr = false; + bool blank; + int ret; + + ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC); + if (ad == NULL) { + return 0; + } + + ok = ad_convert_xattr(handle, + ad, + smb_fname, + catia_mappings, + &converted_xattr); + if (!ok) { + ret = -1; + goto done; + } + + ok = ad_convert_blank_rfork(handle, ad, flags, &blank); + if (!ok) { + ret = -1; + goto done; + } + + if (converted_xattr || blank) { + ok = ad_convert_truncate(handle, ad, smb_fname); + if (!ok) { + ret = -1; + goto done; + } + } + + ok = ad_convert_finderinfo(handle, ad, smb_fname); + if (!ok) { + DBG_ERR("Failed to convert [%s]\n", + smb_fname_str_dbg(smb_fname)); + ret = -1; + goto done; + } + + ok = ad_convert_delete_adfile(handle, + ad, + smb_fname, + flags); + if (!ok) { + ret = -1; + goto done; + } + + ret = 0; +done: + TALLOC_FREE(ad); + return ret; +} + +static bool ad_unconvert_open_ad(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + struct smb_filename *smb_fname, + struct smb_filename *adpath, + files_struct **_fsp) +{ + files_struct *fsp = NULL; + NTSTATUS status; + int ret; + + status = openat_pathref_fsp(handle->conn->cwd_fsp, adpath); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + return false; + } + + status = SMB_VFS_CREATE_FILE( + handle->conn, + NULL, /* req */ + NULL, /* dirfsp */ + adpath, + FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN_IF, + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, + NULL, /* info */ + NULL, NULL); /* create context */ + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n", + smb_fname_str_dbg(adpath), nt_errstr(status)); + return false; + } + + if (fsp->fsp_name->st.st_ex_uid != smb_fname->st.st_ex_uid || + fsp->fsp_name->st.st_ex_gid != smb_fname->st.st_ex_gid) + { + ret = SMB_VFS_FCHOWN(fsp, + smb_fname->st.st_ex_uid, + smb_fname->st.st_ex_gid); + if (ret != 0) { + DBG_ERR("SMB_VFS_FCHOWN [%s] failed: %s\n", + fsp_str_dbg(fsp), nt_errstr(status)); + close_file_free(NULL, &fsp, NORMAL_CLOSE); + return false; + } + } + + *_fsp = fsp; + return true; +} + +static bool ad_unconvert_get_streams(struct vfs_handle_struct *handle, + struct smb_filename *smb_fname, + TALLOC_CTX *mem_ctx, + unsigned int *num_streams, + struct stream_struct **streams) +{ + files_struct *fsp = NULL; + NTSTATUS status; + + status = openat_pathref_fsp(handle->conn->cwd_fsp, smb_fname); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + status = SMB_VFS_CREATE_FILE( + handle->conn, /* conn */ + NULL, /* req */ + NULL, /* dirfsp */ + smb_fname, /* fname */ + FILE_READ_ATTRIBUTES, /* access_mask */ + (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */ + FILE_SHARE_DELETE), + FILE_OPEN, /* create_disposition*/ + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + NULL, NULL); /* create context */ + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Opening [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + return false; + } + + status = vfs_fstreaminfo(fsp, + mem_ctx, + num_streams, + streams); + if (!NT_STATUS_IS_OK(status)) { + close_file_free(NULL, &fsp, NORMAL_CLOSE); + DBG_ERR("streaminfo on [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + return false; + } + + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("close_file [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + return false; + } + + return true; +} + +struct ad_collect_state { + bool have_adfile; + size_t adx_data_off; + char *rsrc_data_buf; +}; + +static bool ad_collect_one_stream(struct vfs_handle_struct *handle, + struct char_mappings **cmaps, + struct smb_filename *smb_fname, + const struct stream_struct *stream, + struct adouble *ad, + struct ad_collect_state *state) +{ + struct smb_filename *sname = NULL; + files_struct *fsp = NULL; + struct ad_xattr_entry *e = NULL; + char *mapped_name = NULL; + char *p = NULL; + size_t needed_size; + ssize_t nread; + NTSTATUS status; + bool ok; + + sname = synthetic_smb_fname(ad, + smb_fname->base_name, + stream->name, + NULL, + smb_fname->twrp, + 0); + if (sname == NULL) { + return false; + } + + if (is_ntfs_default_stream_smb_fname(sname)) { + TALLOC_FREE(sname); + return true; + } + + DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname)); + + status = openat_pathref_fsp(handle->conn->cwd_fsp, sname); + if (!NT_STATUS_IS_OK(status)) { + ok = false; + goto out; + } + + status = SMB_VFS_CREATE_FILE( + handle->conn, + NULL, /* req */ + NULL, /* dirfsp */ + sname, + FILE_READ_DATA|DELETE_ACCESS, + FILE_SHARE_READ, + FILE_OPEN, + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, + NULL, /* info */ + NULL, NULL); /* create context */ + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n", + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + + if (is_afpinfo_stream(stream->name)) { + char buf[AFP_INFO_SIZE]; + + if (stream->size != AFP_INFO_SIZE) { + DBG_ERR("Bad size [%zd] on [%s]\n", + (ssize_t)stream->size, + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + + nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0); + if (nread != AFP_INFO_SIZE) { + DBG_ERR("Bad size [%zd] on [%s]\n", + (ssize_t)stream->size, + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + + memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND, + buf + AFP_OFF_FinderInfo, + AFP_FinderSize); + + ok = set_delete_on_close(fsp, + true, + fsp->conn->session_info->security_token, + fsp->conn->session_info->unix_token); + if (!ok) { + DBG_ERR("Deleting [%s] failed\n", + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + ok = true; + goto out; + } + + if (is_afpresource_stream(stream->name)) { + ad->ad_rsrc_data = talloc_size(ad, stream->size); + if (ad->ad_rsrc_data == NULL) { + ok = false; + goto out; + } + + nread = SMB_VFS_PREAD(fsp, + ad->ad_rsrc_data, + stream->size, + 0); + if (nread != stream->size) { + DBG_ERR("Bad size [%zd] on [%s]\n", + (ssize_t)stream->size, + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + + ad_setentrylen(ad, ADEID_RFORK, stream->size); + + if (!state->have_adfile) { + /* + * We have a resource *stream* but no AppleDouble + * sidecar file, this means the share is configured with + * fruit:resource=stream. So we should delete the + * resource stream. + */ + ok = set_delete_on_close( + fsp, + true, + fsp->conn->session_info->security_token, + fsp->conn->session_info->unix_token); + if (!ok) { + DBG_ERR("Deleting [%s] failed\n", + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + } + ok = true; + goto out; + } + + ad->adx_entries = talloc_realloc(ad, + ad->adx_entries, + struct ad_xattr_entry, + ad->adx_header.adx_num_attrs + 1); + if (ad->adx_entries == NULL) { + ok = false; + goto out; + } + + e = &ad->adx_entries[ad->adx_header.adx_num_attrs]; + *e = (struct ad_xattr_entry) { + .adx_length = stream->size, + }; + e->adx_name = talloc_strdup(ad, stream->name + 1); + if (e->adx_name == NULL) { + ok = false; + goto out; + } + p = strchr(e->adx_name, ':'); + if (p != NULL) { + *p = '\0'; + } + + status = string_replace_allocate(handle->conn, + e->adx_name, + cmaps, + ad, + &mapped_name, + vfs_translate_to_unix); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) + { + DBG_ERR("string_replace_allocate failed\n"); + ok = false; + goto out; + } + + e->adx_name = mapped_name; + e->adx_namelen = strlen(e->adx_name) + 1, + + DBG_DEBUG("%u: name (%s) size (%zu)\n", + ad->adx_header.adx_num_attrs, + e->adx_name, + (size_t)e->adx_length); + + ad->adx_header.adx_num_attrs++; + + needed_size = state->adx_data_off + stream->size; + if (needed_size > talloc_get_size(ad->adx_data)) { + ad->adx_data = talloc_realloc(ad, + ad->adx_data, + char, + needed_size); + if (ad->adx_data == NULL) { + ok = false; + goto out; + } + } + + nread = SMB_VFS_PREAD(fsp, + ad->adx_data + state->adx_data_off, + stream->size, + 0); + if (nread != stream->size) { + DBG_ERR("Bad size [%zd] on [%s]\n", + (ssize_t)stream->size, + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + state->adx_data_off += nread; + + ok = set_delete_on_close(fsp, + true, + fsp->conn->session_info->security_token, + fsp->conn->session_info->unix_token); + if (!ok) { + DBG_ERR("Deleting [%s] failed\n", + smb_fname_str_dbg(sname)); + ok = false; + goto out; + } + +out: + TALLOC_FREE(sname); + if (fsp != NULL) { + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("close_file [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + ok = false; + } + } + + return ok; +} + +/** + * Convert filesystem metadata to AppleDouble file + **/ +bool ad_unconvert(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + const char *catia_mappings, + struct smb_filename *smb_fname, + bool *converted) +{ + static struct char_mappings **cmaps = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + struct ad_collect_state state; + struct stream_struct *streams = NULL; + struct smb_filename *adpath = NULL; + struct adouble *ad = NULL; + unsigned int num_streams = 0; + size_t to_convert = 0; + bool have_rsrc = false; + files_struct *fsp = NULL; + size_t i; + NTSTATUS status; + int ret; + bool ok; + + *converted = false; + + if (cmaps == NULL) { + const char **mappings = NULL; + + mappings = str_list_make_v3_const( + frame, catia_mappings, NULL); + if (mappings == NULL) { + ok = false; + goto out; + } + cmaps = string_replace_init_map(mem_ctx, mappings); + TALLOC_FREE(mappings); + } + + ok = ad_unconvert_get_streams(handle, + smb_fname, + frame, + &num_streams, + &streams); + if (!ok) { + goto out; + } + + for (i = 0; i < num_streams; i++) { + if (strcasecmp_m(streams[i].name, "::$DATA") == 0) { + continue; + } + to_convert++; + if (is_afpresource_stream(streams[i].name)) { + have_rsrc = true; + } + } + + if (to_convert == 0) { + ok = true; + goto out; + } + + state = (struct ad_collect_state) { + .adx_data_off = 0, + }; + + ret = adouble_path(frame, smb_fname, &adpath); + if (ret != 0) { + ok = false; + goto out; + } + + ret = SMB_VFS_STAT(handle->conn, adpath); + if (ret == 0) { + state.have_adfile = true; + } else { + if (errno != ENOENT) { + ok = false; + goto out; + } + state.have_adfile = false; + } + + if (to_convert == 1 && have_rsrc && state.have_adfile) { + /* + * So we have just a single stream, the resource fork stream + * from an AppleDouble file. Fine, that means there's nothing to + * convert. + */ + ok = true; + goto out; + } + + ad = ad_init(frame, ADOUBLE_RSRC); + if (ad == NULL) { + ok = false; + goto out; + } + + for (i = 0; i < num_streams; i++) { + ok = ad_collect_one_stream(handle, + cmaps, + smb_fname, + &streams[i], + ad, + &state); + if (!ok) { + goto out; + } + } + + ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp); + if (!ok) { + DBG_ERR("Failed to open adfile [%s]\n", + smb_fname_str_dbg(smb_fname)); + goto out; + } + + ret = ad_fset(handle, ad, fsp); + if (ret != 0) { + ok = false; + goto out; + } + + *converted = true; + ok = true; + +out: + if (fsp != NULL) { + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("close_file_free() [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + ok = false; + } + } + TALLOC_FREE(frame); + return ok; +} + +/** + * Read and parse Netatalk AppleDouble metadata xattr + **/ +static ssize_t ad_read_meta(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + int rc = 0; + ssize_t ealen; + bool ok; + struct files_struct *fsp = smb_fname->fsp; + + DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name)); + + fsp = metadata_fsp(fsp); + + ealen = SMB_VFS_FGETXATTR(fsp, + AFPINFO_EA_NETATALK, + ad->ad_data, + AD_DATASZ_XATTR); + + if (ealen == -1) { + switch (errno) { + case ENOATTR: + case ENOENT: + if (errno == ENOATTR) { + errno = ENOENT; + } + rc = -1; + goto exit; + default: + DEBUG(2, ("error reading meta xattr: %s\n", + strerror(errno))); + rc = -1; + goto exit; + } + } + if (ealen != AD_DATASZ_XATTR) { + DEBUG(2, ("bad size %zd\n", ealen)); + errno = EINVAL; + rc = -1; + goto exit; + } + + /* Now parse entries */ + ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR); + if (!ok) { + DBG_WARNING( + "Invalid AppleDouble xattr metadata (%s) in file: %s - " + "Consider deleting the corrupted file.\n", + smb_fname->base_name, + ad->ad_fsp->fsp_name->base_name); + errno = EINVAL; + rc = -1; + goto exit; + } + + if (!ad_getentryoff(ad, ADEID_FINDERI) + || !ad_getentryoff(ad, ADEID_COMMENT) + || !ad_getentryoff(ad, ADEID_FILEDATESI) + || !ad_getentryoff(ad, ADEID_AFPFILEI) + || !ad_getentryoff(ad, ADEID_PRIVDEV) + || !ad_getentryoff(ad, ADEID_PRIVINO) + || !ad_getentryoff(ad, ADEID_PRIVSYN) + || !ad_getentryoff(ad, ADEID_PRIVID)) { + DEBUG(2, ("invalid AppleDouble metadata xattr\n")); + errno = EINVAL; + rc = -1; + goto exit; + } + +exit: + DEBUG(10, ("reading meta xattr for %s, rc: %d\n", + smb_fname->base_name, rc)); + + if (rc != 0) { + ealen = -1; + if (errno == EINVAL) { + become_root(); + (void)SMB_VFS_FREMOVEXATTR(fsp, + AFPINFO_EA_NETATALK); + unbecome_root(); + errno = ENOENT; + } + } + return ealen; +} + +static NTSTATUS adouble_open_rsrc_fsp(const struct files_struct *dirfsp, + const struct smb_filename *smb_base_fname, + int in_flags, + mode_t mode, + struct files_struct **_ad_fsp) +{ + int rc = 0; + struct adouble *ad = NULL; + struct smb_filename *adp_smb_fname = NULL; + struct files_struct *ad_fsp = NULL; + NTSTATUS status; + struct vfs_open_how how = { .flags = in_flags, .mode = mode, }; + + rc = adouble_path(talloc_tos(), + smb_base_fname, + &adp_smb_fname); + if (rc != 0) { + return NT_STATUS_NO_MEMORY; + } + + status = create_internal_fsp(dirfsp->conn, + adp_smb_fname, + &ad_fsp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + +#ifdef O_PATH + how.flags &= ~(O_PATH); +#endif + if (how.flags & (O_CREAT | O_TRUNC | O_WRONLY)) { + /* We always need read/write access for the metadata header too */ + how.flags &= ~(O_WRONLY); + how.flags |= O_RDWR; + } + + status = fd_openat(dirfsp, + adp_smb_fname, + ad_fsp, + &how); + if (!NT_STATUS_IS_OK(status)) { + file_free(NULL, ad_fsp); + return status; + } + + if (how.flags & (O_CREAT | O_TRUNC)) { + ad = ad_init(talloc_tos(), ADOUBLE_RSRC); + if (ad == NULL) { + file_free(NULL, ad_fsp); + return NT_STATUS_NO_MEMORY; + } + + rc = ad_fset(ad_fsp->conn->vfs_handles, ad, ad_fsp); + if (rc != 0) { + file_free(NULL, ad_fsp); + return NT_STATUS_IO_DEVICE_ERROR; + } + TALLOC_FREE(ad); + } + + *_ad_fsp = ad_fsp; + return NT_STATUS_OK; +} + +NTSTATUS adouble_open_from_base_fsp(const struct files_struct *dirfsp, + struct files_struct *base_fsp, + adouble_type_t type, + int flags, + mode_t mode, + struct files_struct **_ad_fsp) +{ + *_ad_fsp = NULL; + + SMB_ASSERT(base_fsp != NULL); + SMB_ASSERT(!fsp_is_alternate_stream(base_fsp)); + + switch (type) { + case ADOUBLE_META: + return NT_STATUS_INTERNAL_ERROR; + case ADOUBLE_RSRC: + return adouble_open_rsrc_fsp(dirfsp, + base_fsp->fsp_name, + flags, + mode, + _ad_fsp); + } + + return NT_STATUS_INTERNAL_ERROR; +} + +/* + * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue + * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd + * for file IO on the ._ file. + */ +static int ad_open(vfs_handle_struct *handle, + struct adouble *ad, + files_struct *fsp, + const struct smb_filename *smb_fname, + int flags, + mode_t mode) +{ + NTSTATUS status; + + DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name, + ad->ad_type == ADOUBLE_META ? "meta" : "rsrc"); + + if (ad->ad_type == ADOUBLE_META) { + return 0; + } + + if (fsp != NULL) { + ad->ad_fsp = fsp; + ad->ad_opened = false; + return 0; + } + + status = adouble_open_rsrc_fsp(handle->conn->cwd_fsp, + smb_fname, + flags, + mode, + &ad->ad_fsp); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + return -1; + } + ad->ad_opened = true; + + DBG_DEBUG("Path [%s] type [%s]\n", + smb_fname->base_name, + ad->ad_type == ADOUBLE_META ? "meta" : "rsrc"); + + return 0; +} + +static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + size_t to_read; + ssize_t len; + int ret; + bool ok; + + ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st); + if (ret != 0) { + DBG_ERR("fstat [%s] failed: %s\n", + fsp_str_dbg(ad->ad_fsp), strerror(errno)); + return -1; + } + + to_read = ad->ad_fsp->fsp_name->st.st_ex_size; + if (to_read > AD_XATTR_MAX_HDR_SIZE) { + to_read = AD_XATTR_MAX_HDR_SIZE; + } + + len = SMB_VFS_NEXT_PREAD(handle, + ad->ad_fsp, + ad->ad_data, + to_read, + 0); + if (len != to_read) { + DBG_NOTICE("%s %s: bad size: %zd\n", + smb_fname->base_name, strerror(errno), len); + return -1; + } + + /* Now parse entries */ + ok = ad_unpack(ad, + ADEID_NUM_DOT_UND, + ad->ad_fsp->fsp_name->st.st_ex_size); + if (!ok) { + DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - " + "Consider deleting the corrupted file.\n", + smb_fname->base_name, + ad->ad_fsp->fsp_name->base_name); + errno = EINVAL; + return -1; + } + + if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND) + || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI) + || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) + { + DBG_WARNING("Invalid AppleDouble resource (%s) in file: %s - " + "Consider deleting the corrupted file.\n", + smb_fname->base_name, + ad->ad_fsp->fsp_name->base_name); + errno = EINVAL; + return -1; + } + + return len; +} + +/** + * Read and parse resource fork, either ._ AppleDouble file or xattr + **/ +static ssize_t ad_read_rsrc(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + return ad_read_rsrc_adouble(handle, ad, smb_fname); +} + +/** + * Read and unpack an AppleDouble metadata xattr or resource + **/ +static ssize_t ad_read(vfs_handle_struct *handle, + struct adouble *ad, + const struct smb_filename *smb_fname) +{ + switch (ad->ad_type) { + case ADOUBLE_META: + return ad_read_meta(handle, ad, smb_fname); + case ADOUBLE_RSRC: + return ad_read_rsrc(handle, ad, smb_fname); + default: + return -1; + } +} + +static int adouble_destructor(struct adouble *ad) +{ + NTSTATUS status; + + if (!ad->ad_opened) { + return 0; + } + + SMB_ASSERT(ad->ad_fsp != NULL); + + status = fd_close(ad->ad_fsp); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Closing [%s] failed: %s\n", + fsp_str_dbg(ad->ad_fsp), nt_errstr(status)); + } + file_free(NULL, ad->ad_fsp); + ad->ad_fsp = NULL; + ad->ad_opened = false; + + return 0; +} + +/** + * Allocate a struct adouble without initialiing it + * + * The struct is either hang of the fsp extension context or if fsp is + * NULL from ctx. + * + * @param[in] ctx talloc context + * @param[in] handle vfs handle + * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC + * + * @return adouble handle + **/ +static struct adouble *ad_alloc(TALLOC_CTX *ctx, + adouble_type_t type) +{ + int rc = 0; + size_t adsize = 0; + struct adouble *ad; + + switch (type) { + case ADOUBLE_META: + adsize = AD_DATASZ_XATTR; + break; + case ADOUBLE_RSRC: + /* + * AppleDouble ._ file case, optimize for fewer (but larger) + * IOs. Two cases: + * + * - without xattrs size of the header is exactly + * AD_DATASZ_DOT_UND (82) bytes + * + * - with embedded xattrs it can be larger, up to + * AD_XATTR_MAX_HDR_SIZE + * + * Larger headers are not supported, but this is a reasonable + * limit that is also employed by the macOS client. + * + * We used the largest possible size to be able to read the full + * header with one IO. + */ + adsize = AD_XATTR_MAX_HDR_SIZE; + break; + default: + return NULL; + } + + ad = talloc_zero(ctx, struct adouble); + if (ad == NULL) { + rc = -1; + goto exit; + } + + if (adsize) { + ad->ad_data = talloc_zero_array(ad, char, adsize); + if (ad->ad_data == NULL) { + rc = -1; + goto exit; + } + } + + ad->ad_type = type; + ad->ad_magic = AD_MAGIC; + ad->ad_version = AD_VERSION; + + talloc_set_destructor(ad, adouble_destructor); + +exit: + if (rc != 0) { + TALLOC_FREE(ad); + } + return ad; +} + +/** + * Allocate and initialize a new struct adouble + * + * @param[in] ctx talloc context + * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC + * + * @return adouble handle, initialized + **/ +struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type) +{ + int rc = 0; + const struct ad_entry_order *eid; + struct adouble *ad = NULL; + time_t t = time(NULL); + + switch (type) { + case ADOUBLE_META: + eid = entry_order_meta_xattr; + break; + case ADOUBLE_RSRC: + eid = entry_order_dot_und; + break; + default: + return NULL; + } + + ad = ad_alloc(ctx, type); + if (ad == NULL) { + return NULL; + } + + while (eid->id) { + ad->ad_eid[eid->id].ade_off = eid->offset; + ad->ad_eid[eid->id].ade_len = eid->len; + eid++; + } + + /* put something sane in the date fields */ + ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t); + ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t); + ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t); + ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START)); + + if (rc != 0) { + TALLOC_FREE(ad); + } + return ad; +} + +static struct adouble *ad_get_internal(TALLOC_CTX *ctx, + vfs_handle_struct *handle, + files_struct *fsp, + const struct smb_filename *smb_fname, + adouble_type_t type) +{ + int rc = 0; + ssize_t len; + struct adouble *ad = NULL; + int mode; + + if (fsp != NULL) { + if (fsp_is_alternate_stream(fsp)) { + smb_fname = fsp->base_fsp->fsp_name; + } else { + smb_fname = fsp->fsp_name; + } + } + + DEBUG(10, ("ad_get(%s) called for %s\n", + type == ADOUBLE_META ? "meta" : "rsrc", + smb_fname != NULL ? smb_fname->base_name : "???")); + + ad = ad_alloc(ctx, type); + if (ad == NULL) { + rc = -1; + goto exit; + } + + /* Try rw first so we can use the fd in ad_convert() */ + mode = O_RDWR; + + rc = ad_open(handle, ad, fsp, smb_fname, mode, 0); + if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) { + mode = O_RDONLY; + rc = ad_open(handle, ad, fsp, smb_fname, mode, 0); + } + if (rc == -1) { + DBG_DEBUG("ad_open [%s] error [%s]\n", + smb_fname->base_name, strerror(errno)); + goto exit; + + } + + len = ad_read(handle, ad, smb_fname); + if (len == -1) { + DEBUG(10, ("error reading AppleDouble for %s\n", + smb_fname->base_name)); + rc = -1; + goto exit; + } + +exit: + DEBUG(10, ("ad_get(%s) for %s returning %d\n", + type == ADOUBLE_META ? "meta" : "rsrc", + smb_fname->base_name, rc)); + + if (rc != 0) { + TALLOC_FREE(ad); + } + return ad; +} + +/** + * Return AppleDouble data for a file + * + * @param[in] ctx talloc context + * @param[in] handle vfs handle + * @param[in] smb_fname pathname to file or directory + * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC + * + * @return talloced struct adouble or NULL on error + **/ +struct adouble *ad_get(TALLOC_CTX *ctx, + vfs_handle_struct *handle, + const struct smb_filename *smb_fname, + adouble_type_t type) +{ + return ad_get_internal(ctx, handle, NULL, smb_fname, type); +} + +/** + * Return AppleDouble data for a file + * + * @param[in] ctx talloc context + * @param[in] handle vfs handle + * @param[in] fsp fsp to use for IO + * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC + * + * @return talloced struct adouble or NULL on error + **/ +struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle, + files_struct *fsp, adouble_type_t type) +{ + return ad_get_internal(ctx, handle, fsp, NULL, type); +} + +/** + * Set AppleDouble metadata on a file or directory + * + * @param[in] ad adouble handle + * @param[in] fsp file handle + * + * @return status code, 0 means success + **/ +int ad_fset(struct vfs_handle_struct *handle, + struct adouble *ad, + files_struct *fsp) +{ + int rc = -1; + ssize_t len; + bool ok; + + DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp)); + + ok = ad_pack(handle, ad, fsp); + if (!ok) { + return -1; + } + + switch (ad->ad_type) { + case ADOUBLE_META: + rc = SMB_VFS_NEXT_FSETXATTR(handle, + fsp->base_fsp ? fsp->base_fsp : fsp, + AFPINFO_EA_NETATALK, + ad->ad_data, + AD_DATASZ_XATTR, 0); + break; + case ADOUBLE_RSRC: + len = SMB_VFS_NEXT_PWRITE(handle, + fsp, + ad->ad_data, + ad_getentryoff(ad, ADEID_RFORK), + 0); + if (len != ad_getentryoff(ad, ADEID_RFORK)) { + DBG_ERR("short write on %s: %zd\n", fsp_str_dbg(fsp), len); + return -1; + } + rc = 0; + break; + + default: + return -1; + } + + DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc); + + return rc; +} + +bool is_adouble_file(const char *path) +{ + const char *p = NULL; + int match; + + p = strrchr(path, '/'); + if (p == NULL) { + p = path; + } else { + p++; + } + + match = strncmp(p, + ADOUBLE_NAME_PREFIX, + strlen(ADOUBLE_NAME_PREFIX)); + if (match != 0) { + return false; + } + return true; +} + +/** + * Prepend "._" to a basename + * Return a new struct smb_filename with stream_name == NULL. + **/ +int adouble_path(TALLOC_CTX *ctx, + const struct smb_filename *smb_fname_in, + struct smb_filename **pp_smb_fname_out) +{ + char *parent; + const char *base; + struct smb_filename *smb_fname = NULL; + + smb_fname = cp_smb_filename_nostream(ctx, smb_fname_in); + if (smb_fname == NULL) { + return -1; + } + + /* We're replacing base_name. */ + TALLOC_FREE(smb_fname->base_name); + + SET_STAT_INVALID(smb_fname->st); + + if (!parent_dirname(smb_fname, smb_fname_in->base_name, + &parent, &base)) { + TALLOC_FREE(smb_fname); + return -1; + } + + if (ISDOT(parent)) { + smb_fname->base_name = talloc_asprintf(smb_fname, + "._%s", base); + } else { + smb_fname->base_name = talloc_asprintf(smb_fname, + "%s/._%s", parent, base); + } + if (smb_fname->base_name == NULL) { + TALLOC_FREE(smb_fname); + return -1; + } + + *pp_smb_fname_out = smb_fname; + + return 0; +} + +/** + * Allocate and initialize an AfpInfo struct + **/ +AfpInfo *afpinfo_new(TALLOC_CTX *ctx) +{ + AfpInfo *ai = talloc_zero(ctx, AfpInfo); + if (ai == NULL) { + return NULL; + } + ai->afpi_Signature = AFP_Signature; + ai->afpi_Version = AFP_Version; + ai->afpi_BackupTime = AD_DATE_START; + return ai; +} + +/** + * Pack an AfpInfo struct into a buffer + * + * Buffer size must be at least AFP_INFO_SIZE + * Returns size of packed buffer + **/ +ssize_t afpinfo_pack(const AfpInfo *ai, char *buf) +{ + memset(buf, 0, AFP_INFO_SIZE); + + RSIVAL(buf, 0, ai->afpi_Signature); + RSIVAL(buf, 4, ai->afpi_Version); + RSIVAL(buf, 12, ai->afpi_BackupTime); + memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo)); + + return AFP_INFO_SIZE; +} + +/** + * Unpack a buffer into a AfpInfo structure + * + * Buffer size must be at least AFP_INFO_SIZE + * Returns allocated AfpInfo struct + **/ +AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data, bool validate) +{ + AfpInfo *ai = talloc_zero(ctx, AfpInfo); + if (ai == NULL) { + return NULL; + } + + ai->afpi_Signature = RIVAL(data, 0); + ai->afpi_Version = RIVAL(data, 4); + ai->afpi_BackupTime = RIVAL(data, 12); + memcpy(ai->afpi_FinderInfo, (const char *)data + 16, + sizeof(ai->afpi_FinderInfo)); + + if (validate) { + if (ai->afpi_Signature != AFP_Signature + || ai->afpi_Version != AFP_Version) + { + DEBUG(1, ("Bad AfpInfo signature or version\n")); + TALLOC_FREE(ai); + } + } else { + ai->afpi_Signature = AFP_Signature; + ai->afpi_Version = AFP_Version; + } + + return ai; +} diff --git a/source3/lib/adouble.h b/source3/lib/adouble.h new file mode 100644 index 0000000..d2c7293 --- /dev/null +++ b/source3/lib/adouble.h @@ -0,0 +1,192 @@ +/* + * Samba AppleDouble helpers + * + * Copyright (C) Ralph Boehme, 2019 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _ADOUBLE_H_ +#define _ADOUBLE_H_ + +#include "MacExtensions.h" + +#define ADOUBLE_NAME_PREFIX "._" + +#define NETATALK_META_XATTR "org.netatalk.Metadata" +#define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork" + +#if defined(HAVE_ATTROPEN) +#define AFPINFO_EA_NETATALK NETATALK_META_XATTR +#define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR +#else +#define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR +#define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR +#endif + +/* + * There are two AppleDouble blobs we deal with: + * + * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing + * metadata in an xattr + * + * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in + * ._ files + */ +typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t; + +/* Version info */ +#define AD_VERSION2 0x00020000 +#define AD_VERSION AD_VERSION2 + +/* + * AppleDouble entry IDs. + */ +#define ADEID_DFORK 1 +#define ADEID_RFORK 2 +#define ADEID_NAME 3 +#define ADEID_COMMENT 4 +#define ADEID_ICONBW 5 +#define ADEID_ICONCOL 6 +#define ADEID_FILEI 7 +#define ADEID_FILEDATESI 8 +#define ADEID_FINDERI 9 +#define ADEID_MACFILEI 10 +#define ADEID_PRODOSFILEI 11 +#define ADEID_MSDOSFILEI 12 +#define ADEID_SHORTNAME 13 +#define ADEID_AFPFILEI 14 +#define ADEID_DID 15 + +/* Private Netatalk entries */ +#define ADEID_PRIVDEV 16 +#define ADEID_PRIVINO 17 +#define ADEID_PRIVSYN 18 +#define ADEID_PRIVID 19 +#define ADEID_MAX (ADEID_PRIVID + 1) + +/* + * These are the real ids for the private entries, + * as stored in the adouble file + */ +#define AD_DEV 0x80444556 +#define AD_INO 0x80494E4F +#define AD_SYN 0x8053594E +#define AD_ID 0x8053567E + +/* AppleDouble magic */ +#define AD_APPLESINGLE_MAGIC 0x00051600 +#define AD_APPLEDOUBLE_MAGIC 0x00051607 +#define AD_MAGIC AD_APPLEDOUBLE_MAGIC + +/* Field widths */ +#define ADEDLEN_NAME 255 +#define ADEDLEN_COMMENT 200 +#define ADEDLEN_FILEI 16 +#define ADEDLEN_FINDERI 32 +#define ADEDLEN_FILEDATESI 16 +#define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */ +#define ADEDLEN_AFPFILEI 4 +#define ADEDLEN_MACFILEI 4 +#define ADEDLEN_PRODOSFILEI 8 +#define ADEDLEN_MSDOSFILEI 2 +#define ADEDLEN_ICONBW 128 +#define ADEDLEN_ICONCOL 1024 +#define ADEDLEN_DID 4 +#define ADEDLEN_PRIVDEV 8 +#define ADEDLEN_PRIVINO 8 +#define ADEDLEN_PRIVSYN 8 +#define ADEDLEN_PRIVID 4 + +/* + * Sharemode locks fcntl() offsets + */ +#if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE) +#define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9) +#else +#define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9) +#endif +#define BYTELOCK_MAX (AD_FILELOCK_BASE - 1) + +#define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0) +#define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1) +#define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2) +#define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3) +#define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4) +#define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5) +#define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6) +#define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7) +#define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8) +#define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9) + +/* Time stuff we overload the bits a little */ +#define AD_DATE_CREATE 0 +#define AD_DATE_MODIFY 4 +#define AD_DATE_BACKUP 8 +#define AD_DATE_ACCESS 12 +#define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \ + AD_DATE_BACKUP | AD_DATE_ACCESS) +#define AD_DATE_UNIX (1 << 10) +#define AD_DATE_START 0x80000000 +#define AD_DATE_DELTA 946684800 +#define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA)) +#define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA) + +#define AD_CONV_WIPE_BLANK (1<<0) +#define AD_CONV_DELETE (1<<1) + +struct adouble; + +size_t ad_getentrylen(const struct adouble *ad, int eid); +size_t ad_getentryoff(const struct adouble *ad, int eid); +size_t ad_setentrylen(struct adouble *ad, int eid, size_t len); +size_t ad_setentryoff(struct adouble *ad, int eid, size_t off); +char *ad_get_entry(const struct adouble *ad, int eid); +int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date); +int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date); +int ad_convert(struct vfs_handle_struct *handle, + const struct smb_filename *smb_fname, + const char *catia_mappings, + uint32_t flags); +bool ad_unconvert(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + const char *catia_mappings, + struct smb_filename *smb_fname, + bool *converted); +struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type); +NTSTATUS adouble_open_from_base_fsp(const struct files_struct *dirfsp, + struct files_struct *base_fsp, + adouble_type_t type, + int flags, + mode_t mode, + struct files_struct **_ad_fsp); +struct adouble *ad_get(TALLOC_CTX *ctx, + vfs_handle_struct *handle, + const struct smb_filename *smb_fname, + adouble_type_t type); +struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle, + files_struct *fsp, adouble_type_t type); +int ad_fset(struct vfs_handle_struct *handle, + struct adouble *ad, + files_struct *fsp); +bool is_adouble_file(const char *path); +int adouble_path(TALLOC_CTX *ctx, + const struct smb_filename *smb_fname_in, + struct smb_filename **pp_smb_fname_out); + +AfpInfo *afpinfo_new(TALLOC_CTX *ctx); +ssize_t afpinfo_pack(const AfpInfo *ai, char *buf); +AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data, bool validate); + +#endif diff --git a/source3/lib/adt_tree.c b/source3/lib/adt_tree.c new file mode 100644 index 0000000..5c2b266 --- /dev/null +++ b/source3/lib/adt_tree.c @@ -0,0 +1,448 @@ +/* + * Unix SMB/CIFS implementation. + * Generic Abstract Data Types + * Copyright (C) Gerald Carter 2002. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include <talloc.h> +#include "lib/util/debug.h" +#include "lib/util/samba_util.h" +#include "util/charset/charset.h" +#include "source3/include/smb_macros.h" +#include "adt_tree.h" + +struct tree_node { + struct tree_node *parent; + struct tree_node **children; + int num_children; + char *key; + void *data_p; +}; + +struct sorted_tree { + struct tree_node *root; +}; + +/************************************************************************** + *************************************************************************/ + +static bool trim_tree_keypath( char *path, char **base, char **new_path ) +{ + char *p; + + *new_path = *base = NULL; + + if ( !path ) + return false; + + *base = path; + + p = strchr( path, '\\' ); + + if ( p ) { + *p = '\0'; + *new_path = p+1; + } + + return true; +} + +/************************************************************************** + Initialize the tree's root. + *************************************************************************/ + +struct sorted_tree *pathtree_init(void *data_p) +{ + struct sorted_tree *tree = NULL; + + tree = talloc_zero(NULL, struct sorted_tree); + if (tree == NULL) { + return NULL; + } + + tree->root = talloc_zero(tree, struct tree_node); + if (tree->root == NULL) { + TALLOC_FREE( tree ); + return NULL; + } + + tree->root->data_p = data_p; + + return tree; +} + + +/************************************************************************** + Find the next child given a key string + *************************************************************************/ + +static struct tree_node *pathtree_birth_child(struct tree_node *node, + char* key ) +{ + struct tree_node *infant = NULL; + struct tree_node **siblings; + int i; + + infant = talloc_zero(node, struct tree_node); + if (infant == NULL) { + return NULL; + } + + infant->key = talloc_strdup( infant, key ); + infant->parent = node; + + siblings = talloc_realloc(node, node->children, struct tree_node *, + node->num_children+1); + + if ( siblings ) + node->children = siblings; + + node->num_children++; + + /* first child */ + + if ( node->num_children == 1 ) { + DEBUG(11,("pathtree_birth_child: First child of node [%s]! [%s]\n", + node->key ? node->key : "NULL", infant->key )); + node->children[0] = infant; + } + else + { + /* + * multiple siblings .... (at least 2 children) + * + * work from the end of the list forward + * The last child is not set at this point + * Insert the new infanct in ascending order + * from left to right + */ + + for ( i = node->num_children-1; i>=1; i-- ) + { + DEBUG(11,("pathtree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n", + infant->key, node->children[i-1]->key)); + + /* the strings should never match assuming that we + have called pathtree_find_child() first */ + + if ( strcasecmp_m( infant->key, node->children[i-1]->key ) > 0 ) { + DEBUG(11,("pathtree_birth_child: storing infant in i == [%d]\n", + i)); + node->children[i] = infant; + break; + } + + /* bump everything towards the end on slot */ + + node->children[i] = node->children[i-1]; + } + + DEBUG(11,("pathtree_birth_child: Exiting loop (i == [%d])\n", i )); + + /* if we haven't found the correct slot yet, the child + will be first in the list */ + + if ( i == 0 ) + node->children[0] = infant; + } + + return infant; +} + +/************************************************************************** + Find the next child given a key string + *************************************************************************/ + +static struct tree_node *pathtree_find_child(struct tree_node *node, + char *key ) +{ + struct tree_node *next = NULL; + int i, result; + + if ( !node ) { + DEBUG(0,("pathtree_find_child: NULL node passed into function!\n")); + return NULL; + } + + if ( !key ) { + DEBUG(0,("pathtree_find_child: NULL key string passed into function!\n")); + return NULL; + } + + for ( i=0; i<node->num_children; i++ ) + { + DEBUG(11,("pathtree_find_child: child key => [%s]\n", + node->children[i]->key)); + + result = strcasecmp_m( node->children[i]->key, key ); + + if ( result == 0 ) + next = node->children[i]; + + /* if result > 0 then we've gone to far because + the list of children is sorted by key name + If result == 0, then we have a match */ + + if ( result > 0 ) + break; + } + + DEBUG(11,("pathtree_find_child: %s [%s]\n", + next ? "Found" : "Did not find", key )); + + return next; +} + +/************************************************************************** + Add a new node into the tree given a key path and a blob of data + *************************************************************************/ + +bool pathtree_add(struct sorted_tree *tree, const char *path, void *data_p) +{ + char *str, *base, *path2; + struct tree_node *current, *next; + bool ret = true; + + DEBUG(8,("pathtree_add: Enter\n")); + + if ( !path || *path != '\\' ) { + DEBUG(0,("pathtree_add: Attempt to add a node with a bad path [%s]\n", + path ? path : "NULL" )); + return false; + } + + if ( !tree ) { + DEBUG(0,("pathtree_add: Attempt to add a node to an uninitialized tree!\n")); + return false; + } + + /* move past the first '\\' */ + + path++; + path2 = SMB_STRDUP( path ); + if ( !path2 ) { + DEBUG(0,("pathtree_add: strdup() failed on string [%s]!?!?!\n", path)); + return false; + } + + + /* + * this works sort of like a 'mkdir -p' call, possibly + * creating an entire path to the new node at once + * The path should be of the form /<key1>/<key2>/... + */ + + base = path2; + str = path2; + current = tree->root; + + do { + /* break off the remaining part of the path */ + + str = strchr( str, '\\' ); + if ( str ) + *str = '\0'; + + /* iterate to the next child--birth it if necessary */ + + next = pathtree_find_child( current, base ); + if ( !next ) { + next = pathtree_birth_child( current, base ); + if ( !next ) { + DEBUG(0,("pathtree_add: Failed to create new child!\n")); + ret = false; + goto done; + } + } + current = next; + + /* setup the next part of the path */ + + base = str; + if ( base ) { + *base = '\\'; + base++; + str = base; + } + + } while ( base != NULL ); + + current->data_p = data_p; + + DEBUG(10,("pathtree_add: Successfully added node [%s] to tree\n", + path )); + + DEBUG(8,("pathtree_add: Exit\n")); + +done: + SAFE_FREE( path2 ); + return ret; +} + + +/************************************************************************** + Recursive routine to print out all children of a struct tree_node + *************************************************************************/ + +static void pathtree_print_children(TALLOC_CTX *ctx, + struct tree_node *node, + int debug, + const char *path ) +{ + int i; + int num_children; + char *path2 = NULL; + + if ( !node ) + return; + + if ( node->key ) + DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key, + node->data_p ? "data" : "NULL" )); + + if ( path ) { + path2 = talloc_strdup(ctx, path); + if (!path2) { + return; + } + } + + path2 = talloc_asprintf(ctx, + "%s%s/", + path ? path : "", + node->key ? node->key : "NULL"); + if (!path2) { + return; + } + + num_children = node->num_children; + for ( i=0; i<num_children; i++ ) { + pathtree_print_children(ctx, node->children[i], debug, path2 ); + } +} + +/************************************************************************** + Dump the kys for a tree to the log file + *************************************************************************/ + +void pathtree_print_keys(struct sorted_tree *tree, int debug ) +{ + int i; + int num_children = tree->root->num_children; + + if ( tree->root->key ) + DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key, + tree->root->data_p ? "data" : "NULL" )); + + for ( i=0; i<num_children; i++ ) { + TALLOC_CTX *ctx = talloc_stackframe(); + pathtree_print_children(ctx, tree->root->children[i], debug, + tree->root->key ? tree->root->key : "ROOT/" ); + TALLOC_FREE(ctx); + } + +} + +/************************************************************************** + return the data_p for for the node in tree matching the key string + The key string is the full path. We must break it apart and walk + the tree + *************************************************************************/ + +void* pathtree_find(struct sorted_tree *tree, char *key ) +{ + char *keystr, *base = NULL, *str = NULL, *p; + struct tree_node *current; + void *result = NULL; + + DEBUG(10,("pathtree_find: Enter [%s]\n", key ? key : "NULL" )); + + /* sanity checks first */ + + if ( !key ) { + DEBUG(0,("pathtree_find: Attempt to search tree using NULL search string!\n")); + return NULL; + } + + if ( !tree ) { + DEBUG(0,("pathtree_find: Attempt to search an uninitialized tree using string [%s]!\n", + key ? key : "NULL" )); + return NULL; + } + + if ( !tree->root ) + return NULL; + + /* make a copy to play with */ + + if ( *key == '\\' ) + keystr = SMB_STRDUP( key+1 ); + else + keystr = SMB_STRDUP( key ); + + if ( !keystr ) { + DEBUG(0,("pathtree_find: strdup() failed on string [%s]!?!?!\n", key)); + return NULL; + } + + /* start breaking the path apart */ + + p = keystr; + current = tree->root; + + if ( tree->root->data_p ) + result = tree->root->data_p; + + do + { + /* break off the remaining part of the path */ + + trim_tree_keypath( p, &base, &str ); + + DEBUG(11,("pathtree_find: [loop] base => [%s], new_path => [%s]\n", + base ? base : "", + str ? str : "")); + + /* iterate to the next child */ + + current = pathtree_find_child( current, base ); + + /* + * the idea is that the data_p for a parent should + * be inherited by all children, but allow it to be + * overridden farther down + */ + + if ( current && current->data_p ) + result = current->data_p; + + /* reset the path pointer 'p' to the remaining part of the key string */ + + p = str; + + } while ( str && current ); + + /* result should be the data_p from the lowest match node in the tree */ + if ( result ) + DEBUG(11,("pathtree_find: Found data_p!\n")); + + SAFE_FREE( keystr ); + + DEBUG(10,("pathtree_find: Exit\n")); + + return result; +} diff --git a/source3/lib/audit.c b/source3/lib/audit.c new file mode 100644 index 0000000..f0dc5c2 --- /dev/null +++ b/source3/lib/audit.c @@ -0,0 +1,149 @@ +/* + Unix SMB/CIFS implementation. + Auditing helper functions. + Copyright (C) Guenther Deschner 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../librpc/gen_ndr/lsa.h" + +static const struct audit_category_tab { + uint32_t category; + const char *category_str; + const char *param_str; + const char *description; +} audit_category_tab [] = { + { LSA_AUDIT_CATEGORY_LOGON, + "LSA_AUDIT_CATEGORY_LOGON", + "LOGON", "Logon events" }, + { LSA_AUDIT_CATEGORY_USE_OF_USER_RIGHTS, + "LSA_AUDIT_CATEGORY_USE_OF_USER_RIGHTS", + "PRIVILEGE", "Privilege Use" }, + { LSA_AUDIT_CATEGORY_SYSTEM, + "LSA_AUDIT_CATEGORY_SYSTEM", + "SYSTEM", "System Events" }, + { LSA_AUDIT_CATEGORY_SECURITY_POLICY_CHANGES, + "LSA_AUDIT_CATEGORY_SECURITY_POLICY_CHANGES", + "POLICY", "Policy Change" }, + { LSA_AUDIT_CATEGORY_PROCCESS_TRACKING, + "LSA_AUDIT_CATEGORY_PROCCESS_TRACKING", + "PROCESS", "Process Tracking" }, + { LSA_AUDIT_CATEGORY_FILE_AND_OBJECT_ACCESS, + "LSA_AUDIT_CATEGORY_FILE_AND_OBJECT_ACCESS", + "OBJECT", "Object Access" }, + { LSA_AUDIT_CATEGORY_ACCOUNT_MANAGEMENT, + "LSA_AUDIT_CATEGORY_ACCOUNT_MANAGEMENT", + "SAM", "Account Management" }, + { LSA_AUDIT_CATEGORY_DIRECTORY_SERVICE_ACCESS, + "LSA_AUDIT_CATEGORY_DIRECTORY_SERVICE_ACCESS", + "DIRECTORY", "Directory service access" }, + { LSA_AUDIT_CATEGORY_ACCOUNT_LOGON, + "LSA_AUDIT_CATEGORY_ACCOUNT_LOGON", + "ACCOUNT", "Account logon events" }, + { .category = 0 } +}; + +const char *audit_category_str(uint32_t category) +{ + int i; + for (i=0; audit_category_tab[i].category_str; i++) { + if (category == audit_category_tab[i].category) { + return audit_category_tab[i].category_str; + } + } + return NULL; +} + +const char *audit_param_str(uint32_t category) +{ + int i; + for (i=0; audit_category_tab[i].param_str; i++) { + if (category == audit_category_tab[i].category) { + return audit_category_tab[i].param_str; + } + } + return NULL; +} + +const char *audit_description_str(uint32_t category) +{ + int i; + for (i=0; audit_category_tab[i].description; i++) { + if (category == audit_category_tab[i].category) { + return audit_category_tab[i].description; + } + } + return NULL; +} + +bool get_audit_category_from_param(const char *param, uint32_t *audit_category) +{ + *audit_category = Undefined; + + if (strequal(param, "SYSTEM")) { + *audit_category = LSA_AUDIT_CATEGORY_SYSTEM; + } else if (strequal(param, "LOGON")) { + *audit_category = LSA_AUDIT_CATEGORY_LOGON; + } else if (strequal(param, "OBJECT")) { + *audit_category = LSA_AUDIT_CATEGORY_FILE_AND_OBJECT_ACCESS; + } else if (strequal(param, "PRIVILEGE")) { + *audit_category = LSA_AUDIT_CATEGORY_USE_OF_USER_RIGHTS; + } else if (strequal(param, "PROCESS")) { + *audit_category = LSA_AUDIT_CATEGORY_PROCCESS_TRACKING; + } else if (strequal(param, "POLICY")) { + *audit_category = LSA_AUDIT_CATEGORY_SECURITY_POLICY_CHANGES; + } else if (strequal(param, "SAM")) { + *audit_category = LSA_AUDIT_CATEGORY_ACCOUNT_MANAGEMENT; + } else if (strequal(param, "DIRECTORY")) { + *audit_category = LSA_AUDIT_CATEGORY_DIRECTORY_SERVICE_ACCESS; + } else if (strequal(param, "ACCOUNT")) { + *audit_category = LSA_AUDIT_CATEGORY_ACCOUNT_LOGON; + } else { + DEBUG(0,("unknown parameter: %s\n", param)); + return False; + } + + return True; +} + +const char *audit_policy_str(TALLOC_CTX *mem_ctx, uint32_t policy) +{ + const char *ret = NULL; + + if (policy == LSA_AUDIT_POLICY_NONE) { + return talloc_strdup(mem_ctx, "None"); + } + + if (policy & LSA_AUDIT_POLICY_SUCCESS) { + ret = talloc_strdup(mem_ctx, "Success"); + if (ret == NULL) { + return NULL; + } + } + + if (policy & LSA_AUDIT_POLICY_FAILURE) { + if (ret) { + ret = talloc_asprintf(mem_ctx, "%s, %s", ret, "Failure"); + if (ret == NULL) { + return NULL; + } + } else { + return talloc_strdup(mem_ctx, "Failure"); + } + } + + return ret; +} diff --git a/source3/lib/avahi.c b/source3/lib/avahi.c new file mode 100644 index 0000000..269b329 --- /dev/null +++ b/source3/lib/avahi.c @@ -0,0 +1,275 @@ +/* + Unix SMB/CIFS implementation. + Connect avahi to lib/tevents + Copyright (C) Volker Lendecke 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#include <avahi-common/watch.h> + +struct avahi_poll_context { + struct tevent_context *ev; + AvahiWatch **watches; + AvahiTimeout **timeouts; +}; + +struct AvahiWatch { + struct avahi_poll_context *ctx; + struct tevent_fd *fde; + int fd; + AvahiWatchEvent latest_event; + AvahiWatchCallback callback; + void *userdata; +}; + +struct AvahiTimeout { + struct avahi_poll_context *ctx; + struct tevent_timer *te; + AvahiTimeoutCallback callback; + void *userdata; +}; + +static uint16_t avahi_flags_map_to_tevent(AvahiWatchEvent event) +{ + return ((event & AVAHI_WATCH_IN) ? TEVENT_FD_READ : 0) + | ((event & AVAHI_WATCH_OUT) ? TEVENT_FD_WRITE : 0); +} + +static void avahi_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data); + +static AvahiWatch *avahi_watch_new(const AvahiPoll *api, int fd, + AvahiWatchEvent event, + AvahiWatchCallback callback, + void *userdata) +{ + struct avahi_poll_context *ctx = talloc_get_type_abort( + api->userdata, struct avahi_poll_context); + int num_watches = talloc_array_length(ctx->watches); + AvahiWatch **tmp, *watch_ctx; + + tmp = talloc_realloc(ctx, ctx->watches, AvahiWatch *, num_watches + 1); + if (tmp == NULL) { + return NULL; + } + ctx->watches = tmp; + + watch_ctx = talloc(tmp, AvahiWatch); + if (watch_ctx == NULL) { + goto fail; + } + ctx->watches[num_watches] = watch_ctx; + + watch_ctx->ctx = ctx; + watch_ctx->fde = tevent_add_fd(ctx->ev, watch_ctx, fd, + avahi_flags_map_to_tevent(event), + avahi_fd_handler, watch_ctx); + if (watch_ctx->fde == NULL) { + goto fail; + } + watch_ctx->callback = callback; + watch_ctx->userdata = userdata; + return watch_ctx; + + fail: + TALLOC_FREE(watch_ctx); + ctx->watches = talloc_realloc(ctx, ctx->watches, AvahiWatch *, + num_watches); + return NULL; +} + +static void avahi_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, uint16_t flags, + void *private_data) +{ + AvahiWatch *watch_ctx = talloc_get_type_abort(private_data, AvahiWatch); + + watch_ctx->latest_event = + ((flags & TEVENT_FD_READ) ? AVAHI_WATCH_IN : 0) + | ((flags & TEVENT_FD_WRITE) ? AVAHI_WATCH_OUT : 0); + + watch_ctx->callback(watch_ctx, watch_ctx->fd, watch_ctx->latest_event, + watch_ctx->userdata); +} + +static void avahi_watch_update(AvahiWatch *w, AvahiWatchEvent event) +{ + tevent_fd_set_flags(w->fde, avahi_flags_map_to_tevent(event)); +} + +static AvahiWatchEvent avahi_watch_get_events(AvahiWatch *w) +{ + return w->latest_event; +} + +static void avahi_watch_free(AvahiWatch *w) +{ + int i, num_watches; + AvahiWatch **watches = w->ctx->watches; + struct avahi_poll_context *ctx; + + num_watches = talloc_array_length(watches); + + for (i=0; i<num_watches; i++) { + if (w == watches[i]) { + break; + } + } + if (i == num_watches) { + return; + } + ctx = w->ctx; + TALLOC_FREE(w); + memmove(&watches[i], &watches[i+1], + sizeof(*watches) * (num_watches - i - 1)); + ctx->watches = talloc_realloc(ctx, watches, AvahiWatch *, + num_watches - 1); +} + +static void avahi_timeout_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data); + +static AvahiTimeout *avahi_timeout_new(const AvahiPoll *api, + const struct timeval *tv, + AvahiTimeoutCallback callback, + void *userdata) +{ + struct avahi_poll_context *ctx = talloc_get_type_abort( + api->userdata, struct avahi_poll_context); + int num_timeouts = talloc_array_length(ctx->timeouts); + AvahiTimeout **tmp, *timeout_ctx; + + tmp = talloc_realloc(ctx, ctx->timeouts, AvahiTimeout *, + num_timeouts + 1); + if (tmp == NULL) { + return NULL; + } + ctx->timeouts = tmp; + + timeout_ctx = talloc(tmp, AvahiTimeout); + if (timeout_ctx == NULL) { + goto fail; + } + ctx->timeouts[num_timeouts] = timeout_ctx; + + timeout_ctx->ctx = ctx; + if (tv == NULL) { + timeout_ctx->te = NULL; + } else { + timeout_ctx->te = tevent_add_timer(ctx->ev, timeout_ctx, + *tv, avahi_timeout_handler, + timeout_ctx); + if (timeout_ctx->te == NULL) { + goto fail; + } + } + timeout_ctx->callback = callback; + timeout_ctx->userdata = userdata; + return timeout_ctx; + + fail: + TALLOC_FREE(timeout_ctx); + ctx->timeouts = talloc_realloc(ctx, ctx->timeouts, AvahiTimeout *, + num_timeouts); + return NULL; +} + +static void avahi_timeout_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + AvahiTimeout *timeout_ctx = talloc_get_type_abort( + private_data, AvahiTimeout); + + TALLOC_FREE(timeout_ctx->te); + timeout_ctx->callback(timeout_ctx, timeout_ctx->userdata); +} + +static void avahi_timeout_update(AvahiTimeout *t, const struct timeval *tv) +{ + TALLOC_FREE(t->te); + + if (tv == NULL) { + /* + * Disable this timer + */ + return; + } + + t->te = tevent_add_timer(t->ctx->ev, t, *tv, avahi_timeout_handler, t); + /* + * No failure mode defined here + */ + SMB_ASSERT(t->te != NULL); +} + +static void avahi_timeout_free(AvahiTimeout *t) +{ + int i, num_timeouts; + AvahiTimeout **timeouts = t->ctx->timeouts; + struct avahi_poll_context *ctx; + + num_timeouts = talloc_array_length(timeouts); + + for (i=0; i<num_timeouts; i++) { + if (t == timeouts[i]) { + break; + } + } + if (i == num_timeouts) { + return; + } + ctx = t->ctx; + TALLOC_FREE(t); + memmove(&timeouts[i], &timeouts[i+1], + sizeof(*timeouts) * (num_timeouts - i - 1)); + ctx->timeouts = talloc_realloc(ctx, timeouts, AvahiTimeout *, + num_timeouts - 1); +} + +struct AvahiPoll *tevent_avahi_poll(TALLOC_CTX *mem_ctx, + struct tevent_context *ev) +{ + struct AvahiPoll *result; + struct avahi_poll_context *ctx; + + result = talloc(mem_ctx, struct AvahiPoll); + if (result == NULL) { + return result; + } + ctx = talloc_zero(result, struct avahi_poll_context); + if (ctx == NULL) { + TALLOC_FREE(result); + return NULL; + } + ctx->ev = ev; + + result->watch_new = avahi_watch_new; + result->watch_update = avahi_watch_update; + result->watch_get_events = avahi_watch_get_events; + result->watch_free = avahi_watch_free; + result->timeout_new = avahi_timeout_new; + result->timeout_update = avahi_timeout_update; + result->timeout_free = avahi_timeout_free; + result->userdata = ctx; + + return result; +} diff --git a/source3/lib/background.c b/source3/lib/background.c new file mode 100644 index 0000000..cf6cc3a --- /dev/null +++ b/source3/lib/background.c @@ -0,0 +1,256 @@ +/* + Unix SMB/CIFS implementation. + Regular background jobs as forked helpers + Copyright (C) Volker Lendecke 2012 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/async_req/async_sock.h" +#include "include/messages.h" +#include "background.h" + +struct background_job_state { + struct tevent_context *ev; + struct messaging_context *msg; + uint32_t *trigger_msgs; + size_t num_trigger_msgs; + bool parent_longlived; + int (*fn)(void *private_data); + void *private_data; + + struct tevent_req *wakeup_req; + int pipe_fd; + struct tevent_req *pipe_req; +}; + +static int background_job_state_destructor(struct background_job_state *s); +static void background_job_waited(struct tevent_req *subreq); +static void background_job_done(struct tevent_req *subreq); +static bool background_job_trigger( + struct messaging_rec *rec, void *private_data); + +struct tevent_req *background_job_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context *msg, + uint32_t *trigger_msgs, + size_t num_trigger_msgs, + time_t initial_wait_sec, + int (*fn)(void *private_data), + void *private_data) +{ + struct tevent_req *req, *subreq; + struct background_job_state *state; + size_t i; + + req = tevent_req_create(mem_ctx, &state, + struct background_job_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->msg = msg; + + if (num_trigger_msgs != 0) { + state->trigger_msgs = (uint32_t *)talloc_memdup( + state, trigger_msgs, + sizeof(uint32_t) * num_trigger_msgs); + if (tevent_req_nomem(state->trigger_msgs, req)) { + return tevent_req_post(req, ev); + } + state->num_trigger_msgs = num_trigger_msgs; + } + + state->fn = fn; + state->private_data = private_data; + + state->pipe_fd = -1; + talloc_set_destructor(state, background_job_state_destructor); + + for (i=0; i<num_trigger_msgs; i++) { + subreq = messaging_filtered_read_send( + state, ev, msg, background_job_trigger, state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } + + subreq = tevent_wakeup_send( + state, state->ev, timeval_current_ofs(initial_wait_sec, 0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, background_job_waited, req); + state->wakeup_req = subreq; + return req; +} + +static int background_job_state_destructor(struct background_job_state *state) +{ + TALLOC_FREE(state->pipe_req); + if (state->pipe_fd != -1) { + close(state->pipe_fd); + state->pipe_fd = -1; + } + + return 0; +} + +static bool background_job_trigger( + struct messaging_rec *rec, void *private_data) +{ + struct background_job_state *state = talloc_get_type_abort( + private_data, struct background_job_state); + size_t i; + + if (state->wakeup_req == NULL) { + return false; + } + for (i=0; i<state->num_trigger_msgs; i++) { + if (rec->msg_type == state->trigger_msgs[i]) { + break; + } + } + if (i == state->num_trigger_msgs) { + return false; + } + if (!tevent_req_set_endtime(state->wakeup_req, state->ev, + timeval_zero())) { + DEBUG(10, ("tevent_req_set_endtime failed\n")); + } + return false; +} + +static void background_job_waited(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct background_job_state *state = tevent_req_data( + req, struct background_job_state); + int fds[2]; + int res; + bool ret; + + ret = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + state->wakeup_req = NULL; + if (!ret) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + res = pipe(fds); + if (res == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return; + } + + res = fork(); + if (res == -1) { + int err = errno; + close(fds[0]); + close(fds[1]); + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + + if (res == 0) { + /* child */ + + NTSTATUS status; + ssize_t written; + + close(fds[0]); + + status = reinit_after_fork(state->msg, state->ev, true); + if (NT_STATUS_IS_OK(status)) { + res = state->fn(state->private_data); + } else { + res = -1; + } + written = write(fds[1], &res, sizeof(res)); + if (written == -1) { + _exit(1); + } + + /* + * No TALLOC_FREE here, messaging_parent_dgm_cleanup_init for + * example calls background_job_send with "messaging_context" + * as talloc parent. Thus "state" will be freed with the + * following talloc_free will have removed "state" when it + * returns. TALLOC_FREE will then write a NULL into free'ed + * memory. talloc_free() is required although we immediately + * exit, the messaging_context's destructor will want to clean + * up. + */ + talloc_free(state->msg); + _exit(0); + } + + /* parent */ + + close(fds[1]); + state->pipe_fd = fds[0]; + + subreq = read_packet_send(state, state->ev, state->pipe_fd, + sizeof(int), NULL, NULL); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, background_job_done, req); + state->pipe_req = subreq; +} + +static void background_job_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct background_job_state *state = tevent_req_data( + req, struct background_job_state); + ssize_t ret; + uint8_t *buf; + int err; + int wait_secs; + + state->pipe_req = NULL; + + ret = read_packet_recv(subreq, talloc_tos(), &buf, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + close(state->pipe_fd); + state->pipe_fd = -1; + memcpy(&wait_secs, buf, sizeof(wait_secs)); + if (wait_secs == -1) { + tevent_req_done(req); + return; + } + subreq = tevent_wakeup_send( + state, state->ev, timeval_current_ofs(wait_secs, 0)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, background_job_waited, req); + state->wakeup_req = subreq; +} + +NTSTATUS background_job_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} diff --git a/source3/lib/background.h b/source3/lib/background.h new file mode 100644 index 0000000..8c8fe22 --- /dev/null +++ b/source3/lib/background.h @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Regular background jobs as forked helpers + Copyright (C) Volker Lendecke 2012 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIB_BACKGROUND_H_ +#define _LIB_BACKGROUND_H_ + +#include "replace.h" +#include <tevent.h> +#include "libcli/util/ntstatus.h" + +struct messaging_context; + +/* + * From a parent process regularly fork a process and execute fn(). fn() + * returns the number of seconds to wait before it is run next time. Returning + * -1 means stop the job. + */ + +struct tevent_req *background_job_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context *msg, + uint32_t *trigger_msgs, + size_t num_trigger_msgs, + time_t initial_wait_sec, + int (*fn)(void *private_data), + void *private_data); +NTSTATUS background_job_recv(struct tevent_req *req); + +#endif diff --git a/source3/lib/cbuf.c b/source3/lib/cbuf.c new file mode 100644 index 0000000..de0518d --- /dev/null +++ b/source3/lib/cbuf.c @@ -0,0 +1,328 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2010 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @file cbuf.c + * @author Gregor Beck <gb@sernet.de> + * @date Aug 2010 + * + * @brief A talloced character buffer. + * + */ + + +#include "replace.h" +#include "system/locale.h" +#include "cbuf.h" +#include <talloc.h> +#include <assert.h> +#include "lib/util/byteorder.h" + + +struct cbuf { + char* buf; + size_t pos; + size_t size; +}; + + +cbuf* cbuf_clear(cbuf* b) +{ + cbuf_setpos(b, 0); + return b; +} + +cbuf* cbuf_new(const void* ctx) +{ + cbuf* s = talloc(ctx, cbuf); + if (s == NULL) + return NULL; + s->size = 32; + s->buf = (char *)talloc_size(s, s->size); + if (s->size && (s->buf == NULL)) { + talloc_free(s); + return NULL; + } + return cbuf_clear(s); +} + +cbuf* cbuf_copy(const cbuf* b) +{ + cbuf* s = talloc(talloc_parent(b), cbuf); + if (s == NULL) { + return NULL; + } + + s->buf = (char *)talloc_memdup(s, b->buf, b->size); /* only up to pos? */ + + /* XXX shallow did not work, because realloc */ + /* fails with multiple references */ + /* s->buf = talloc_reference(s, b->buf); */ + + if (s->buf == NULL) { + cbuf_delete(s); + return NULL; + } + s->size = b->size; + s->pos = b->pos; + return s; +} + +void cbuf_delete(cbuf* b) +{ + talloc_free(b); +} + +#define SWAP(A,B,T) do { \ + T tmp = A; A = B; B = tmp; \ + } while(0) + + +void cbuf_swap(cbuf* b1, cbuf* b2) +{ + if (b1 == b2) { + return; + } + talloc_reparent(b1, b2, b1->buf); + talloc_reparent(b2, b1, b2->buf); + SWAP(b1->buf, b2->buf, char*); + SWAP(b1->pos, b2->pos, size_t); + SWAP(b1->size, b2->size, size_t); +} + + + +cbuf* cbuf_takeover(cbuf* b1, cbuf* b2) +{ + talloc_reparent(b2, b1, b2->buf); + b1->buf = b2->buf; + b1->pos = b2->pos; + b1->size = b2->size; + cbuf_delete(b2); + return b1; +} + +cbuf* cbuf_swapptr(cbuf* b, char** ptr, size_t len) +{ + void* p = talloc_parent(*ptr); + SWAP(b->buf, *ptr, char*); + talloc_steal(b, b->buf); + talloc_steal(p, *ptr); + b->size = talloc_get_size(b->buf); + b->pos = (len == -1) ? strlen(b->buf) : len; + + assert(b->pos <= b->size); + return b; +} + +cbuf* cbuf_resize(cbuf* b, size_t size) +{ + char* save_buf = b->buf; + b->buf = talloc_realloc(b, b->buf, char, size); + if (b->buf == NULL) { + talloc_free(save_buf); + b->size = 0; + } else { + b->size = size; + } + b->pos = MIN(b->pos, b->size); + return b->buf ? b : NULL; +} + +char* cbuf_reserve(cbuf* b, size_t len) +{ + if(b->size < b->pos + len) + cbuf_resize(b, MAX(2*b->size, b->pos + len)); + return b->buf ? b->buf + b->pos : NULL; +} + +int cbuf_puts(cbuf* b, const char* str, size_t len) +{ + char* dst; + + if (b == NULL) + return 0; + + if (len == -1) { + len=strlen(str); + } + + dst = cbuf_reserve(b, len+1); + if (dst == NULL) + return -1; + + memcpy(dst, str, len); + dst[len] = '\0'; /* just to ease debugging */ + + b->pos += len; + assert(b->pos < b->size); + + return len; +} + +int cbuf_putc(cbuf* b, char c) { + char* dst; + + if (b == NULL) + return 0; + + dst = cbuf_reserve(b, 2); + if (dst == NULL) { + return -1; + } + + dst[0] = c; + dst[1] = '\0'; /* just to ease debugging */ + + b->pos++; + assert(b->pos < b->size); + + return 1; +} + +int cbuf_putdw(cbuf* b, uint32_t u) { + char* dst; + static const size_t LEN = sizeof(uint32_t); + + if (b == NULL) + return 0; + + dst = cbuf_reserve(b, LEN); + if (dst == NULL) { + return -1; + } + + SIVAL(dst, 0, u); + + b->pos += LEN; + assert(b->pos <= b->size); /* no NULL termination*/ + + return LEN; +} + +size_t cbuf_getpos(const cbuf* b) { + assert(b->pos <= b->size); + return b->pos; +} + +void cbuf_setpos(cbuf* b, size_t pos) { + assert(pos <= b->size); + b->pos = pos; + if (pos < b->size) + b->buf[pos] = '\0'; /* just to ease debugging */ +} + +char* cbuf_gets(cbuf* b, size_t idx) { + assert(idx <= b->pos); + + if (cbuf_reserve(b, 1) == NULL) + return NULL; + + b->buf[b->pos] = '\0'; + return b->buf + idx; +} + +int cbuf_printf(cbuf* b, const char* fmt, ...) +{ + va_list args, args2; + int len; + char* dst = b->buf + b->pos; + const int avail = b->size - b->pos; + assert(avail >= 0); + + va_start(args, fmt); + va_copy(args2, args); + + len = vsnprintf(dst, avail, fmt, args); + + if (len >= avail) { + dst = cbuf_reserve(b, len+1); + len = (dst != NULL) ? vsnprintf(dst, len+1, fmt, args2) : -1; + } + + if (len > 0) { + b->pos += len; + } + + va_end(args); + va_end(args2); + assert(b->pos <= b->size); + + return len; +} + +int cbuf_print_quoted_string(cbuf* ost, const char* s) +{ + int n = 1; + cbuf_putc(ost,'"'); + + while(true) { + switch (*s) { + case '\0': + cbuf_putc(ost, '"'); + return n+1; + + case '"': + case '\\': + cbuf_putc(ost, '\\'); + n++; + + FALL_THROUGH; + default: + cbuf_putc(ost, *s); + n++; + } + s++; + } +} + + +int cbuf_print_quoted(cbuf* ost, const char* s, size_t len) +{ + int n = 1; + int ret; + cbuf_reserve(ost, len+2); + + cbuf_putc(ost,'"'); + + while(len--) { + switch (*s) { + case '"': + case '\\': + ret = cbuf_printf(ost, "\\%c", *s); + break; + default: + if (isprint(*s) && ((*s == ' ') || !isspace(*s))) { + ret = cbuf_putc(ost, *s); + } else { + ret = cbuf_printf(ost, + "\\%02x", + (unsigned char)*s); + } + } + s++; + if (ret == -1) { + return -1; + } + n += ret; + } + ret = cbuf_putc(ost,'"'); + + return (ret == -1) ? -1 : (n + ret); +} diff --git a/source3/lib/cbuf.h b/source3/lib/cbuf.h new file mode 100644 index 0000000..d4f5a72 --- /dev/null +++ b/source3/lib/cbuf.h @@ -0,0 +1,245 @@ +/* + * Samba Unix/Linux SMB client library + * Copyright (C) Gregor Beck 2010 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @file cbuf.h + * @author Gregor Beck <gb@sernet.de> + * @date Aug 2010 + * + * @brief A talloced character buffer. + * + * A cbuf carries a write position and keeps track of its size. + */ + +#ifndef __CBUF_H +#define __CBUF_H + +struct cbuf; +typedef struct cbuf cbuf; + +/** + * Create a new character buffer. + * + * @param talloc_ctx the talloc parent + * + * @return a new cbuf object, NULL on error + */ +cbuf* cbuf_new(const void* talloc_ctx); + +/** + * Create a copy of a character buffer. + * + * @param b the cbuf to copy + * @return a new cbuf object, NULL on error + */ +cbuf* cbuf_copy(const cbuf* b); + +/** + * Delete a character buffer. + * This invalidates b and free's the memory allocated. + * @warning don't talloc_free b directly, however freeing + * the parent works as expected + * @param b the cbuf to delete + */ +void cbuf_delete(cbuf* b); + +/** + * Reset the buffer to initial state. + * Set the write position to the start of buffer, effectivly + * clearing its contents. Doesn't free memory. + * + * @param b the buffer to clear + * + * @return b + */ +cbuf* cbuf_clear(cbuf* b); + +/** + * Swap the contents of two buffers in O(1). + * + * @param b1 a character buffer + * @param b2 another character buffer + */ +void cbuf_swap(cbuf* b1, cbuf* b2); + +/** + * Swap the contents of a buffer with a talloced string. + * + * @param b a character buffer + * @param ptr a pointer to a talloced string + * @param len size of string, -1 means strlen(*ptr) + * + * @return b + */ +cbuf* cbuf_swapptr(cbuf* b, char** ptr, size_t len); + +/** + * Let a character buffer takeover the contents of another. + * This is equivalent to @code + * cbuf_swap(b1, b2); + * cbuf_delete(b2); + * @endcode + * @param b1 the destination + * @param b2 the victim + * + * @return b1 + */ +cbuf* cbuf_takeover(cbuf* b1, cbuf* b2); + +/** + * Resize a character buffer. + * This may free allocated memory. + * + * @param b the character buffer. + * @param size the new size + * + * @return b, NULL on error + */ +cbuf* cbuf_resize(cbuf* b, size_t size); + +/** + * Reserve space in a character buffer. + * Assert there are at least len bytes following the current write position. + * + * @param b a character buffer + * @param len number of bytes to reserve. + * + * @return a pointer to the current write position, NULL on error + */ +char* cbuf_reserve(cbuf* b, size_t len); + +/** + * Put a character into the buffer. + * + * @param b a character buffer, may be NULL. + * @param c a character + * @return number of characters written ((b==NULL) ? 0 : 1) + * + * @retval -1 on error + */ +int cbuf_putc(cbuf* b, char c); + +/** + * Put a string into the buffer. + * + * @param b a character buffer, may be NULL + * @param str a string + * @param len number of bytes to write, -1 means strlen(str) + * + * @return number of characters written, -1 on error + */ +int cbuf_puts(cbuf* b, const char* str, size_t len); + +/* /\** */ +/* * Put a string into the buffer, changing case. */ +/* * */ +/* * @param b a character buffer, may be NULL */ +/* * @param str a string */ +/* * @param len number of bytes to write, -1 means strlen(str) */ +/* * @param c a character specifying case: */ +/* * @li 'U' upper case */ +/* * @li 'L' lower case */ +/* * @li 'T' title case */ +/* * @li 'P' preserve case */ +/* * @return number of characters written, -1 on error */ +/* *\/ */ +/* int cbuf_puts_case(cbuf* b, const char* str, size_t len, char c); */ + + + +/** + * Put a uint32 into the buffer. + * Write in little endian order. + * + * @param b a character buffer, may be NULL + * @param u an uint32 + * + * @return number of characters written, -1 on error + */ +int cbuf_putdw(cbuf* b, uint32_t u); + +/** + * Print formatted to a character buffer. + * + * @param b a character buffer + * @param fmt a printf format string + * + * @return number of characters written, negative on error + */ +int cbuf_printf(cbuf* b, const char* fmt, ...) PRINTF_ATTRIBUTE(2,3); + + +/** + * Get the current write position. + * + * @param b a character buffer. + * + * @return index of the next character to write. + */ +size_t cbuf_getpos(const cbuf* b); + +/** + * Set the current write position of a buffer. + * Invalidates the buffer contents from on the new position. + * + * @param b a character buffer + * @param pos a position obtained by cbuf_getpos + */ +void cbuf_setpos(cbuf* b, size_t pos); + +/** + * Get the buffer contents + * starting at idx. + * @pre @code idx <= cbuf_getpos(b) @endcode + * @param b a character buffer + * @param idx a position obtained by cbuf_getpos + * + * @return a NUL terminated string + */ +char* cbuf_gets(cbuf* b, size_t idx); + +/** + * Print quoted string to stream. + * + * @todo check for ssputc failure + * @see srprs_quoted_string + * + * @param[out] ost outstream + * @param[in] s '\0' terminated string of printable characters. + * + * @return number of bytes written, -1 on error + */ +int cbuf_print_quoted_string(cbuf* ost, const char* s); + +/** + * Print quoted string to stream. + * Escapes nonprintable characters. + * + * @todo check for ssputc failure + * @see srprs_quoted + * + * @param[out] ost outstream + * @param[in] s string of bytes + * @param[in] len number of bytes + * + * @return number of bytes written, -1 on error + */ +int cbuf_print_quoted(cbuf* ost, const char* s, size_t len); + + +#endif /*__CBUF_H*/ diff --git a/source3/lib/charcnv.c b/source3/lib/charcnv.c new file mode 100644 index 0000000..d00a3a3 --- /dev/null +++ b/source3/lib/charcnv.c @@ -0,0 +1,535 @@ +/* + Unix SMB/CIFS implementation. + Character set conversion Extensions + Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Simo Sorce 2001 + Copyright (C) Martin Pool 2003 + + 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 <http://www.gnu.org/licenses/>. + +*/ +#include "includes.h" + +/** + * Destroy global objects allocated by init_iconv() + **/ +void gfree_charcnv(void) +{ + free_iconv_handle(); +} + +/** + * Copy a string from a char* unix src to a dos codepage string destination. + * + * @return the number of bytes occupied by the string in the destination. + * + * @param flags can include + * <dl> + * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd> + * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd> + * </dl> + * + * @param dest_len the maximum length in bytes allowed in the + * destination. + **/ +size_t push_ascii(void *dest, const char *src, size_t dest_len, int flags) +{ + size_t src_len = 0; + char *tmpbuf = NULL; + size_t size = 0; + bool ret; + + /* No longer allow a length of -1. */ + if (dest_len == (size_t)-1) { + smb_panic("push_ascii - dest_len == -1"); + } + + if (flags & STR_UPPER) { + tmpbuf = SMB_STRDUP(src); + if (!tmpbuf) { + smb_panic("malloc fail"); + } + if (!strupper_m(tmpbuf)) { + if ((flags & (STR_TERMINATE|STR_TERMINATE_ASCII)) && + dest && + dest_len > 0) { + *(char *)dest = 0; + } + SAFE_FREE(tmpbuf); + return 0; + } + src = tmpbuf; + } + + src_len = strlen(src); + if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) { + src_len++; + } + + ret = convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, &size); + SAFE_FREE(tmpbuf); + if (ret == false) { + if ((flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) && + dest_len > 0) { + ((char *)dest)[0] = '\0'; + } + return 0; + } + return size; +} + +/******************************************************************** + Push and malloc an ascii string. src and dest null terminated. +********************************************************************/ + +/** + * Copy a string from a dos codepage source to a unix char* destination. + * + * The resulting string in "dest" is always null terminated. + * + * @param flags can have: + * <dl> + * <dt>STR_TERMINATE</dt> + * <dd>STR_TERMINATE means the string in @p src + * is null terminated, and src_len is ignored.</dd> + * </dl> + * + * @param src_len is the length of the source area in bytes. + * @returns the number of bytes occupied by the string in @p src. + **/ +size_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + bool ret; + size_t size = 0; + + if (dest_len == (size_t)-1) { + /* No longer allow dest_len of -1. */ + smb_panic("pull_ascii - invalid dest_len of -1"); + } + + if (flags & STR_TERMINATE) { + if (src_len == (size_t)-1) { + src_len = strlen((const char *)src) + 1; + } else { + size_t len = strnlen((const char *)src, src_len); + if (len < src_len) + len++; + src_len = len; + } + } + + ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, &size); + if (ret == false) { + size = 0; + dest_len = 0; + } + + if (dest_len && size) { + /* Did we already process the terminating zero ? */ + if (dest[MIN(size-1, dest_len-1)] != 0) { + dest[MIN(size, dest_len-1)] = 0; + } + } else { + dest[0] = 0; + } + + return src_len; +} + +/** + * Copy a string from a dos codepage source to a unix char* destination. + * Talloc version. + * + * The resulting string in "dest" is always null terminated. + * + * @param flags can have: + * <dl> + * <dt>STR_TERMINATE</dt> + * <dd>STR_TERMINATE means the string in @p src + * is null terminated, and src_len is ignored.</dd> + * </dl> + * + * @param src_len is the length of the source area in bytes. + * @returns the number of bytes occupied by the string in @p src. + **/ + +static size_t pull_ascii_base_talloc(TALLOC_CTX *ctx, + char **ppdest, + const void *src, + size_t src_len, + int flags) +{ + char *dest = NULL; + size_t dest_len; + + *ppdest = NULL; + + if (!src_len) { + return 0; + } + + if (src_len == (size_t)-1) { + smb_panic("src_len == -1 in pull_ascii_base_talloc"); + } + + if (flags & STR_TERMINATE) { + size_t len = strnlen((const char *)src, src_len); + if (len < src_len) + len++; + src_len = len; + /* Ensure we don't use an insane length from the client. */ + if (src_len >= 1024*1024) { + char *msg = talloc_asprintf(ctx, + "Bad src length (%u) in " + "pull_ascii_base_talloc", + (unsigned int)src_len); + smb_panic(msg); + } + } + + /* src_len != -1 here. */ + + if (!convert_string_talloc(ctx, CH_DOS, CH_UNIX, src, src_len, &dest, + &dest_len)) { + dest_len = 0; + } + + if (dest_len && dest) { + /* Did we already process the terminating zero ? */ + if (dest[dest_len-1] != 0) { + size_t size = talloc_get_size(dest); + /* Have we got space to append the '\0' ? */ + if (size <= dest_len) { + /* No, realloc. */ + dest = talloc_realloc(ctx, dest, char, + dest_len+1); + if (!dest) { + /* talloc fail. */ + dest_len = (size_t)-1; + return 0; + } + } + /* Yay - space ! */ + dest[dest_len] = '\0'; + dest_len++; + } + } else if (dest) { + dest[0] = 0; + } + + *ppdest = dest; + return src_len; +} + +/** + * Copy a string from a char* src to a unicode destination. + * + * @returns the number of bytes occupied by the string in the destination. + * + * @param flags can have: + * + * <dl> + * <dt>STR_TERMINATE <dd>means include the null termination. + * <dt>STR_UPPER <dd>means uppercase in the destination. + * <dt>STR_NOALIGN <dd>means don't do alignment. + * </dl> + * + * @param dest_len is the maximum length allowed in the + * destination. + **/ + +static size_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags) +{ + size_t len=0; + size_t src_len; + size_t size = 0; + bool ret; + + if (dest_len == (size_t)-1) { + /* No longer allow dest_len of -1. */ + smb_panic("push_ucs2 - invalid dest_len of -1"); + } + + if (flags & STR_TERMINATE) + src_len = (size_t)-1; + else + src_len = strlen(src); + + if (ucs2_align(base_ptr, dest, flags)) { + *(char *)dest = 0; + dest = (void *)((char *)dest + 1); + if (dest_len) + dest_len--; + len++; + } + + /* ucs2 is always a multiple of 2 bytes */ + dest_len &= ~1; + + ret = convert_string(CH_UNIX, CH_UTF16LE, src, src_len, dest, dest_len, &size); + if (ret == false) { + if ((flags & STR_TERMINATE) && + dest && + dest_len) { + *(char *)dest = 0; + } + return len; + } + + len += size; + + if (flags & STR_UPPER) { + smb_ucs2_t *dest_ucs2 = (smb_ucs2_t *)dest; + size_t i; + + /* We check for i < (size / 2) below as the dest string isn't null + terminated if STR_TERMINATE isn't set. */ + + for (i = 0; i < (size / 2) && i < (dest_len / 2) && dest_ucs2[i]; i++) { + smb_ucs2_t v = toupper_w(dest_ucs2[i]); + if (v != dest_ucs2[i]) { + dest_ucs2[i] = v; + } + } + } + + return len; +} + +/** + Copy a string from a ucs2 source to a unix char* destination. + Talloc version with a base pointer. + Uses malloc if TALLOC_CTX is NULL (this is a bad interface and + needs fixing. JRA). + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_NOALIGN means don't try to align. + if STR_TERMINATE is set then src_len is ignored if it is -1. + src_len is the length of the source area in bytes + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +static size_t pull_ucs2_base_talloc(TALLOC_CTX *ctx, + const void *base_ptr, + char **ppdest, + const void *src, + size_t src_len, + int flags) +{ + char *dest; + size_t dest_len; + size_t ucs2_align_len = 0; + + *ppdest = NULL; + +#ifdef DEVELOPER + /* Ensure we never use the braindead "malloc" variant. */ + if (ctx == NULL) { + smb_panic("NULL talloc CTX in pull_ucs2_base_talloc\n"); + } +#endif + + if (!src_len) { + return 0; + } + + if (src_len == (size_t)-1) { + /* no longer used anywhere, but worth checking */ + smb_panic("sec_len == -1 in pull_ucs2_base_talloc"); + } + + if (ucs2_align(base_ptr, src, flags)) { + src = (const void *)((const char *)src + 1); + src_len--; + ucs2_align_len = 1; + } + + if (flags & STR_TERMINATE) { + /* src_len -1 is the default for null terminated strings. */ + size_t len = strnlen_w((const smb_ucs2_t *)src, + src_len/2); + if (len < src_len/2) + len++; + src_len = len*2; + + /* Ensure we don't use an insane length from the client. */ + if (src_len >= 1024*1024) { + smb_panic("Bad src length in pull_ucs2_base_talloc\n"); + } + } + + /* ucs2 is always a multiple of 2 bytes */ + src_len &= ~1; + + if (!convert_string_talloc(ctx, CH_UTF16LE, CH_UNIX, src, src_len, + (void *)&dest, &dest_len)) { + dest_len = 0; + } + + if (dest_len) { + /* Did we already process the terminating zero ? */ + if (dest[dest_len-1] != 0) { + size_t size = talloc_get_size(dest); + /* Have we got space to append the '\0' ? */ + if (size <= dest_len) { + /* No, realloc. */ + dest = talloc_realloc(ctx, dest, char, + dest_len+1); + if (!dest) { + /* talloc fail. */ + dest_len = (size_t)-1; + return 0; + } + } + /* Yay - space ! */ + dest[dest_len] = '\0'; + dest_len++; + } + } else if (dest) { + dest[0] = 0; + } + + *ppdest = dest; + return src_len + ucs2_align_len; +} + +/** + Copy a string from a char* src to a unicode or ascii + dos codepage destination choosing unicode or ascii based on the + flags supplied + Return the number of bytes occupied by the string in the destination. + flags can have: + STR_TERMINATE means include the null termination. + STR_UPPER means uppercase in the destination. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + dest_len is the maximum length allowed in the destination. If dest_len + is -1 then no maximum is used. +**/ + +size_t push_string_check_fn(void *dest, const char *src, + size_t dest_len, int flags) +{ + if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) { + return push_ucs2(NULL, dest, src, dest_len, flags); + } + return push_ascii(dest, src, dest_len, flags); +} + + +/** + Copy a string from a char* src to a unicode or ascii + dos codepage destination choosing unicode or ascii based on the + flags in the SMB buffer starting at base_ptr. + Return the number of bytes occupied by the string in the destination. + flags can have: + STR_TERMINATE means include the null termination. + STR_UPPER means uppercase in the destination. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + dest_len is the maximum length allowed in the destination. If dest_len + is -1 then no maximum is used. +**/ + +size_t push_string_base(const char *base, uint16_t flags2, + void *dest, const char *src, + size_t dest_len, int flags) +{ + + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (flags2 & FLAGS2_UNICODE_STRINGS)))) { + return push_ucs2(base, dest, src, dest_len, flags); + } + return push_ascii(dest, src, dest_len, flags); +} + +/** + Copy a string from a unicode or ascii source (depending on + the packet flags) to a char* destination. + Variant that uses talloc. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_UNICODE means to force as unicode. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + if STR_TERMINATE is set then src_len is ignored is it is -1 + src_len is the length of the source area in bytes. + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +size_t pull_string_talloc(TALLOC_CTX *ctx, + const void *base_ptr, + uint16_t smb_flags2, + char **ppdest, + const void *src, + size_t src_len, + int flags) +{ + if ((base_ptr == NULL) && ((flags & (STR_ASCII|STR_UNICODE)) == 0)) { + smb_panic("No base ptr to get flg2 and neither ASCII nor " + "UNICODE defined"); + } + + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (smb_flags2 & FLAGS2_UNICODE_STRINGS)))) { + return pull_ucs2_base_talloc(ctx, + base_ptr, + ppdest, + src, + src_len, + flags); + } + return pull_ascii_base_talloc(ctx, + ppdest, + src, + src_len, + flags); +} + +/******************************************************************* + Write a string in (little-endian) unicode format. src is in + the current DOS codepage. len is the length in bytes of the + string pointed to by dst. + + if null_terminate is True then null terminate the packet (adds 2 bytes) + + the return value is the length in bytes consumed by the string, including the + null termination if applied +********************************************************************/ + +size_t dos_PutUniCode(char *dst,const char *src, size_t len, bool null_terminate) +{ + int flags = null_terminate ? STR_UNICODE|STR_NOALIGN|STR_TERMINATE + : STR_UNICODE|STR_NOALIGN; + return push_ucs2(NULL, dst, src, len, flags); +} + + +/* Converts a string from internal samba format to unicode. Always terminates. + * Actually just a wrapper round push_ucs2_talloc(). + */ + +int rpcstr_push_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src) +{ + size_t size; + if (push_ucs2_talloc(ctx, dest, src, &size)) + return size; + else + return -1; +} diff --git a/source3/lib/cleanupdb.c b/source3/lib/cleanupdb.c new file mode 100644 index 0000000..1b3c5a2 --- /dev/null +++ b/source3/lib/cleanupdb.c @@ -0,0 +1,169 @@ +/* + Unix SMB/CIFS implementation. + Implementation of reliable cleanup events + Copyright (C) Ralph Boehme 2016 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/debug.h" +#include "source3/lib/cleanupdb.h" +#include "source3/lib/util_path.h" + +struct cleanup_key { + pid_t pid; +}; + +struct cleanup_rec { + bool unclean; +}; + +static struct tdb_wrap *cleanup_db(void) +{ + static struct tdb_wrap *db; + char *db_path = NULL; + int tdbflags = TDB_INCOMPATIBLE_HASH | TDB_CLEAR_IF_FIRST | + TDB_MUTEX_LOCKING; + + if (db != NULL) { + return db; + } + + db_path = lock_path(talloc_tos(), "smbd_cleanupd.tdb"); + if (db_path == NULL) { + return NULL; + } + + db = tdb_wrap_open(NULL, db_path, 0, tdbflags, + O_CREAT | O_RDWR, 0644); + if (db == NULL) { + DBG_ERR("Failed to open smbd_cleanupd.tdb\n"); + } + + TALLOC_FREE(db_path); + return db; +} + +bool cleanupdb_store_child(const pid_t pid, const bool unclean) +{ + struct tdb_wrap *db; + struct cleanup_key key = { .pid = pid }; + struct cleanup_rec rec = { .unclean = unclean }; + TDB_DATA tdbkey = { .dptr = (uint8_t *)&key, .dsize = sizeof(key) }; + TDB_DATA tdbdata = { .dptr = (uint8_t *)&rec, .dsize = sizeof(rec) }; + int result; + + db = cleanup_db(); + if (db == NULL) { + return false; + } + + result = tdb_store(db->tdb, tdbkey, tdbdata, TDB_REPLACE); + if (result != 0) { + DBG_ERR("tdb_store failed for pid %d\n", (int)pid); + return false; + } + + return true; +} + +bool cleanupdb_delete_child(const pid_t pid) +{ + struct tdb_wrap *db; + struct cleanup_key key = { .pid = pid }; + TDB_DATA tdbkey = { .dptr = (uint8_t *)&key, .dsize = sizeof(key) }; + int result; + + db = cleanup_db(); + if (db == NULL) { + return false; + } + + result = tdb_delete(db->tdb, tdbkey); + if (result != 0) { + DBG_ERR("tdb_delete failed for pid %d\n", (int)pid); + return false; + } + + return true; +} + +struct cleanup_read_state { + int (*fn)(const pid_t pid, const bool cleanup, void *private_data); + void *private_data; +}; + +static int cleanup_traverse_fn(struct tdb_context *tdb, + TDB_DATA key, TDB_DATA value, + void *private_data) +{ + struct cleanup_read_state *state = + (struct cleanup_read_state *)private_data; + struct cleanup_key ckey; + struct cleanup_rec rec; + int result; + + if (key.dsize != sizeof(struct cleanup_key)) { + DBG_ERR("Found invalid key length %zu in cleanup.tdb\n", + key.dsize); + return -1; + } + memcpy(&ckey, key.dptr, sizeof(struct cleanup_key)); + + if (value.dsize != sizeof(struct cleanup_rec)) { + DBG_ERR("Found invalid value length %zu in cleanup.tdb\n", + value.dsize); + return -1; + } + memcpy(&rec, value.dptr, sizeof(struct cleanup_rec)); + + result = state->fn(ckey.pid, rec.unclean, state->private_data); + if (result != 0) { + return -1; + } + + return 0; +} + +int cleanupdb_traverse_read(int (*fn)(const pid_t pid, + const bool cleanup, + void *private_data), + void *private_data) +{ + struct tdb_wrap *db; + struct cleanup_read_state state; + int result; + + db = cleanup_db(); + if (db == NULL) { + return -1; + } + + state = (struct cleanup_read_state) { + .fn = fn, + .private_data = private_data + }; + + result = tdb_traverse_read(db->tdb, cleanup_traverse_fn, &state); + if (result < 0) { + DBG_ERR("tdb_traverse_read failed\n"); + return -1; + } + + return result; +} diff --git a/source3/lib/cleanupdb.h b/source3/lib/cleanupdb.h new file mode 100644 index 0000000..83f51bb --- /dev/null +++ b/source3/lib/cleanupdb.h @@ -0,0 +1,32 @@ +/* + Unix SMB/CIFS implementation. + Implementation of reliable cleanup events + Copyright (C) Ralph Boehme 2016 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __LIB_CLEANUPDB_H__ +#define __LIB_CLEANUPDB_H__ + +#include "replace.h" + +bool cleanupdb_store_child(const pid_t pid, const bool unclean); +bool cleanupdb_delete_child(const pid_t pid); +int cleanupdb_traverse_read(int (*fn)(const pid_t pid, + const bool cleanup, + void *private_data), + void *private_data); + +#endif diff --git a/source3/lib/cluster_support.c b/source3/lib/cluster_support.c new file mode 100644 index 0000000..c11b1f7 --- /dev/null +++ b/source3/lib/cluster_support.c @@ -0,0 +1,72 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2014 Stefan Metzmacher + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include <tdb.h> +#include "cluster_support.h" + +#ifdef CLUSTER_SUPPORT +#include <ctdb_protocol.h> +#endif + +bool cluster_support_available(void) +{ +#ifdef CLUSTER_SUPPORT + return true; +#else + return false; +#endif +} + +const char *cluster_support_features(void) +{ +#define _LINE_DEF(x) " " #x "\n" +#define _LINE_STR(x) " " #x ": " x "\n" +#define _LINE_INT(x) " " #x ": " __STRINGSTRING(x) "\n" + static const char *v = "Cluster support features:\n" +#ifdef CLUSTER_SUPPORT + _LINE_DEF(CLUSTER_SUPPORT) +#else + " NONE\n" +#endif +#ifdef CTDB_SOCKET + _LINE_STR(CTDB_SOCKET) +#endif +#ifdef CTDB_PROTOCOL + _LINE_INT(CTDB_PROTOCOL) +#endif + ""; + + return v; +} + +const char *lp_ctdbd_socket(void) +{ + const char *ret; + + ret = lp__ctdbd_socket(); + if (ret != NULL && strlen(ret) > 0) { + return ret; + } + +#ifdef CTDB_SOCKET + return CTDB_SOCKET; +#else + return ""; +#endif +} diff --git a/source3/lib/cluster_support.h b/source3/lib/cluster_support.h new file mode 100644 index 0000000..5e040bb --- /dev/null +++ b/source3/lib/cluster_support.h @@ -0,0 +1,21 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2014 Stefan Metzmacher + + 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 <http://www.gnu.org/licenses/>. +*/ + +bool cluster_support_available(void); +const char *cluster_support_features(void); +const char *lp_ctdbd_socket(void); diff --git a/source3/lib/cmdline_contexts.c b/source3/lib/cmdline_contexts.c new file mode 100644 index 0000000..627ee4f --- /dev/null +++ b/source3/lib/cmdline_contexts.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + cmdline context wrapper. + + Copyright (C) Christof Schmitt <cs@samba.org> 2018 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "cmdline_contexts.h" +#include "includes.h" +#include "messages.h" +#include "lib/global_contexts.h" + +struct messaging_context *cmdline_messaging_context(const char *config_file) +{ + struct messaging_context *msg_ctx = NULL; + + /* + * Ensure that a config is loaded, in case the underlying + * messaging_init needs to create directories or sockets. + */ + if (!lp_loaded()) { + if (!lp_load_initial_only(config_file)) { + return NULL; + } + } + + /* + * Clustered Samba can only work as root due to required + * access to the registry and ctdb, which in turn requires + * messaging access as root. + */ + if (lp_clustering() && geteuid() != 0) { + fprintf(stderr, "Cluster mode requires running as root.\n"); + exit(1); + } + + msg_ctx = global_messaging_context(); + if (msg_ctx == NULL) { + if (geteuid() == 0) { + fprintf(stderr, + "Unable to initialize messaging context!\n"); + exit(1); + } else { + /* + * Non-cluster, non-root: Log error, but leave + * it up to the caller how to proceed. + */ + DBG_NOTICE("Unable to initialize messaging context.\n"); + } + } + + return msg_ctx; +} + +void cmdline_messaging_context_free(void) +{ + global_messaging_context_free(); +} diff --git a/source3/lib/cmdline_contexts.h b/source3/lib/cmdline_contexts.h new file mode 100644 index 0000000..21f81f0 --- /dev/null +++ b/source3/lib/cmdline_contexts.h @@ -0,0 +1,27 @@ +/* + Unix SMB/CIFS implementation. + cmdline context wrapper. + + Copyright (C) Christof Schmitt <cs@samba.org> 2018 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIB_CMDLINE_CONTEXTS_H +#define _LIB_CMDLINE_CONTEXTS_H + +struct messaging_context *cmdline_messaging_context(const char *config_file); +void cmdline_messaging_context_free(void); + +#endif diff --git a/source3/lib/ctdb_dummy.c b/source3/lib/ctdb_dummy.c new file mode 100644 index 0000000..f159b7d --- /dev/null +++ b/source3/lib/ctdb_dummy.c @@ -0,0 +1,180 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2014 Björn Baumbach + Copyright (C) 2014 Stefan Metzmacher + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "messages.h" +#include "lib/messages_ctdb.h" +#include "lib/messages_ctdb_ref.h" +#include "ctdbd_conn.h" +#include "lib/dbwrap/dbwrap.h" +#include "lib/dbwrap/dbwrap_ctdb.h" +#include "torture/proto.h" + +int ctdbd_probe(const char *sockname, int timeout) +{ + return ENOSYS; +} + +int ctdbd_messaging_send_iov(struct ctdbd_connection *conn, + uint32_t dst_vnn, uint64_t dst_srvid, + const struct iovec *iov, int iovlen) +{ + return ENOSYS; +} + +int register_with_ctdbd(struct ctdbd_connection *conn, uint64_t srvid, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, size_t msglen, + void *private_data), + void *private_data) +{ + return ENOSYS; +} + +void deregister_from_ctdbd(struct ctdbd_connection *conn, + uint64_t srvid, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, + uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, + size_t msglen, + void *private_data), + void *private_data) +{ +} + +int ctdbd_register_ips(struct ctdbd_connection *conn, + const struct sockaddr_storage *_server, + const struct sockaddr_storage *_client, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, size_t msglen, + void *private_data), + void *private_data) +{ + return ENOSYS; +} + +void ctdbd_unregister_ips(struct ctdbd_connection *conn, + const struct sockaddr_storage *_server, + const struct sockaddr_storage *_client, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, + uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, + size_t msglen, + void *private_data), + void *private_data) +{ + return; +} +void ctdbd_passed_ips(struct ctdbd_connection *conn, + const struct sockaddr_storage *_server, + const struct sockaddr_storage *_client, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, + uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, + size_t msglen, + void *private_data), + void *private_data) +{ + return; +} + +int ctdbd_public_ip_foreach(struct ctdbd_connection *conn, + int (*cb)(uint32_t total_ip_count, + const struct sockaddr_storage *ip, + bool is_movable_ip, + void *private_data), + void *private_data) +{ + return ENOSYS; +} + +int ctdbd_all_ip_foreach(struct ctdbd_connection *conn, + bool include_node_ips, + bool include_public_ips, + int (*cb)(uint32_t total_ip_count, + const struct sockaddr_storage *ip, + uint32_t pinned_pnn, + uint32_t current_pnn, + void *private_data), + void *private_data) +{ + return ENOSYS; +} + +bool ctdbd_process_exists(struct ctdbd_connection *conn, uint32_t vnn, + pid_t pid, uint64_t unique_id) +{ + return false; +} + +struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *name, + int hash_size, int tdb_flags, + int open_flags, mode_t mode, + enum dbwrap_lock_order lock_order, + uint64_t dbwrap_flags) +{ + errno = ENOSYS; + return NULL; +} + +int messaging_ctdb_send(uint32_t dst_vnn, uint64_t dst_srvid, + const struct iovec *iov, int iovlen) +{ + return ENOSYS; +} + +void *messaging_ctdb_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *sockname, int timeout, uint64_t unique_id, + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data), + void *private_data, + int *err) +{ + return NULL; +} + +struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context( + TALLOC_CTX *mem_ctx, struct tevent_context *ev) +{ + return NULL; +} + +struct ctdbd_connection *messaging_ctdb_connection(void) +{ + return NULL; +} + +int ctdb_async_ctx_reinit(TALLOC_CTX *mem_ctx, struct tevent_context *ev) +{ + return ENOSYS; +} diff --git a/source3/lib/ctdbd_conn.c b/source3/lib/ctdbd_conn.c new file mode 100644 index 0000000..9f8dce6 --- /dev/null +++ b/source3/lib/ctdbd_conn.c @@ -0,0 +1,2341 @@ +/* + Unix SMB/CIFS implementation. + Samba internal messaging functions + Copyright (C) 2007 by Volker Lendecke + Copyright (C) 2007 by Andrew Tridgell + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include <tevent.h> +#include "util_tdb.h" +#include "serverid.h" +#include "ctdbd_conn.h" +#include "system/select.h" +#include "lib/util/util_net.h" +#include "lib/util/sys_rw_data.h" +#include "lib/util/iov_buf.h" +#include "lib/util/select.h" +#include "lib/util/debug.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/genrand.h" +#include "lib/util/fault.h" +#include "lib/util/dlinklist.h" +#include "lib/util/tevent_unix.h" +#include "lib/util/sys_rw.h" +#include "lib/util/blocking.h" +#include "ctdb/include/ctdb_protocol.h" +#include "lib/async_req/async_sock.h" +#include "lib/dbwrap/dbwrap.h" +#include "lib/dbwrap/dbwrap_rbt.h" + +/* paths to these include files come from --with-ctdb= in configure */ + +struct ctdbd_srvid_cb { + uint64_t srvid; + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, size_t msglen, + void *private_data); + void *private_data; +}; + +struct ctdbd_connection { + uint32_t reqid; + uint32_t our_vnn; + uint64_t rand_srvid; + struct ctdbd_srvid_cb *callbacks; + int fd; + int timeout; + + /* + * Outgoing queue for writev_send of asynchronous ctdb requests + */ + struct tevent_queue *outgoing; + struct tevent_req **pending; + struct tevent_req *read_req; +}; + +static bool ctdbd_conn_has_async_reqs(struct ctdbd_connection *conn) +{ + size_t len = talloc_array_length(conn->pending); + return (len != 0); +} + +static uint32_t ctdbd_next_reqid(struct ctdbd_connection *conn) +{ + conn->reqid += 1; + if (conn->reqid == 0) { + conn->reqid += 1; + } + return conn->reqid; +} + +static int ctdbd_control(struct ctdbd_connection *conn, + uint32_t vnn, uint32_t opcode, + uint64_t srvid, uint32_t flags, + TDB_DATA data, + TALLOC_CTX *mem_ctx, TDB_DATA *outdata, + int32_t *cstatus); + +/* + * exit on fatal communications errors with the ctdbd daemon + */ +static void cluster_fatal(const char *why) +{ + DEBUG(0,("cluster fatal event: %s - exiting immediately\n", why)); + /* we don't use smb_panic() as we don't want to delay to write + a core file. We need to release this process id immediately + so that someone else can take over without getting sharing + violations */ + _exit(1); +} + +/* + * + */ +static void ctdb_packet_dump(struct ctdb_req_header *hdr) +{ + if (DEBUGLEVEL < 11) { + return; + } + DEBUGADD(11, ("len=%"PRIu32", magic=%"PRIu32", vers=%"PRIu32", " + "gen=%"PRIu32", op=%"PRIu32", reqid=%"PRIu32"\n", + hdr->length, + hdr->ctdb_magic, + hdr->ctdb_version, + hdr->generation, + hdr->operation, + hdr->reqid)); +} + +/* + * Register a srvid with ctdbd + */ +int register_with_ctdbd(struct ctdbd_connection *conn, uint64_t srvid, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, size_t msglen, + void *private_data), + void *private_data) +{ + size_t num_callbacks = talloc_array_length(conn->callbacks); + struct ctdbd_srvid_cb *tmp; + bool need_register = true; + size_t i; + + for (i = 0; i < num_callbacks; i++) { + struct ctdbd_srvid_cb *c = &conn->callbacks[i]; + + if (c->srvid == srvid) { + need_register = false; + break; + } + } + + if (need_register) { + int ret; + int32_t cstatus; + + ret = ctdbd_control_local(conn, CTDB_CONTROL_REGISTER_SRVID, + srvid, 0, tdb_null, NULL, NULL, + &cstatus); + if (ret != 0) { + return ret; + } + } + + + tmp = talloc_realloc(conn, conn->callbacks, struct ctdbd_srvid_cb, + num_callbacks + 1); + if (tmp == NULL) { + return ENOMEM; + } + conn->callbacks = tmp; + + conn->callbacks[num_callbacks] = (struct ctdbd_srvid_cb) { + .srvid = srvid, .cb = cb, .private_data = private_data + }; + + return 0; +} + +void deregister_from_ctdbd(struct ctdbd_connection *conn, + uint64_t srvid, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, + uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, + size_t msglen, + void *private_data), + void *private_data) +{ + struct ctdbd_srvid_cb *cbs = conn->callbacks; + size_t i, num_callbacks = talloc_array_length(cbs); + bool need_deregister = false; + bool keep_registration = false; + + if (num_callbacks == 0) { + return; + } + + for (i = 0; i < num_callbacks;) { + struct ctdbd_srvid_cb *c = &cbs[i]; + + if (c->srvid != srvid) { + i++; + continue; + } + + if ((c->cb == cb) && (c->private_data == private_data)) { + need_deregister = true; + ARRAY_DEL_ELEMENT(cbs, i, num_callbacks); + num_callbacks--; + continue; + } + + keep_registration = true; + i++; + } + + conn->callbacks = talloc_realloc(conn, + cbs, + struct ctdbd_srvid_cb, + num_callbacks); + + if (keep_registration) { + need_deregister = false; + } + + if (need_deregister) { + int ret; + int32_t cstatus; + + ret = ctdbd_control_local(conn, CTDB_CONTROL_DEREGISTER_SRVID, + srvid, 0, tdb_null, NULL, NULL, + &cstatus); + if (ret != 0) { + /* + * If CTDB_CONTROL_DEREGISTER_SRVID fails we may still + * get messages later, but we don't have a callback + * anymore, we just ignore these. + */ + } + } + + return; +} + +static int ctdbd_msg_call_back(struct tevent_context *ev, + struct ctdbd_connection *conn, + struct ctdb_req_message_old *msg) +{ + uint32_t msg_len; + size_t i, num_callbacks; + + msg_len = msg->hdr.length; + if (msg_len < offsetof(struct ctdb_req_message_old, data)) { + DBG_DEBUG("len %"PRIu32" too small\n", msg_len); + return 0; + } + msg_len -= offsetof(struct ctdb_req_message_old, data); + + if (msg_len < msg->datalen) { + DBG_DEBUG("msg_len=%"PRIu32" < msg->datalen=%"PRIu32"\n", + msg_len, msg->datalen); + return 0; + } + + num_callbacks = talloc_array_length(conn->callbacks); + + for (i=0; i<num_callbacks; i++) { + struct ctdbd_srvid_cb *cb = &conn->callbacks[i]; + + if ((cb->srvid == msg->srvid) && (cb->cb != NULL)) { + int ret; + + ret = cb->cb(ev, + msg->hdr.srcnode, msg->hdr.destnode, + msg->srvid, msg->data, msg->datalen, + cb->private_data); + if (ret != 0) { + return ret; + } + } + } + return 0; +} + +/* + * get our vnn from the cluster + */ +static int get_cluster_vnn(struct ctdbd_connection *conn, uint32_t *vnn) +{ + int32_t cstatus=-1; + int ret; + ret = ctdbd_control_local(conn, CTDB_CONTROL_GET_PNN, 0, 0, + tdb_null, NULL, NULL, &cstatus); + if (ret != 0) { + DEBUG(1, ("ctdbd_control failed: %s\n", strerror(ret))); + return ret; + } + *vnn = (uint32_t)cstatus; + return ret; +} + +static int ctdbd_control_get_nodemap(struct ctdbd_connection *conn, + TALLOC_CTX *mem_ctx, + struct ctdb_node_map_old **_nodemap) +{ + int32_t cstatus=-1; + TDB_DATA outdata = {0}; + int ret; + + ret = ctdbd_control_local(conn, CTDB_CONTROL_GET_NODEMAP, 0, 0, + tdb_null, mem_ctx, &outdata, &cstatus); + if (ret != 0) { + DEBUG(1, ("ctdbd_control failed: %s\n", strerror(ret))); + return ret; + } + if ((cstatus != 0) || (outdata.dptr == NULL)) { + DEBUG(2, ("Received invalid ctdb data\n")); + return EINVAL; + } + + *_nodemap = (struct ctdb_node_map_old *)outdata.dptr; + return 0; +} + +/* + * Are we active (i.e. not banned or stopped?) + */ +static bool ctdbd_working(struct ctdbd_connection *conn, uint32_t vnn) +{ + struct ctdb_node_map_old *m = NULL; + bool ok = false; + uint32_t i; + int ret; + + ret = ctdbd_control_get_nodemap(conn, talloc_tos(), &m); + if (ret != 0) { + DEBUG(1, ("ctdbd_control_get_nodemap() failed: %s\n", strerror(ret))); + return false; + } + + for (i=0; i<m->num; i++) { + if (vnn == m->nodes[i].pnn) { + break; + } + } + + if (i == m->num) { + DEBUG(2, ("Did not find ourselves (node %d) in nodemap\n", + (int)vnn)); + goto fail; + } + + if ((m->nodes[i].flags & NODE_FLAGS_INACTIVE) != 0) { + DEBUG(2, ("Node has status %x, not active\n", + (int)m->nodes[i].flags)); + goto fail; + } + + ok = true; +fail: + TALLOC_FREE(m); + return ok; +} + +uint32_t ctdbd_vnn(const struct ctdbd_connection *conn) +{ + return conn->our_vnn; +} + +/* + * Get us a ctdb connection + */ + +static int ctdbd_connect(const char *sockname, int *pfd) +{ + struct samba_sockaddr addr = { + .sa_socklen = sizeof(struct sockaddr_un), + .u = { + .un = { + .sun_family = AF_UNIX, + }, + }, + }; + int fd; + size_t namelen; + int ret; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + int err = errno; + DEBUG(3, ("Could not create socket: %s\n", strerror(err))); + return err; + } + + namelen = strlcpy(addr.u.un.sun_path, + sockname, + sizeof(addr.u.un.sun_path)); + if (namelen >= sizeof(addr.u.un.sun_path)) { + DEBUG(3, ("%s: Socket name too long: %s\n", __func__, + sockname)); + close(fd); + return ENAMETOOLONG; + } + + ret = connect(fd, &addr.u.sa, addr.sa_socklen); + if (ret == -1) { + int err = errno; + DEBUG(1, ("connect(%s) failed: %s\n", sockname, + strerror(err))); + close(fd); + return err; + } + + *pfd = fd; + return 0; +} + +static int ctdb_read_packet(int fd, int timeout, TALLOC_CTX *mem_ctx, + struct ctdb_req_header **result) +{ + struct ctdb_req_header *req; + uint32_t msglen; + ssize_t nread; + + if (timeout != -1) { + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + int ret; + + ret = sys_poll_intr(&pfd, 1, timeout); + if (ret == -1) { + return errno; + } + if (ret == 0) { + return ETIMEDOUT; + } + if (ret != 1) { + return EIO; + } + } + + nread = read_data(fd, &msglen, sizeof(msglen)); + if (nread == -1) { + return errno; + } + if (nread == 0) { + return EIO; + } + + if (msglen < sizeof(struct ctdb_req_header)) { + return EIO; + } + + req = talloc_size(mem_ctx, msglen); + if (req == NULL) { + return ENOMEM; + } + talloc_set_name_const(req, "struct ctdb_req_header"); + + req->length = msglen; + + nread = read_data(fd, ((char *)req) + sizeof(msglen), + msglen - sizeof(msglen)); + if (nread == -1) { + TALLOC_FREE(req); + return errno; + } + if (nread == 0) { + TALLOC_FREE(req); + return EIO; + } + + *result = req; + return 0; +} + +/* + * Read a full ctdbd request. If we have a messaging context, defer incoming + * messages that might come in between. + */ + +static int ctdb_read_req(struct ctdbd_connection *conn, uint32_t reqid, + TALLOC_CTX *mem_ctx, struct ctdb_req_header **result) +{ + struct ctdb_req_header *hdr = NULL; + int ret; + + next_pkt: + + ret = ctdb_read_packet(conn->fd, conn->timeout, mem_ctx, &hdr); + if (ret != 0) { + DBG_ERR("ctdb_read_packet failed: %s\n", strerror(ret)); + cluster_fatal("failed to read data from ctdbd\n"); + return -1; + } + SMB_ASSERT(hdr != NULL); + + DEBUG(11, ("Received ctdb packet\n")); + ctdb_packet_dump(hdr); + + if (hdr->operation == CTDB_REQ_MESSAGE) { + struct ctdb_req_message_old *msg = (struct ctdb_req_message_old *)hdr; + + ret = ctdbd_msg_call_back(NULL, conn, msg); + if (ret != 0) { + TALLOC_FREE(hdr); + return ret; + } + + TALLOC_FREE(hdr); + goto next_pkt; + } + + if ((reqid != 0) && (hdr->reqid != reqid)) { + /* we got the wrong reply */ + DEBUG(0,("Discarding mismatched ctdb reqid %u should have " + "been %u\n", hdr->reqid, reqid)); + TALLOC_FREE(hdr); + goto next_pkt; + } + + *result = talloc_move(mem_ctx, &hdr); + + return 0; +} + +static int ctdbd_connection_destructor(struct ctdbd_connection *c); + +/* + * Get us a ctdbd connection + */ + +static int ctdbd_init_connection_internal(TALLOC_CTX *mem_ctx, + const char *sockname, int timeout, + struct ctdbd_connection *conn) +{ + int ret; + + conn->timeout = timeout; + if (conn->timeout == 0) { + conn->timeout = -1; + } + + ret = ctdbd_connect(sockname, &conn->fd); + if (ret != 0) { + DEBUG(1, ("ctdbd_connect failed: %s\n", strerror(ret))); + return ret; + } + talloc_set_destructor(conn, ctdbd_connection_destructor); + + ret = get_cluster_vnn(conn, &conn->our_vnn); + if (ret != 0) { + DEBUG(10, ("get_cluster_vnn failed: %s\n", strerror(ret))); + return ret; + } + + if (!ctdbd_working(conn, conn->our_vnn)) { + DEBUG(2, ("Node is not working, can not connect\n")); + return EIO; + } + + generate_random_buffer((unsigned char *)&conn->rand_srvid, + sizeof(conn->rand_srvid)); + + ret = register_with_ctdbd(conn, conn->rand_srvid, NULL, NULL); + if (ret != 0) { + DEBUG(5, ("Could not register random srvid: %s\n", + strerror(ret))); + return ret; + } + + return 0; +} + +int ctdbd_init_connection(TALLOC_CTX *mem_ctx, + const char *sockname, int timeout, + struct ctdbd_connection **pconn) +{ + struct ctdbd_connection *conn; + int ret; + + if (!(conn = talloc_zero(mem_ctx, struct ctdbd_connection))) { + DEBUG(0, ("talloc failed\n")); + return ENOMEM; + } + + ret = ctdbd_init_connection_internal(mem_ctx, + sockname, + timeout, + conn); + if (ret != 0) { + DBG_ERR("ctdbd_init_connection_internal failed (%s)\n", + strerror(ret)); + goto fail; + } + + *pconn = conn; + return 0; + + fail: + TALLOC_FREE(conn); + return ret; +} + +int ctdbd_reinit_connection(TALLOC_CTX *mem_ctx, + const char *sockname, int timeout, + struct ctdbd_connection *conn) +{ + int ret; + + ret = ctdbd_connection_destructor(conn); + if (ret != 0) { + DBG_ERR("ctdbd_connection_destructor failed\n"); + return ret; + } + + ret = ctdbd_init_connection_internal(mem_ctx, + sockname, + timeout, + conn); + if (ret != 0) { + DBG_ERR("ctdbd_init_connection_internal failed (%s)\n", + strerror(ret)); + return ret; + } + + return 0; +} + +int ctdbd_init_async_connection( + TALLOC_CTX *mem_ctx, + const char *sockname, + int timeout, + struct ctdbd_connection **pconn) +{ + struct ctdbd_connection *conn = NULL; + int ret; + + *pconn = NULL; + + ret = ctdbd_init_connection(mem_ctx, sockname, timeout, &conn); + if (ret != 0) { + return ret; + } + + ret = set_blocking(conn->fd, false); + if (ret == -1) { + int err = errno; + SMB_ASSERT(err != 0); + TALLOC_FREE(conn); + return err; + } + + conn->outgoing = tevent_queue_create(conn, "ctdb async outgoing"); + if (conn->outgoing == NULL) { + TALLOC_FREE(conn); + return ENOMEM; + } + + *pconn = conn; + return 0; +} + +int ctdbd_conn_get_fd(struct ctdbd_connection *conn) +{ + return conn->fd; +} + +/* + * Packet handler to receive and handle a ctdb message + */ +static int ctdb_handle_message(struct tevent_context *ev, + struct ctdbd_connection *conn, + struct ctdb_req_header *hdr) +{ + struct ctdb_req_message_old *msg; + + if (hdr->operation != CTDB_REQ_MESSAGE) { + DEBUG(0, ("Received async msg of type %u, discarding\n", + hdr->operation)); + return EINVAL; + } + + msg = (struct ctdb_req_message_old *)hdr; + + ctdbd_msg_call_back(ev, conn, msg); + + return 0; +} + +void ctdbd_socket_readable(struct tevent_context *ev, + struct ctdbd_connection *conn) +{ + struct ctdb_req_header *hdr = NULL; + int ret; + + ret = ctdb_read_packet(conn->fd, conn->timeout, talloc_tos(), &hdr); + if (ret != 0) { + DBG_ERR("ctdb_read_packet failed: %s\n", strerror(ret)); + cluster_fatal("failed to read data from ctdbd\n"); + } + SMB_ASSERT(hdr != NULL); + + ret = ctdb_handle_message(ev, conn, hdr); + + TALLOC_FREE(hdr); + + if (ret != 0) { + DEBUG(10, ("could not handle incoming message: %s\n", + strerror(ret))); + } +} + +int ctdbd_messaging_send_iov(struct ctdbd_connection *conn, + uint32_t dst_vnn, uint64_t dst_srvid, + const struct iovec *iov, int iovlen) +{ + struct ctdb_req_message_old r; + struct iovec iov2[iovlen+1]; + size_t buflen = iov_buflen(iov, iovlen); + ssize_t nwritten; + + r.hdr.length = offsetof(struct ctdb_req_message_old, data) + buflen; + r.hdr.ctdb_magic = CTDB_MAGIC; + r.hdr.ctdb_version = CTDB_PROTOCOL; + r.hdr.generation = 1; + r.hdr.operation = CTDB_REQ_MESSAGE; + r.hdr.destnode = dst_vnn; + r.hdr.srcnode = conn->our_vnn; + r.hdr.reqid = 0; + r.srvid = dst_srvid; + r.datalen = buflen; + + DEBUG(10, ("ctdbd_messaging_send: Sending ctdb packet\n")); + ctdb_packet_dump(&r.hdr); + + iov2[0].iov_base = &r; + iov2[0].iov_len = offsetof(struct ctdb_req_message_old, data); + memcpy(&iov2[1], iov, iovlen * sizeof(struct iovec)); + + nwritten = write_data_iov(conn->fd, iov2, iovlen+1); + if (nwritten == -1) { + DEBUG(3, ("write_data_iov failed: %s\n", strerror(errno))); + cluster_fatal("cluster dispatch daemon msg write error\n"); + } + + return 0; +} + +/* + * send/recv a generic ctdb control message + */ +static int ctdbd_control(struct ctdbd_connection *conn, + uint32_t vnn, uint32_t opcode, + uint64_t srvid, uint32_t flags, + TDB_DATA data, + TALLOC_CTX *mem_ctx, TDB_DATA *outdata, + int32_t *cstatus) +{ + struct ctdb_req_control_old req; + struct ctdb_req_header *hdr; + struct ctdb_reply_control_old *reply = NULL; + struct iovec iov[2]; + ssize_t nwritten; + int ret; + + if (ctdbd_conn_has_async_reqs(conn)) { + /* + * Can't use sync call while an async call is in flight. Adding + * this check as a safety net. We'll be using different + * connections for sync and async requests, so this shouldn't + * happen, but who knows... + */ + DBG_ERR("Async ctdb req on sync connection\n"); + return EINVAL; + } + + ZERO_STRUCT(req); + req.hdr.length = offsetof(struct ctdb_req_control_old, data) + data.dsize; + req.hdr.ctdb_magic = CTDB_MAGIC; + req.hdr.ctdb_version = CTDB_PROTOCOL; + req.hdr.operation = CTDB_REQ_CONTROL; + req.hdr.reqid = ctdbd_next_reqid(conn); + req.hdr.destnode = vnn; + req.opcode = opcode; + req.srvid = srvid; + req.datalen = data.dsize; + req.flags = flags; + + DBG_DEBUG("Sending ctdb packet reqid=%"PRIu32", vnn=%"PRIu32", " + "opcode=%"PRIu32", srvid=%"PRIu64"\n", req.hdr.reqid, + req.hdr.destnode, req.opcode, req.srvid); + ctdb_packet_dump(&req.hdr); + + iov[0].iov_base = &req; + iov[0].iov_len = offsetof(struct ctdb_req_control_old, data); + iov[1].iov_base = data.dptr; + iov[1].iov_len = data.dsize; + + nwritten = write_data_iov(conn->fd, iov, ARRAY_SIZE(iov)); + if (nwritten == -1) { + DEBUG(3, ("write_data_iov failed: %s\n", strerror(errno))); + cluster_fatal("cluster dispatch daemon msg write error\n"); + } + + if (flags & CTDB_CTRL_FLAG_NOREPLY) { + if (cstatus) { + *cstatus = 0; + } + return 0; + } + + ret = ctdb_read_req(conn, req.hdr.reqid, NULL, &hdr); + if (ret != 0) { + DEBUG(10, ("ctdb_read_req failed: %s\n", strerror(ret))); + return ret; + } + + if (hdr->operation != CTDB_REPLY_CONTROL) { + DEBUG(0, ("received invalid reply\n")); + TALLOC_FREE(hdr); + return EIO; + } + reply = (struct ctdb_reply_control_old *)hdr; + + if (outdata) { + if (!(outdata->dptr = (uint8_t *)talloc_memdup( + mem_ctx, reply->data, reply->datalen))) { + TALLOC_FREE(reply); + return ENOMEM; + } + outdata->dsize = reply->datalen; + } + if (cstatus) { + (*cstatus) = reply->status; + } + + TALLOC_FREE(reply); + return ret; +} + +/* + * see if a remote process exists + */ +bool ctdbd_process_exists(struct ctdbd_connection *conn, uint32_t vnn, + pid_t pid, uint64_t unique_id) +{ + uint8_t buf[sizeof(pid)+sizeof(unique_id)]; + int32_t cstatus = 0; + int ret; + + if (unique_id == SERVERID_UNIQUE_ID_NOT_TO_VERIFY) { + ret = ctdbd_control(conn, vnn, CTDB_CONTROL_PROCESS_EXISTS, + 0, 0, + (TDB_DATA) { .dptr = (uint8_t *)&pid, + .dsize = sizeof(pid) }, + NULL, NULL, &cstatus); + if (ret != 0) { + return false; + } + return (cstatus == 0); + } + + memcpy(buf, &pid, sizeof(pid)); + memcpy(buf+sizeof(pid), &unique_id, sizeof(unique_id)); + + ret = ctdbd_control(conn, vnn, CTDB_CONTROL_CHECK_PID_SRVID, 0, 0, + (TDB_DATA) { .dptr = buf, .dsize = sizeof(buf) }, + NULL, NULL, &cstatus); + if (ret != 0) { + return false; + } + return (cstatus == 0); +} + +/* + * Get a db path + */ +char *ctdbd_dbpath(struct ctdbd_connection *conn, + TALLOC_CTX *mem_ctx, uint32_t db_id) +{ + int ret; + TDB_DATA data; + TDB_DATA rdata = {0}; + int32_t cstatus = 0; + + data.dptr = (uint8_t*)&db_id; + data.dsize = sizeof(db_id); + + ret = ctdbd_control_local(conn, CTDB_CONTROL_GETDBPATH, 0, 0, data, + mem_ctx, &rdata, &cstatus); + if ((ret != 0) || cstatus != 0) { + DEBUG(0, (__location__ " ctdb_control for getdbpath failed: %s\n", + strerror(ret))); + TALLOC_FREE(rdata.dptr); + } + + return (char *)rdata.dptr; +} + +/* + * attach to a ctdb database + */ +int ctdbd_db_attach(struct ctdbd_connection *conn, + const char *name, uint32_t *db_id, bool persistent) +{ + int ret; + TDB_DATA data = {0}; + int32_t cstatus; + + data = string_term_tdb_data(name); + + ret = ctdbd_control_local(conn, + persistent + ? CTDB_CONTROL_DB_ATTACH_PERSISTENT + : CTDB_CONTROL_DB_ATTACH, + 0, 0, data, NULL, &data, &cstatus); + if (ret != 0) { + DEBUG(0, (__location__ " ctdb_control for db_attach " + "failed: %s\n", strerror(ret))); + return ret; + } + + if (cstatus != 0 || data.dsize != sizeof(uint32_t)) { + DEBUG(0,(__location__ " ctdb_control for db_attach failed\n")); + TALLOC_FREE(data.dptr); + return EIO; + } + + *db_id = *(uint32_t *)data.dptr; + talloc_free(data.dptr); + + return 0; +} + +/* + * force the migration of a record to this node + */ +int ctdbd_migrate(struct ctdbd_connection *conn, uint32_t db_id, TDB_DATA key) +{ + struct ctdb_req_call_old req; + struct ctdb_req_header *hdr = NULL; + struct iovec iov[2]; + ssize_t nwritten; + int ret; + + if (ctdbd_conn_has_async_reqs(conn)) { + /* + * Can't use sync call while an async call is in flight. Adding + * this check as a safety net. We'll be using different + * connections for sync and async requests, so this shouldn't + * happen, but who knows... + */ + DBG_ERR("Async ctdb req on sync connection\n"); + return EINVAL; + } + + ZERO_STRUCT(req); + + req.hdr.length = offsetof(struct ctdb_req_call_old, data) + key.dsize; + req.hdr.ctdb_magic = CTDB_MAGIC; + req.hdr.ctdb_version = CTDB_PROTOCOL; + req.hdr.operation = CTDB_REQ_CALL; + req.hdr.reqid = ctdbd_next_reqid(conn); + req.flags = CTDB_IMMEDIATE_MIGRATION; + req.callid = CTDB_NULL_FUNC; + req.db_id = db_id; + req.keylen = key.dsize; + + DEBUG(10, ("ctdbd_migrate: Sending ctdb packet\n")); + ctdb_packet_dump(&req.hdr); + + iov[0].iov_base = &req; + iov[0].iov_len = offsetof(struct ctdb_req_call_old, data); + iov[1].iov_base = key.dptr; + iov[1].iov_len = key.dsize; + + nwritten = write_data_iov(conn->fd, iov, ARRAY_SIZE(iov)); + if (nwritten == -1) { + DEBUG(3, ("write_data_iov failed: %s\n", strerror(errno))); + cluster_fatal("cluster dispatch daemon msg write error\n"); + } + + ret = ctdb_read_req(conn, req.hdr.reqid, NULL, &hdr); + if (ret != 0) { + DEBUG(10, ("ctdb_read_req failed: %s\n", strerror(ret))); + goto fail; + } + + if (hdr->operation != CTDB_REPLY_CALL) { + if (hdr->operation == CTDB_REPLY_ERROR) { + DBG_ERR("received error from ctdb\n"); + } else { + DBG_ERR("received invalid reply\n"); + } + ret = EIO; + goto fail; + } + + fail: + + TALLOC_FREE(hdr); + return ret; +} + +/* + * Fetch a record and parse it + */ +int ctdbd_parse(struct ctdbd_connection *conn, uint32_t db_id, + TDB_DATA key, bool local_copy, + void (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + struct ctdb_req_call_old req; + struct ctdb_req_header *hdr = NULL; + struct ctdb_reply_call_old *reply; + struct iovec iov[2]; + ssize_t nwritten; + uint32_t flags; + int ret; + + if (ctdbd_conn_has_async_reqs(conn)) { + /* + * Can't use sync call while an async call is in flight. Adding + * this check as a safety net. We'll be using different + * connections for sync and async requests, so this shouldn't + * happen, but who knows... + */ + DBG_ERR("Async ctdb req on sync connection\n"); + return EINVAL; + } + + flags = local_copy ? CTDB_WANT_READONLY : 0; + + ZERO_STRUCT(req); + + req.hdr.length = offsetof(struct ctdb_req_call_old, data) + key.dsize; + req.hdr.ctdb_magic = CTDB_MAGIC; + req.hdr.ctdb_version = CTDB_PROTOCOL; + req.hdr.operation = CTDB_REQ_CALL; + req.hdr.reqid = ctdbd_next_reqid(conn); + req.flags = flags; + req.callid = CTDB_FETCH_FUNC; + req.db_id = db_id; + req.keylen = key.dsize; + + iov[0].iov_base = &req; + iov[0].iov_len = offsetof(struct ctdb_req_call_old, data); + iov[1].iov_base = key.dptr; + iov[1].iov_len = key.dsize; + + nwritten = write_data_iov(conn->fd, iov, ARRAY_SIZE(iov)); + if (nwritten == -1) { + DEBUG(3, ("write_data_iov failed: %s\n", strerror(errno))); + cluster_fatal("cluster dispatch daemon msg write error\n"); + } + + ret = ctdb_read_req(conn, req.hdr.reqid, NULL, &hdr); + if (ret != 0) { + DEBUG(10, ("ctdb_read_req failed: %s\n", strerror(ret))); + goto fail; + } + + if ((hdr == NULL) || (hdr->operation != CTDB_REPLY_CALL)) { + DEBUG(0, ("received invalid reply\n")); + ret = EIO; + goto fail; + } + reply = (struct ctdb_reply_call_old *)hdr; + + if (reply->datalen == 0) { + /* + * Treat an empty record as non-existing + */ + ret = ENOENT; + goto fail; + } + + parser(key, make_tdb_data(&reply->data[0], reply->datalen), + private_data); + + ret = 0; + fail: + TALLOC_FREE(hdr); + return ret; +} + +/* + Traverse a ctdb database. "conn" must be an otherwise unused + ctdb_connection where no other messages but the traverse ones are + expected. +*/ + +int ctdbd_traverse(struct ctdbd_connection *conn, uint32_t db_id, + void (*fn)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + int ret; + TDB_DATA key, data; + struct ctdb_traverse_start t; + int32_t cstatus = 0; + + if (ctdbd_conn_has_async_reqs(conn)) { + /* + * Can't use sync call while an async call is in flight. Adding + * this check as a safety net. We'll be using different + * connections for sync and async requests, so this shouldn't + * happen, but who knows... + */ + DBG_ERR("Async ctdb req on sync connection\n"); + return EINVAL; + } + + t.db_id = db_id; + t.srvid = conn->rand_srvid; + t.reqid = ctdbd_next_reqid(conn); + + data.dptr = (uint8_t *)&t; + data.dsize = sizeof(t); + + ret = ctdbd_control_local(conn, CTDB_CONTROL_TRAVERSE_START, + conn->rand_srvid, + 0, data, NULL, NULL, &cstatus); + + if ((ret != 0) || (cstatus != 0)) { + DEBUG(0,("ctdbd_control failed: %s, %d\n", strerror(ret), + cstatus)); + + if (ret == 0) { + /* + * We need a mapping here + */ + ret = EIO; + } + return ret; + } + + while (true) { + struct ctdb_req_header *hdr = NULL; + struct ctdb_req_message_old *m; + struct ctdb_rec_data_old *d; + + ret = ctdb_read_packet(conn->fd, conn->timeout, conn, &hdr); + if (ret != 0) { + DBG_ERR("ctdb_read_packet failed: %s\n", strerror(ret)); + cluster_fatal("failed to read data from ctdbd\n"); + } + SMB_ASSERT(hdr != NULL); + + if (hdr->operation != CTDB_REQ_MESSAGE) { + DEBUG(0, ("Got operation %u, expected a message\n", + (unsigned)hdr->operation)); + return EIO; + } + + m = (struct ctdb_req_message_old *)hdr; + d = (struct ctdb_rec_data_old *)&m->data[0]; + if (m->datalen < sizeof(uint32_t) || m->datalen != d->length) { + DEBUG(0, ("Got invalid traverse data of length %d\n", + (int)m->datalen)); + return EIO; + } + + key.dsize = d->keylen; + key.dptr = &d->data[0]; + data.dsize = d->datalen; + data.dptr = &d->data[d->keylen]; + + if (key.dsize == 0 && data.dsize == 0) { + /* end of traverse */ + return 0; + } + + if (data.dsize < sizeof(struct ctdb_ltdb_header)) { + DEBUG(0, ("Got invalid ltdb header length %d\n", + (int)data.dsize)); + return EIO; + } + data.dsize -= sizeof(struct ctdb_ltdb_header); + data.dptr += sizeof(struct ctdb_ltdb_header); + + if (fn != NULL) { + fn(key, data, private_data); + } + } + return 0; +} + +/* + This is used to canonicalize a ctdb_sock_addr structure. +*/ +static void smbd_ctdb_canonicalize_ip(const struct sockaddr_storage *in, + struct sockaddr_storage *out) +{ + memcpy(out, in, sizeof (*out)); + +#ifdef HAVE_IPV6 + if (in->ss_family == AF_INET6) { + const char prefix[12] = { 0,0,0,0,0,0,0,0,0,0,0xff,0xff }; + const struct sockaddr_in6 *in6 = + (const struct sockaddr_in6 *)in; + struct sockaddr_in *out4 = (struct sockaddr_in *)out; + if (memcmp(&in6->sin6_addr, prefix, 12) == 0) { + memset(out, 0, sizeof(*out)); +#ifdef HAVE_SOCK_SIN_LEN + out4->sin_len = sizeof(*out); +#endif + out4->sin_family = AF_INET; + out4->sin_port = in6->sin6_port; + memcpy(&out4->sin_addr, &in6->sin6_addr.s6_addr[12], 4); + } + } +#endif +} + +/* + * Register us as a server for a particular tcp connection + */ + +int ctdbd_register_ips(struct ctdbd_connection *conn, + const struct sockaddr_storage *_server, + const struct sockaddr_storage *_client, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, size_t msglen, + void *private_data), + void *private_data) +{ + struct ctdb_connection p; + TDB_DATA data = { .dptr = (uint8_t *)&p, .dsize = sizeof(p) }; + int ret; + struct sockaddr_storage client; + struct sockaddr_storage server; + + /* + * Only one connection so far + */ + + smbd_ctdb_canonicalize_ip(_client, &client); + smbd_ctdb_canonicalize_ip(_server, &server); + + ZERO_STRUCT(p); + switch (client.ss_family) { + case AF_INET: + memcpy(&p.dst.ip, &server, sizeof(p.dst.ip)); + memcpy(&p.src.ip, &client, sizeof(p.src.ip)); + break; + case AF_INET6: + memcpy(&p.dst.ip6, &server, sizeof(p.dst.ip6)); + memcpy(&p.src.ip6, &client, sizeof(p.src.ip6)); + break; + default: + return EIO; + } + + /* + * We want to be told about IP releases + */ + + ret = register_with_ctdbd(conn, CTDB_SRVID_RELEASE_IP, + cb, private_data); + if (ret != 0) { + return ret; + } + + /* + * inform ctdb of our tcp connection, so if IP takeover happens ctdb + * can send an extra ack to trigger a reset for our client, so it + * immediately reconnects + */ + ret = ctdbd_control_local(conn, + CTDB_CONTROL_TCP_CLIENT, 0, + CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL, + NULL); + if (ret != 0) { + return ret; + } + return 0; +} + +void ctdbd_unregister_ips(struct ctdbd_connection *conn, + const struct sockaddr_storage *_server, + const struct sockaddr_storage *_client, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, + uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, + size_t msglen, + void *private_data), + void *private_data) +{ + struct ctdb_connection p; + TDB_DATA data = { .dptr = (uint8_t *)&p, .dsize = sizeof(p) }; + int ret; + struct sockaddr_storage client; + struct sockaddr_storage server; + + /* + * Only one connection so far + */ + + smbd_ctdb_canonicalize_ip(_client, &client); + smbd_ctdb_canonicalize_ip(_server, &server); + + ZERO_STRUCT(p); + switch (client.ss_family) { + case AF_INET: + memcpy(&p.dst.ip, &server, sizeof(p.dst.ip)); + memcpy(&p.src.ip, &client, sizeof(p.src.ip)); + break; + case AF_INET6: + memcpy(&p.dst.ip6, &server, sizeof(p.dst.ip6)); + memcpy(&p.src.ip6, &client, sizeof(p.src.ip6)); + break; + default: + return; + } + + /* + * We no longer want to be told about IP releases + * for the given callback/private_data combination + */ + deregister_from_ctdbd(conn, CTDB_SRVID_RELEASE_IP, + cb, private_data); + + /* + * inform ctdb of our tcp connection is no longer active + */ + ret = ctdbd_control_local(conn, + CTDB_CONTROL_TCP_CLIENT_DISCONNECTED, 0, + CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL, + NULL); + if (ret != 0) { + /* + * We ignore errors here, as we'll just + * no longer have a callback handler + * registered and messages may just be ignored + */ + } + + return; +} + +void ctdbd_passed_ips(struct ctdbd_connection *conn, + const struct sockaddr_storage *_server, + const struct sockaddr_storage *_client, + int (*cb)(struct tevent_context *ev, + uint32_t src_vnn, + uint32_t dst_vnn, + uint64_t dst_srvid, + const uint8_t *msg, + size_t msglen, + void *private_data), + void *private_data) +{ + struct ctdb_connection p; + TDB_DATA data = { .dptr = (uint8_t *)&p, .dsize = sizeof(p) }; + int ret; + struct sockaddr_storage client; + struct sockaddr_storage server; + + /* + * Only one connection so far + */ + + smbd_ctdb_canonicalize_ip(_client, &client); + smbd_ctdb_canonicalize_ip(_server, &server); + + ZERO_STRUCT(p); + switch (client.ss_family) { + case AF_INET: + memcpy(&p.dst.ip, &server, sizeof(p.dst.ip)); + memcpy(&p.src.ip, &client, sizeof(p.src.ip)); + break; + case AF_INET6: + memcpy(&p.dst.ip6, &server, sizeof(p.dst.ip6)); + memcpy(&p.src.ip6, &client, sizeof(p.src.ip6)); + break; + default: + return; + } + + /* + * We no longer want to be told about IP releases + * for the given callback/private_data combination + */ + deregister_from_ctdbd(conn, CTDB_SRVID_RELEASE_IP, + cb, private_data); + + /* + * inform ctdb of our tcp connection is now passed to + * another process. + */ + ret = ctdbd_control_local(conn, + CTDB_CONTROL_TCP_CLIENT_PASSED, 0, + CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL, + NULL); + if (ret != 0) { + /* + * We ignore errors here, as we'll just + * no longer have a callback handler + * registered and messages may just be ignored + */ + } + + return; +} + +static int ctdbd_control_get_public_ips(struct ctdbd_connection *conn, + uint32_t vnn, + uint32_t flags, + TALLOC_CTX *mem_ctx, + struct ctdb_public_ip_list_old **_ips) +{ + struct ctdb_public_ip_list_old *ips = NULL; + TDB_DATA outdata; + int32_t cstatus = -1; + size_t min_dsize; + size_t max_ips; + int ret; + + *_ips = NULL; + + ret = ctdbd_control(conn, + vnn, + CTDB_CONTROL_GET_PUBLIC_IPS, + 0, /* srvid */ + flags, + tdb_null, /* indata */ + mem_ctx, + &outdata, + &cstatus); + if (ret != 0 || cstatus != 0) { + DBG_ERR("ctdb_control for getpublicips failed ret:%d cstatus:%d\n", + ret, (int)cstatus); + return -1; + } + + min_dsize = offsetof(struct ctdb_public_ip_list_old, ips); + if (outdata.dsize < min_dsize) { + DBG_ERR("outdata.dsize=%zu < min_dsize=%zu\n", + outdata.dsize, min_dsize); + return -1; + } + max_ips = (outdata.dsize - min_dsize)/sizeof(struct ctdb_public_ip); + ips = (struct ctdb_public_ip_list_old *)outdata.dptr; + if ((size_t)ips->num > max_ips) { + DBG_ERR("ips->num=%zu > max_ips=%zu\n", + (size_t)ips->num, max_ips); + return -1; + } + + *_ips = ips; + return 0; +} + +static struct samba_sockaddr ctdbd_sock_addr_to_samba(const ctdb_sock_addr *c) +{ + struct samba_sockaddr s = {}; + + switch (c->sa.sa_family) { + case AF_INET: + s.u.in = c->ip; + break; + case AF_INET6: + /* + * ctdb always requires HAVE_IPV6, + * so we don't need an ifdef here. + */ + s.u.in6 = c->ip6; + break; + default: + /* + * ctdb_sock_addr only supports ipv4 and ipv6 + */ + smb_panic(__location__); + break; + } + + return s; +} + +int ctdbd_public_ip_foreach(struct ctdbd_connection *conn, + int (*cb)(uint32_t total_ip_count, + const struct sockaddr_storage *ip, + bool is_movable_ip, + void *private_data), + void *private_data) +{ + uint32_t i; + struct ctdb_public_ip_list_old *ips = NULL; + int ret = ENOMEM; + TALLOC_CTX *frame = talloc_stackframe(); + + ret = ctdbd_control_get_public_ips(conn, CTDB_CURRENT_NODE, 0, frame, &ips); + if (ret < 0) { + ret = EIO; + goto out_free; + } + + for (i=0; i < ips->num; i++) { + const ctdb_sock_addr *addr = &ips->ips[i].addr; + struct samba_sockaddr tmp = ctdbd_sock_addr_to_samba(addr); + + ret = cb(ips->num, + &tmp.u.ss, + true, /* all ctdb public ips are movable */ + private_data); + if (ret != 0) { + goto out_free; + } + } + + ret = 0; +out_free: + TALLOC_FREE(frame); + return ret; +} + +static int count_ips(struct db_record *rec, void *private_data) +{ + return 0; +} + +static int collect_ips(struct db_record *rec, void *private_data) +{ + struct ctdb_public_ip_list_old *ips = talloc_get_type_abort( + private_data, struct ctdb_public_ip_list_old); + struct ctdb_public_ip *ip; + TDB_DATA val = dbwrap_record_get_value(rec); + + SMB_ASSERT(val.dsize == sizeof(*ip)); + + ip = (struct ctdb_public_ip *)val.dptr; + ips->ips[ips->num] = *ip; + ips->num += 1; + + return 0; +} + +static int ctdbd_control_get_all_public_ips(struct ctdbd_connection *conn, + const struct ctdb_node_map_old *nodemap, + TALLOC_CTX *mem_ctx, + struct ctdb_public_ip_list_old **_ips) +{ + TALLOC_CTX *frame = talloc_stackframe(); + uint32_t ni; + struct ctdb_public_ip_list_old *ips = NULL; + struct db_context *rbt = NULL; + NTSTATUS status; + size_t len; + int ret; + int count; + + rbt = db_open_rbt(frame); + if (rbt == NULL) { + DBG_WARNING("db_open_rbt() failed\n"); + TALLOC_FREE(frame); + return -1; + } + + for (ni=0; ni < nodemap->num; ni++) { + const struct ctdb_node_and_flags *n = &nodemap->nodes[ni]; + uint32_t j; + + if (n->flags & NODE_FLAGS_INACTIVE) { + continue; + } + + ret = ctdbd_control_get_public_ips(conn, + n->pnn, + 0, + frame, + &ips); + if (ret != 0) { + TALLOC_FREE(frame); + return -1; + } + + for (j=0; j<ips->num; j++) { + struct ctdb_public_ip ip; + TDB_DATA key; + TDB_DATA val; + + ip.pnn = ips->ips[j].pnn; + ip.addr = ips->ips[j].addr; + + key = make_tdb_data((uint8_t *)&ip.addr, sizeof(ip.addr)); + val = make_tdb_data((uint8_t *)&ip, sizeof(ip)); + + if (n->pnn == ip.pnn) { + /* + * Node claims IP is hosted on it, so + * save that information + */ + status = dbwrap_store(rbt, key, val, + TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return -1; + } + } else { + /* + * Node thinks IP is hosted elsewhere, + * so overwrite with CTDB_UNKNOWN_PNN + * if there's no existing entry + */ + bool exists = dbwrap_exists(rbt, key); + if (!exists) { + ip.pnn = CTDB_UNKNOWN_PNN; + status = dbwrap_store(rbt, key, val, + TDB_INSERT); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return -1; + } + } + } + } + + TALLOC_FREE(ips); + } + + status = dbwrap_traverse_read(rbt, count_ips, NULL, &count); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return -1; + } + + len = offsetof(struct ctdb_public_ip_list_old, ips) + + count*sizeof(struct ctdb_public_ip); + ips = talloc_zero_size(mem_ctx, len); + if (ips == NULL) { + TALLOC_FREE(frame); + return -1; + } + talloc_set_type(ips, struct ctdb_public_ip_list_old); + talloc_reparent(mem_ctx, frame, ips); + + status = dbwrap_traverse_read(rbt, collect_ips, ips, &count); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return -1; + } + + if ((unsigned int)count != ips->num) { + TALLOC_FREE(frame); + return -1; + } + + *_ips = talloc_move(mem_ctx, &ips); + TALLOC_FREE(frame); + return 0; +} + +/* + * This includes all node and/or public ips + * of the whole cluster. + * + * node ips have: + * - a valid pinned_pnn value. + * - current_pnn is valid if the node is healthy + * + * public ips have: + * - pinned_pnn as CTDB_UNKNOWN_PNN + * - current_pnn is valid if a node healthy and hosting this ip. + */ +int ctdbd_all_ip_foreach(struct ctdbd_connection *conn, + bool include_node_ips, + bool include_public_ips, + int (*cb)(uint32_t total_ip_count, + const struct sockaddr_storage *ip, + uint32_t pinned_pnn, + uint32_t current_pnn, + void *private_data), + void *private_data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct ctdb_node_map_old *nodemap = NULL; + struct ctdb_public_ip_list_old *ips = NULL; + int ret = ENOMEM; + uint32_t total_ip_count = 0; + uint32_t i; + + ret = ctdbd_control_get_nodemap(conn, frame, &nodemap); + if (ret != 0) { + DBG_WARNING("ctdbd_control_get_nodemap() failed: %s\n", strerror(ret)); + TALLOC_FREE(frame); + return -1; + } + + for (i=0; include_node_ips && i < nodemap->num; i++) { + const struct ctdb_node_and_flags *n = &nodemap->nodes[i]; + + if (n->flags & NODE_FLAGS_DELETED) { + continue; + } + + total_ip_count += 1; + } + + if (include_public_ips) { + ret = ctdbd_control_get_all_public_ips(conn, nodemap, + frame, &ips); + if (ret < 0) { + ret = EIO; + goto out_free; + } + + total_ip_count += ips->num; + } + + for (i=0; include_node_ips && i < nodemap->num; i++) { + const struct ctdb_node_and_flags *n = &nodemap->nodes[i]; + struct samba_sockaddr tmp = ctdbd_sock_addr_to_samba(&n->addr); + uint32_t pinned_pnn = n->pnn; + uint32_t current_pnn = n->pnn; + + if (n->flags & NODE_FLAGS_DELETED) { + continue; + } + + if (n->flags & (NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED)) { + /* + * The ip address is not available + * unless the node is up and + * healthy. + */ + current_pnn = CTDB_UNKNOWN_PNN; + } + + ret = cb(total_ip_count, + &tmp.u.ss, + pinned_pnn, + current_pnn, + private_data); + if (ret != 0) { + goto out_free; + } + } + + for (i=0; include_public_ips && i < ips->num; i++) { + const ctdb_sock_addr *addr = &ips->ips[i].addr; + struct samba_sockaddr tmp = ctdbd_sock_addr_to_samba(addr); + /* all ctdb public ips are movable and not pinned */ + uint32_t pinned_pnn = CTDB_UNKNOWN_PNN; + uint32_t current_pnn = ips->ips[i].pnn; + uint32_t ni; + + for (ni=0; ni < nodemap->num; ni++) { + const struct ctdb_node_and_flags *n = &nodemap->nodes[ni]; + + if (n->pnn != current_pnn) { + continue; + } + + if (n->flags & (NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED)) { + current_pnn = CTDB_UNKNOWN_PNN; + } + break; + } + + ret = cb(total_ip_count, + &tmp.u.ss, + pinned_pnn, + current_pnn, + private_data); + if (ret != 0) { + goto out_free; + } + } + + ret = 0; +out_free: + TALLOC_FREE(frame); + return ret; +} + +/* + call a control on the local node + */ +int ctdbd_control_local(struct ctdbd_connection *conn, uint32_t opcode, + uint64_t srvid, uint32_t flags, TDB_DATA data, + TALLOC_CTX *mem_ctx, TDB_DATA *outdata, + int32_t *cstatus) +{ + return ctdbd_control(conn, CTDB_CURRENT_NODE, opcode, srvid, flags, data, + mem_ctx, outdata, cstatus); +} + +int ctdb_watch_us(struct ctdbd_connection *conn) +{ + struct ctdb_notify_data_old reg_data; + size_t struct_len; + int ret; + int32_t cstatus; + + reg_data.srvid = CTDB_SRVID_SAMBA_NOTIFY; + reg_data.len = 1; + reg_data.notify_data[0] = 0; + + struct_len = offsetof(struct ctdb_notify_data_old, + notify_data) + reg_data.len; + + ret = ctdbd_control_local( + conn, CTDB_CONTROL_REGISTER_NOTIFY, conn->rand_srvid, 0, + make_tdb_data((uint8_t *)®_data, struct_len), + NULL, NULL, &cstatus); + if (ret != 0) { + DEBUG(1, ("ctdbd_control_local failed: %s\n", + strerror(ret))); + } + return ret; +} + +int ctdb_unwatch(struct ctdbd_connection *conn) +{ + uint64_t srvid = CTDB_SRVID_SAMBA_NOTIFY; + int ret; + int32_t cstatus; + + ret = ctdbd_control_local( + conn, CTDB_CONTROL_DEREGISTER_NOTIFY, conn->rand_srvid, 0, + make_tdb_data((uint8_t *)&srvid, sizeof(srvid)), + NULL, NULL, &cstatus); + if (ret != 0) { + DEBUG(1, ("ctdbd_control_local failed: %s\n", + strerror(ret))); + } + return ret; +} + +int ctdbd_probe(const char *sockname, int timeout) +{ + /* + * Do a very early check if ctdbd is around to avoid an abort and core + * later + */ + struct ctdbd_connection *conn = NULL; + int ret; + + ret = ctdbd_init_connection(talloc_tos(), sockname, timeout, + &conn); + + /* + * We only care if we can connect. + */ + TALLOC_FREE(conn); + + return ret; +} + +static int ctdbd_connection_destructor(struct ctdbd_connection *c) +{ + if (c->fd != -1) { + close(c->fd); + c->fd = -1; + } + return 0; +} + +void ctdbd_prep_hdr_next_reqid( + struct ctdbd_connection *conn, struct ctdb_req_header *hdr) +{ + *hdr = (struct ctdb_req_header) { + .ctdb_magic = CTDB_MAGIC, + .ctdb_version = CTDB_PROTOCOL, + .reqid = ctdbd_next_reqid(conn), + .destnode = CTDB_CURRENT_NODE, + }; +} + +struct ctdbd_pkt_read_state { + uint8_t *pkt; +}; + +static ssize_t ctdbd_pkt_read_more( + uint8_t *buf, size_t buflen, void *private_data); +static void ctdbd_pkt_read_done(struct tevent_req *subreq); + +static struct tevent_req *ctdbd_pkt_read_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, int fd) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct ctdbd_pkt_read_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct ctdbd_pkt_read_state); + if (req == NULL) { + return NULL; + } + subreq = read_packet_send(state, ev, fd, 4, ctdbd_pkt_read_more, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, ctdbd_pkt_read_done, req); + return req; +} + +static ssize_t ctdbd_pkt_read_more( + uint8_t *buf, size_t buflen, void *private_data) +{ + uint32_t msglen; + if (buflen < 4) { + return -1; + } + if (buflen > 4) { + return 0; /* Been here, done */ + } + memcpy(&msglen, buf, 4); + + if (msglen < sizeof(struct ctdb_req_header)) { + return -1; + } + return msglen - sizeof(msglen); +} + +static void ctdbd_pkt_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct ctdbd_pkt_read_state *state = tevent_req_data( + req, struct ctdbd_pkt_read_state); + ssize_t nread; + int err; + + nread = read_packet_recv(subreq, state, &state->pkt, &err); + TALLOC_FREE(subreq); + if (nread == -1) { + tevent_req_error(req, err); + return; + } + tevent_req_done(req); +} + +static int ctdbd_pkt_read_recv( + struct tevent_req *req, TALLOC_CTX *mem_ctx, uint8_t **pkt) +{ + struct ctdbd_pkt_read_state *state = tevent_req_data( + req, struct ctdbd_pkt_read_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + return err; + } + *pkt = talloc_move(mem_ctx, &state->pkt); + tevent_req_received(req); + return 0; +} + +static bool ctdbd_conn_receive_next(struct ctdbd_connection *conn); +static void ctdbd_conn_received(struct tevent_req *subreq); + +struct ctdbd_req_state { + struct ctdbd_connection *conn; + struct tevent_context *ev; + uint32_t reqid; + struct ctdb_req_header *reply; +}; + +static void ctdbd_req_unset_pending(struct tevent_req *req) +{ + struct ctdbd_req_state *state = tevent_req_data( + req, struct ctdbd_req_state); + struct ctdbd_connection *conn = state->conn; + size_t num_pending = talloc_array_length(conn->pending); + size_t i, num_after; + + tevent_req_set_cleanup_fn(req, NULL); + + if (num_pending == 1) { + /* + * conn->read_req is a child of conn->pending + */ + TALLOC_FREE(conn->pending); + conn->read_req = NULL; + return; + } + + for (i=0; i<num_pending; i++) { + if (req == conn->pending[i]) { + break; + } + } + if (i == num_pending) { + /* + * Something's seriously broken. Just returning here is the + * right thing nevertheless, the point of this routine is to + * remove ourselves from conn->pending. + */ + return; + } + + num_after = num_pending - i - 1; + if (num_after > 0) { + memmove(&conn->pending[i], + &conn->pending[i] + 1, + sizeof(*conn->pending) * num_after); + } + conn->pending = talloc_realloc( + NULL, conn->pending, struct tevent_req *, num_pending - 1); +} + +static void ctdbd_req_cleanup( + struct tevent_req *req, enum tevent_req_state req_state) +{ + ctdbd_req_unset_pending(req); +} + +static bool ctdbd_req_set_pending(struct tevent_req *req) +{ + struct ctdbd_req_state *state = tevent_req_data( + req, struct ctdbd_req_state); + struct ctdbd_connection *conn = state->conn; + struct tevent_req **pending = NULL; + size_t num_pending = talloc_array_length(conn->pending); + bool ok; + + pending = talloc_realloc( + conn, conn->pending, struct tevent_req *, num_pending + 1); + if (pending == NULL) { + return false; + } + pending[num_pending] = req; + conn->pending = pending; + + tevent_req_set_cleanup_fn(req, ctdbd_req_cleanup); + + ok = ctdbd_conn_receive_next(conn); + if (!ok) { + ctdbd_req_unset_pending(req); + return false; + } + + return true; +} + +static bool ctdbd_conn_receive_next(struct ctdbd_connection *conn) +{ + size_t num_pending = talloc_array_length(conn->pending); + struct tevent_req *req = NULL; + struct ctdbd_req_state *state = NULL; + + if (conn->read_req != NULL) { + return true; + } + if (num_pending == 0) { + /* + * done for now + */ + return true; + } + + req = conn->pending[0]; + state = tevent_req_data(req, struct ctdbd_req_state); + + conn->read_req = ctdbd_pkt_read_send( + conn->pending, state->ev, conn->fd); + if (conn->read_req == NULL) { + return false; + } + tevent_req_set_callback(conn->read_req, ctdbd_conn_received, conn); + return true; +} + +static void ctdbd_conn_received(struct tevent_req *subreq) +{ + struct ctdbd_connection *conn = tevent_req_callback_data( + subreq, struct ctdbd_connection); + TALLOC_CTX *frame = talloc_stackframe(); + uint8_t *pkt = NULL; + int ret; + struct ctdb_req_header *hdr = NULL; + uint32_t reqid; + struct tevent_req *req = NULL; + struct ctdbd_req_state *state = NULL; + size_t i, num_pending; + bool ok; + + SMB_ASSERT(subreq == conn->read_req); + conn->read_req = NULL; + + ret = ctdbd_pkt_read_recv(subreq, frame, &pkt); + TALLOC_FREE(subreq); + if (ret != 0) { + cluster_fatal("ctdbd_pkt_read failed\n"); + } + + hdr = (struct ctdb_req_header *)pkt; + reqid = hdr->reqid; + num_pending = talloc_array_length(conn->pending); + + for (i=0; i<num_pending; i++) { + req = conn->pending[i]; + state = tevent_req_data(req, struct ctdbd_req_state); + if (state->reqid == reqid) { + break; + } + } + + if (i == num_pending) { + /* not found */ + TALLOC_FREE(frame); + return; + } + + state->reply = talloc_move(state, &hdr); + tevent_req_defer_callback(req, state->ev); + tevent_req_done(req); + + TALLOC_FREE(frame); + + ok = ctdbd_conn_receive_next(conn); + if (!ok) { + cluster_fatal("ctdbd_conn_receive_next failed\n"); + } +} + +static void ctdbd_req_written(struct tevent_req *subreq); + +struct tevent_req *ctdbd_req_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdbd_connection *conn, + struct iovec *iov, + size_t num_iov) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct ctdbd_req_state *state = NULL; + struct ctdb_req_header *hdr = NULL; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct ctdbd_req_state); + if (req == NULL) { + return NULL; + } + state->conn = conn; + state->ev = ev; + + if ((num_iov == 0) || + (iov[0].iov_len < sizeof(struct ctdb_req_header))) { + tevent_req_error(req, EINVAL); + return tevent_req_post(req, ev); + } + hdr = iov[0].iov_base; + state->reqid = hdr->reqid; + + ok = ctdbd_req_set_pending(req); + if (!ok) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + + subreq = writev_send( + state, ev, conn->outgoing, conn->fd, false, iov, num_iov); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, ctdbd_req_written, req); + + return req; +} + +static void ctdbd_req_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + ssize_t nwritten; + int err; + + nwritten = writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (nwritten == -1) { + tevent_req_error(req, err); + return; + } +} + +int ctdbd_req_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ctdb_req_header **reply) +{ + struct ctdbd_req_state *state = tevent_req_data( + req, struct ctdbd_req_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + return err; + } + *reply = talloc_move(mem_ctx, &state->reply); + tevent_req_received(req); + return 0; +} + +struct ctdbd_parse_state { + struct tevent_context *ev; + struct ctdbd_connection *conn; + uint32_t reqid; + TDB_DATA key; + uint8_t _keybuf[64]; + struct ctdb_req_call_old ctdb_req; + struct iovec iov[2]; + void (*parser)(TDB_DATA key, + TDB_DATA data, + void *private_data); + void *private_data; +}; + +static void ctdbd_parse_done(struct tevent_req *subreq); + +struct tevent_req *ctdbd_parse_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ctdbd_connection *conn, + uint32_t db_id, + TDB_DATA key, + bool local_copy, + void (*parser)(TDB_DATA key, + TDB_DATA data, + void *private_data), + void *private_data, + enum dbwrap_req_state *req_state) +{ + struct tevent_req *req = NULL; + struct ctdbd_parse_state *state = NULL; + uint32_t flags; + uint32_t packet_length; + struct tevent_req *subreq = NULL; + + req = tevent_req_create(mem_ctx, &state, struct ctdbd_parse_state); + if (req == NULL) { + *req_state = DBWRAP_REQ_ERROR; + return NULL; + } + + *req_state = DBWRAP_REQ_DISPATCHED; + + *state = (struct ctdbd_parse_state) { + .ev = ev, + .conn = conn, + .reqid = ctdbd_next_reqid(conn), + .parser = parser, + .private_data = private_data, + }; + + flags = local_copy ? CTDB_WANT_READONLY : 0; + packet_length = offsetof(struct ctdb_req_call_old, data) + key.dsize; + + /* + * Copy the key into our state, as ctdb_pkt_send_cleanup() requires that + * all passed iov elements have a lifetime longer that the tevent_req + * returned by ctdb_pkt_send_send(). This is required continue sending a + * the low level request into the ctdb socket, if a higher level + * ('this') request is canceled (or talloc free'd) by the application + * layer, without sending invalid packets to ctdb. + */ + if (key.dsize > sizeof(state->_keybuf)) { + state->key.dptr = talloc_memdup(state, key.dptr, key.dsize); + if (tevent_req_nomem(state->key.dptr, req)) { + return tevent_req_post(req, ev); + } + } else { + memcpy(state->_keybuf, key.dptr, key.dsize); + state->key.dptr = state->_keybuf; + } + state->key.dsize = key.dsize; + + state->ctdb_req.hdr.length = packet_length; + state->ctdb_req.hdr.ctdb_magic = CTDB_MAGIC; + state->ctdb_req.hdr.ctdb_version = CTDB_PROTOCOL; + state->ctdb_req.hdr.operation = CTDB_REQ_CALL; + state->ctdb_req.hdr.reqid = state->reqid; + state->ctdb_req.flags = flags; + state->ctdb_req.callid = CTDB_FETCH_FUNC; + state->ctdb_req.db_id = db_id; + state->ctdb_req.keylen = state->key.dsize; + + state->iov[0].iov_base = &state->ctdb_req; + state->iov[0].iov_len = offsetof(struct ctdb_req_call_old, data); + state->iov[1].iov_base = state->key.dptr; + state->iov[1].iov_len = state->key.dsize; + + subreq = ctdbd_req_send( + state, ev, conn, state->iov, ARRAY_SIZE(state->iov)); + if (tevent_req_nomem(subreq, req)) { + *req_state = DBWRAP_REQ_ERROR; + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, ctdbd_parse_done, req); + + return req; +} + +static void ctdbd_parse_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct ctdbd_parse_state *state = tevent_req_data( + req, struct ctdbd_parse_state); + struct ctdb_req_header *hdr = NULL; + struct ctdb_reply_call_old *reply = NULL; + int ret; + + ret = ctdbd_req_recv(subreq, state, &hdr); + TALLOC_FREE(subreq); + if (tevent_req_error(req, ret)) { + DBG_DEBUG("ctdb_req_recv failed %s\n", strerror(ret)); + return; + } + SMB_ASSERT(hdr != NULL); + + if (hdr->operation != CTDB_REPLY_CALL) { + DBG_ERR("received invalid reply\n"); + ctdb_packet_dump(hdr); + tevent_req_error(req, EIO); + return; + } + + reply = (struct ctdb_reply_call_old *)hdr; + + if (reply->datalen == 0) { + /* + * Treat an empty record as non-existing + */ + tevent_req_error(req, ENOENT); + return; + } + + state->parser(state->key, + make_tdb_data(&reply->data[0], reply->datalen), + state->private_data); + + tevent_req_done(req); + return; +} + +int ctdbd_parse_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_unix(req); +} diff --git a/source3/lib/dbwrap/dbwrap_ctdb.c b/source3/lib/dbwrap/dbwrap_ctdb.c new file mode 100644 index 0000000..46165e8 --- /dev/null +++ b/source3/lib/dbwrap/dbwrap_ctdb.c @@ -0,0 +1,1985 @@ +/* + Unix SMB/CIFS implementation. + Database interface wrapper around ctdbd + Copyright (C) Volker Lendecke 2007-2009 + Copyright (C) Michael Adam 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include "util_tdb.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_ctdb.h" +#include "dbwrap/dbwrap_rbt.h" +#include "lib/param/param.h" + +#include "ctdb/include/ctdb_protocol.h" +#include "ctdbd_conn.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_private.h" +#include "dbwrap/dbwrap_ctdb.h" +#include "g_lock.h" +#include "messages.h" +#include "messages_ctdb.h" +#include "lib/cluster_support.h" +#include "lib/util/tevent_ntstatus.h" + +struct db_ctdb_transaction_handle { + struct db_ctdb_ctx *ctx; + /* + * we store the writes done under a transaction: + */ + struct ctdb_marshall_buffer *m_write; + uint32_t nesting; + bool nested_cancel; + char *lock_name; +}; + +struct db_ctdb_ctx { + struct db_context *db; + struct tdb_wrap *wtdb; + uint32_t db_id; + struct db_ctdb_transaction_handle *transaction; + struct g_lock_ctx *lock_ctx; + + /* thresholds for warning messages */ + int warn_unlock_msecs; + int warn_migrate_msecs; + int warn_migrate_attempts; + int warn_locktime_msecs; +}; + +struct db_ctdb_rec { + struct db_ctdb_ctx *ctdb_ctx; + struct ctdb_ltdb_header header; + struct timeval lock_time; +}; + +struct ctdb_async_ctx { + bool initialized; + struct ctdbd_connection *async_conn; +}; + +static struct ctdb_async_ctx ctdb_async_ctx; + +static int ctdb_async_ctx_init_internal(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + bool reinit) +{ + int ret; + + if (reinit) { + TALLOC_FREE(ctdb_async_ctx.async_conn); + ctdb_async_ctx.initialized = false; + } + + if (ctdb_async_ctx.initialized) { + return 0; + } + + become_root(); + ret = ctdbd_init_async_connection( + mem_ctx, + lp_ctdbd_socket(), + lp_ctdb_timeout(), + &ctdb_async_ctx.async_conn); + unbecome_root(); + + if (ret != 0) { + DBG_ERR("ctdbd_init_async_connection(%s, timeout=%d) " + "failed: ret=%d %s\n", + lp_ctdbd_socket(), + lp_ctdb_timeout(), + ret, strerror(ret)); + return ret; + } + + SMB_ASSERT(ctdb_async_ctx.async_conn != NULL); + + ctdb_async_ctx.initialized = true; + return 0; +} + +static int ctdb_async_ctx_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev) +{ + return ctdb_async_ctx_init_internal(mem_ctx, ev, false); +} + +int ctdb_async_ctx_reinit(TALLOC_CTX *mem_ctx, struct tevent_context *ev) +{ + return ctdb_async_ctx_init_internal(mem_ctx, ev, true); +} + +static NTSTATUS tdb_error_to_ntstatus(struct tdb_context *tdb) +{ + enum TDB_ERROR tret = tdb_error(tdb); + + return map_nt_error_from_tdb(tret); +} + +struct db_ctdb_ltdb_parse_state { + void (*parser)(TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data); + void *private_data; +}; + +static int db_ctdb_ltdb_parser(TDB_DATA key, TDB_DATA data, + void *private_data) +{ + struct db_ctdb_ltdb_parse_state *state = + (struct db_ctdb_ltdb_parse_state *)private_data; + + if (data.dsize < sizeof(struct ctdb_ltdb_header)) { + return -1; + } + + state->parser( + key, (struct ctdb_ltdb_header *)data.dptr, + make_tdb_data(data.dptr + sizeof(struct ctdb_ltdb_header), + data.dsize - sizeof(struct ctdb_ltdb_header)), + state->private_data); + return 0; +} + +static NTSTATUS db_ctdb_ltdb_parse( + struct db_ctdb_ctx *db, TDB_DATA key, + void (*parser)(TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data), + void *private_data) +{ + struct db_ctdb_ltdb_parse_state state; + int ret; + + state.parser = parser; + state.private_data = private_data; + + ret = tdb_parse_record(db->wtdb->tdb, key, db_ctdb_ltdb_parser, + &state); + if (ret == -1) { + return NT_STATUS_NOT_FOUND; + } + return NT_STATUS_OK; +} + +/* + * Store a record together with the ctdb record header + * in the local copy of the database. + */ +static NTSTATUS db_ctdb_ltdb_store(struct db_ctdb_ctx *db, + TDB_DATA key, + struct ctdb_ltdb_header *header, + const TDB_DATA *dbufs, int num_dbufs) +{ + TDB_DATA recs[num_dbufs+1]; + int ret; + + recs[0] = (TDB_DATA) { .dptr = (uint8_t *)header, + .dsize = sizeof(struct ctdb_ltdb_header) }; + memcpy(&recs[1], dbufs, sizeof(TDB_DATA) * num_dbufs); + + ret = tdb_storev(db->wtdb->tdb, key, recs, num_dbufs + 1, TDB_REPLACE); + + return (ret == 0) ? NT_STATUS_OK + : tdb_error_to_ntstatus(db->wtdb->tdb); + +} + +/* + form a ctdb_rec_data record from a key/data pair + */ +static struct ctdb_rec_data_old *db_ctdb_marshall_record(TALLOC_CTX *mem_ctx, uint32_t reqid, + TDB_DATA key, + struct ctdb_ltdb_header *header, + TDB_DATA data) +{ + size_t length; + struct ctdb_rec_data_old *d; + + length = offsetof(struct ctdb_rec_data_old, data) + key.dsize + + data.dsize + sizeof(*header); + d = (struct ctdb_rec_data_old *)talloc_size(mem_ctx, length); + if (d == NULL) { + return NULL; + } + d->length = length; + d->reqid = reqid; + d->keylen = key.dsize; + memcpy(&d->data[0], key.dptr, key.dsize); + + d->datalen = data.dsize + sizeof(*header); + memcpy(&d->data[key.dsize], header, sizeof(*header)); + memcpy(&d->data[key.dsize+sizeof(*header)], data.dptr, data.dsize); + return d; +} + + +/* helper function for marshalling multiple records */ +static struct ctdb_marshall_buffer *db_ctdb_marshall_add(TALLOC_CTX *mem_ctx, + struct ctdb_marshall_buffer *m, + uint32_t db_id, + uint32_t reqid, + TDB_DATA key, + struct ctdb_ltdb_header *header, + TDB_DATA data) +{ + struct ctdb_rec_data_old *r; + size_t m_size, r_size; + struct ctdb_marshall_buffer *m2 = NULL; + + r = db_ctdb_marshall_record(talloc_tos(), reqid, key, header, data); + if (r == NULL) { + talloc_free(m); + return NULL; + } + + if (m == NULL) { + m = (struct ctdb_marshall_buffer *)talloc_zero_size( + mem_ctx, offsetof(struct ctdb_marshall_buffer, data)); + if (m == NULL) { + goto done; + } + m->db_id = db_id; + } + + m_size = talloc_get_size(m); + r_size = talloc_get_size(r); + + m2 = (struct ctdb_marshall_buffer *)talloc_realloc_size( + mem_ctx, m, m_size + r_size); + if (m2 == NULL) { + talloc_free(m); + goto done; + } + + memcpy(m_size + (uint8_t *)m2, r, r_size); + + m2->count++; + +done: + talloc_free(r); + return m2; +} + +/* we've finished marshalling, return a data blob with the marshalled records */ +static TDB_DATA db_ctdb_marshall_finish(struct ctdb_marshall_buffer *m) +{ + TDB_DATA data; + data.dptr = (uint8_t *)m; + data.dsize = talloc_get_size(m); + return data; +} + +/* + loop over a marshalling buffer + + - pass r==NULL to start + - loop the number of times indicated by m->count +*/ +static struct ctdb_rec_data_old *db_ctdb_marshall_loop_next_key( + struct ctdb_marshall_buffer *m, struct ctdb_rec_data_old *r, TDB_DATA *key) +{ + if (r == NULL) { + r = (struct ctdb_rec_data_old *)&m->data[0]; + } else { + r = (struct ctdb_rec_data_old *)(r->length + (uint8_t *)r); + } + + key->dptr = &r->data[0]; + key->dsize = r->keylen; + return r; +} + +static bool db_ctdb_marshall_buf_parse( + struct ctdb_rec_data_old *r, uint32_t *reqid, + struct ctdb_ltdb_header **header, TDB_DATA *data) +{ + if (r->datalen < sizeof(struct ctdb_ltdb_header)) { + return false; + } + + *reqid = r->reqid; + + data->dptr = &r->data[r->keylen] + sizeof(struct ctdb_ltdb_header); + data->dsize = r->datalen - sizeof(struct ctdb_ltdb_header); + + *header = (struct ctdb_ltdb_header *)&r->data[r->keylen]; + + return true; +} + +/** + * CTDB transaction destructor + */ +static int db_ctdb_transaction_destructor(struct db_ctdb_transaction_handle *h) +{ + NTSTATUS status; + + status = g_lock_unlock(h->ctx->lock_ctx, + string_term_tdb_data(h->lock_name)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("g_lock_unlock failed for %s: %s\n", h->lock_name, + nt_errstr(status))); + return -1; + } + return 0; +} + +/** + * CTDB dbwrap API: transaction_start function + * starts a transaction on a persistent database + */ +static int db_ctdb_transaction_start(struct db_context *db) +{ + struct db_ctdb_transaction_handle *h; + NTSTATUS status; + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + + if (!db->persistent) { + DEBUG(0,("transactions not supported on non-persistent database 0x%08x\n", + ctx->db_id)); + return -1; + } + + if (ctx->transaction) { + ctx->transaction->nesting++; + DEBUG(5, (__location__ " transaction start on db 0x%08x: nesting %d -> %d\n", + ctx->db_id, ctx->transaction->nesting - 1, ctx->transaction->nesting)); + return 0; + } + + h = talloc_zero(db, struct db_ctdb_transaction_handle); + if (h == NULL) { + DEBUG(0,(__location__ " oom for transaction handle\n")); + return -1; + } + + h->ctx = ctx; + + h->lock_name = talloc_asprintf(h, "transaction_db_0x%08x", + (unsigned int)ctx->db_id); + if (h->lock_name == NULL) { + DEBUG(0, ("talloc_asprintf failed\n")); + TALLOC_FREE(h); + return -1; + } + + /* + * Wait a day, i.e. forever... + */ + status = g_lock_lock(ctx->lock_ctx, string_term_tdb_data(h->lock_name), + G_LOCK_WRITE, timeval_set(86400, 0), NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("g_lock_lock failed: %s\n", nt_errstr(status))); + TALLOC_FREE(h); + return -1; + } + + talloc_set_destructor(h, db_ctdb_transaction_destructor); + + ctx->transaction = h; + + DEBUG(5,(__location__ " transaction started on db 0x%08x\n", ctx->db_id)); + + return 0; +} + +static bool parse_newest_in_marshall_buffer( + struct ctdb_marshall_buffer *buf, TDB_DATA key, + void (*parser)(TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data), + void *private_data) +{ + struct ctdb_rec_data_old *rec = NULL; + struct ctdb_ltdb_header *h = NULL; + TDB_DATA data; + uint32_t i; + + if (buf == NULL) { + return false; + } + + /* + * Walk the list of records written during this + * transaction. If we want to read one we have already + * written, return the last written sample. Thus we do not do + * a "break;" for the first hit, this record might have been + * overwritten later. + */ + + for (i=0; i<buf->count; i++) { + TDB_DATA tkey; + uint32_t reqid; + + rec = db_ctdb_marshall_loop_next_key(buf, rec, &tkey); + if (rec == NULL) { + return false; + } + + if (!tdb_data_equal(key, tkey)) { + continue; + } + + if (!db_ctdb_marshall_buf_parse(rec, &reqid, &h, &data)) { + return false; + } + } + + if (h == NULL) { + return false; + } + + parser(key, h, data, private_data); + + return true; +} + +struct pull_newest_from_marshall_buffer_state { + struct ctdb_ltdb_header *pheader; + TALLOC_CTX *mem_ctx; + TDB_DATA *pdata; +}; + +static void pull_newest_from_marshall_buffer_parser( + TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data) +{ + struct pull_newest_from_marshall_buffer_state *state = + (struct pull_newest_from_marshall_buffer_state *)private_data; + + if (state->pheader != NULL) { + memcpy(state->pheader, header, sizeof(*state->pheader)); + } + if (state->pdata != NULL) { + state->pdata->dsize = data.dsize; + state->pdata->dptr = (uint8_t *)talloc_memdup( + state->mem_ctx, data.dptr, data.dsize); + } +} + +static bool pull_newest_from_marshall_buffer(struct ctdb_marshall_buffer *buf, + TDB_DATA key, + struct ctdb_ltdb_header *pheader, + TALLOC_CTX *mem_ctx, + TDB_DATA *pdata) +{ + struct pull_newest_from_marshall_buffer_state state; + + state.pheader = pheader; + state.mem_ctx = mem_ctx; + state.pdata = pdata; + + if (!parse_newest_in_marshall_buffer( + buf, key, pull_newest_from_marshall_buffer_parser, + &state)) { + return false; + } + if ((pdata != NULL) && (pdata->dsize != 0) && (pdata->dptr == NULL)) { + /* ENOMEM */ + return false; + } + return true; +} + +static NTSTATUS db_ctdb_storev_transaction(struct db_record *rec, + const TDB_DATA *dbufs, int num_dbufs, + int flag); +static NTSTATUS db_ctdb_delete_transaction(struct db_record *rec); + +static struct db_record *db_ctdb_fetch_locked_transaction(struct db_ctdb_ctx *ctx, + TALLOC_CTX *mem_ctx, + TDB_DATA key) +{ + struct db_record *result; + TDB_DATA ctdb_data; + + if (!(result = talloc(mem_ctx, struct db_record))) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + result->db = ctx->db; + result->private_data = ctx->transaction; + + result->key.dsize = key.dsize; + result->key.dptr = (uint8_t *)talloc_memdup(result, key.dptr, + key.dsize); + if (result->key.dptr == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + result->storev = db_ctdb_storev_transaction; + result->delete_rec = db_ctdb_delete_transaction; + + if (ctx->transaction == NULL) { + DEBUG(0, ("no transaction available\n")); + TALLOC_FREE(result); + return NULL; + } + if (pull_newest_from_marshall_buffer(ctx->transaction->m_write, key, + NULL, result, &result->value)) { + result->value_valid = true; + return result; + } + + ctdb_data = tdb_fetch(ctx->wtdb->tdb, key); + if (ctdb_data.dptr == NULL) { + /* create the record */ + result->value = tdb_null; + result->value_valid = true; + return result; + } + + result->value.dsize = ctdb_data.dsize - sizeof(struct ctdb_ltdb_header); + result->value.dptr = NULL; + + if ((result->value.dsize != 0) + && !(result->value.dptr = (uint8_t *)talloc_memdup( + result, ctdb_data.dptr + sizeof(struct ctdb_ltdb_header), + result->value.dsize))) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + result->value_valid = true; + + SAFE_FREE(ctdb_data.dptr); + + return result; +} + +static int db_ctdb_record_destructor(struct db_record **recp) +{ + struct db_record *rec = talloc_get_type_abort(*recp, struct db_record); + struct db_ctdb_transaction_handle *h = talloc_get_type_abort( + rec->private_data, struct db_ctdb_transaction_handle); + int ret = h->ctx->db->transaction_commit(h->ctx->db); + if (ret != 0) { + DEBUG(0,(__location__ " transaction_commit failed\n")); + } + return 0; +} + +/* + auto-create a transaction for persistent databases + */ +static struct db_record *db_ctdb_fetch_locked_persistent(struct db_ctdb_ctx *ctx, + TALLOC_CTX *mem_ctx, + TDB_DATA key) +{ + int res; + struct db_record *rec, **recp; + + res = db_ctdb_transaction_start(ctx->db); + if (res == -1) { + return NULL; + } + + rec = db_ctdb_fetch_locked_transaction(ctx, mem_ctx, key); + if (rec == NULL) { + ctx->db->transaction_cancel(ctx->db); + return NULL; + } + + /* destroy this transaction when we release the lock */ + recp = talloc(rec, struct db_record *); + if (recp == NULL) { + ctx->db->transaction_cancel(ctx->db); + talloc_free(rec); + return NULL; + } + *recp = rec; + talloc_set_destructor(recp, db_ctdb_record_destructor); + return rec; +} + + +/* + stores a record inside a transaction + */ +static NTSTATUS db_ctdb_transaction_store(struct db_ctdb_transaction_handle *h, + TDB_DATA key, TDB_DATA data) +{ + TALLOC_CTX *tmp_ctx = talloc_new(h); + TDB_DATA rec; + struct ctdb_ltdb_header header; + + ZERO_STRUCT(header); + + /* we need the header so we can update the RSN */ + + if (!pull_newest_from_marshall_buffer(h->m_write, key, &header, + NULL, NULL)) { + + rec = tdb_fetch(h->ctx->wtdb->tdb, key); + + if (rec.dptr != NULL) { + memcpy(&header, rec.dptr, + sizeof(struct ctdb_ltdb_header)); + rec.dsize -= sizeof(struct ctdb_ltdb_header); + + /* + * a special case, we are writing the same + * data that is there now + */ + if (data.dsize == rec.dsize && + memcmp(data.dptr, + rec.dptr + sizeof(struct ctdb_ltdb_header), + data.dsize) == 0) { + SAFE_FREE(rec.dptr); + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } + } + SAFE_FREE(rec.dptr); + } + + header.dmaster = get_my_vnn(); + header.rsn++; + + h->m_write = db_ctdb_marshall_add(h, h->m_write, h->ctx->db_id, 0, key, &header, data); + if (h->m_write == NULL) { + DEBUG(0,(__location__ " Failed to add to marshalling record\n")); + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + + +/* + a record store inside a transaction + */ +static NTSTATUS db_ctdb_storev_transaction( + struct db_record *rec, const TDB_DATA *dbufs, int num_dbufs, int flag) +{ + struct db_ctdb_transaction_handle *h = talloc_get_type_abort( + rec->private_data, struct db_ctdb_transaction_handle); + NTSTATUS status; + TDB_DATA data = {0}; + + status = dbwrap_merge_dbufs(&data, rec, dbufs, num_dbufs); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = db_ctdb_transaction_store(h, rec->key, data); + + TALLOC_FREE(data.dptr); + + return status; +} + +/* + a record delete inside a transaction + */ +static NTSTATUS db_ctdb_delete_transaction(struct db_record *rec) +{ + struct db_ctdb_transaction_handle *h = talloc_get_type_abort( + rec->private_data, struct db_ctdb_transaction_handle); + NTSTATUS status; + + status = db_ctdb_transaction_store(h, rec->key, tdb_null); + return status; +} + +static void db_ctdb_fetch_db_seqnum_parser( + TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data) +{ + uint64_t *seqnum = (uint64_t *)private_data; + + if (data.dsize != sizeof(uint64_t)) { + *seqnum = 0; + return; + } + memcpy(seqnum, data.dptr, sizeof(*seqnum)); +} + +/** + * Fetch the db sequence number of a persistent db directly from the db. + */ +static NTSTATUS db_ctdb_fetch_db_seqnum_from_db(struct db_ctdb_ctx *db, + uint64_t *seqnum) +{ + NTSTATUS status; + TDB_DATA key; + + if (seqnum == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + key = string_term_tdb_data(CTDB_DB_SEQNUM_KEY); + + status = db_ctdb_ltdb_parse( + db, key, db_ctdb_fetch_db_seqnum_parser, seqnum); + + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_OK; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + *seqnum = 0; + return NT_STATUS_OK; + } + return status; +} + +/** + * Store the database sequence number inside a transaction. + */ +static NTSTATUS db_ctdb_store_db_seqnum(struct db_ctdb_transaction_handle *h, + uint64_t seqnum) +{ + NTSTATUS status; + TDB_DATA key = string_term_tdb_data(CTDB_DB_SEQNUM_KEY); + TDB_DATA data = { .dptr=(uint8_t *)&seqnum, .dsize=sizeof(seqnum) }; + + status = db_ctdb_transaction_store(h, key, data); + + return status; +} + +/* + commit a transaction + */ +static int db_ctdb_transaction_commit(struct db_context *db) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + NTSTATUS rets; + int32_t status; + struct db_ctdb_transaction_handle *h = ctx->transaction; + uint64_t old_seqnum, new_seqnum; + int ret; + + if (h == NULL) { + DEBUG(0,(__location__ " transaction commit with no open transaction on db 0x%08x\n", ctx->db_id)); + return -1; + } + + if (h->nested_cancel) { + db->transaction_cancel(db); + DEBUG(5,(__location__ " Failed transaction commit after nested cancel\n")); + return -1; + } + + if (h->nesting != 0) { + h->nesting--; + DEBUG(5, (__location__ " transaction commit on db 0x%08x: nesting %d -> %d\n", + ctx->db_id, ctx->transaction->nesting + 1, ctx->transaction->nesting)); + return 0; + } + + if (h->m_write == NULL) { + /* + * No changes were made, so don't change the seqnum, + * don't push to other node, just exit with success. + */ + ret = 0; + goto done; + } + + DEBUG(5,(__location__ " transaction commit on db 0x%08x\n", ctx->db_id)); + + /* + * As the last db action before committing, bump the database sequence + * number. Note that this undoes all changes to the seqnum records + * performed under the transaction. This record is not meant to be + * modified by user interaction. It is for internal use only... + */ + rets = db_ctdb_fetch_db_seqnum_from_db(ctx, &old_seqnum); + if (!NT_STATUS_IS_OK(rets)) { + DEBUG(1, (__location__ " failed to fetch the db sequence number " + "in transaction commit on db 0x%08x\n", ctx->db_id)); + ret = -1; + goto done; + } + + new_seqnum = old_seqnum + 1; + + rets = db_ctdb_store_db_seqnum(h, new_seqnum); + if (!NT_STATUS_IS_OK(rets)) { + DEBUG(1, (__location__ "failed to store the db sequence number " + " in transaction commit on db 0x%08x\n", ctx->db_id)); + ret = -1; + goto done; + } + +again: + /* tell ctdbd to commit to the other nodes */ + ret = ctdbd_control_local(messaging_ctdb_connection(), + CTDB_CONTROL_TRANS3_COMMIT, + h->ctx->db_id, 0, + db_ctdb_marshall_finish(h->m_write), + NULL, NULL, &status); + if ((ret != 0) || status != 0) { + /* + * The TRANS3_COMMIT control should only possibly fail when a + * recovery has been running concurrently. In any case, the db + * will be the same on all nodes, either the new copy or the + * old copy. This can be detected by comparing the old and new + * local sequence numbers. + */ + rets = db_ctdb_fetch_db_seqnum_from_db(ctx, &new_seqnum); + if (!NT_STATUS_IS_OK(rets)) { + DEBUG(1, (__location__ " failed to refetch db sequence " + "number after failed TRANS3_COMMIT\n")); + ret = -1; + goto done; + } + + if (new_seqnum == old_seqnum) { + /* Recovery prevented all our changes: retry. */ + goto again; + } + if (new_seqnum != (old_seqnum + 1)) { + DEBUG(0, (__location__ " ERROR: new_seqnum[%lu] != " + "old_seqnum[%lu] + (0 or 1) after failed " + "TRANS3_COMMIT - this should not happen!\n", + (unsigned long)new_seqnum, + (unsigned long)old_seqnum)); + ret = -1; + goto done; + } + /* + * Recovery propagated our changes to all nodes, completing + * our commit for us - succeed. + */ + } + + ret = 0; + +done: + h->ctx->transaction = NULL; + talloc_free(h); + return ret; +} + + +/* + cancel a transaction + */ +static int db_ctdb_transaction_cancel(struct db_context *db) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + struct db_ctdb_transaction_handle *h = ctx->transaction; + + if (h == NULL) { + DEBUG(0,(__location__ " transaction cancel with no open transaction on db 0x%08x\n", ctx->db_id)); + return -1; + } + + if (h->nesting != 0) { + h->nesting--; + h->nested_cancel = true; + DEBUG(5, (__location__ " transaction cancel on db 0x%08x: nesting %d -> %d\n", + ctx->db_id, ctx->transaction->nesting + 1, ctx->transaction->nesting)); + return 0; + } + + DEBUG(5,(__location__ " Cancel transaction on db 0x%08x\n", ctx->db_id)); + + ctx->transaction = NULL; + talloc_free(h); + return 0; +} + + +static NTSTATUS db_ctdb_storev(struct db_record *rec, + const TDB_DATA *dbufs, int num_dbufs, int flag) +{ + struct db_ctdb_rec *crec = talloc_get_type_abort( + rec->private_data, struct db_ctdb_rec); + NTSTATUS status; + + status = db_ctdb_ltdb_store(crec->ctdb_ctx, rec->key, &(crec->header), + dbufs, num_dbufs); + return status; +} + + + +static NTSTATUS db_ctdb_send_schedule_for_deletion(struct db_record *rec) +{ + NTSTATUS status = NT_STATUS_OK; + int ret; + struct ctdb_control_schedule_for_deletion *dd; + TDB_DATA indata; + int32_t cstatus; + struct db_ctdb_rec *crec = talloc_get_type_abort( + rec->private_data, struct db_ctdb_rec); + struct db_ctdb_ctx *ctx = crec->ctdb_ctx; + + indata.dsize = offsetof(struct ctdb_control_schedule_for_deletion, key) + rec->key.dsize; + indata.dptr = talloc_zero_array(crec, uint8_t, indata.dsize); + if (indata.dptr == NULL) { + DEBUG(0, (__location__ " talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + dd = (struct ctdb_control_schedule_for_deletion *)(void *)indata.dptr; + dd->db_id = ctx->db_id; + dd->hdr = crec->header; + dd->keylen = rec->key.dsize; + memcpy(dd->key, rec->key.dptr, rec->key.dsize); + + ret = ctdbd_control_local(messaging_ctdb_connection(), + CTDB_CONTROL_SCHEDULE_FOR_DELETION, + crec->ctdb_ctx->db_id, + CTDB_CTRL_FLAG_NOREPLY, /* flags */ + indata, + NULL, /* mem_ctx */ + NULL, /* outdata */ + &cstatus); + talloc_free(indata.dptr); + + if ((ret != 0) || cstatus != 0) { + DEBUG(1, (__location__ " Error sending local control " + "SCHEDULE_FOR_DELETION: %s, cstatus = %"PRIi32"\n", + strerror(ret), cstatus)); + if (ret != 0) { + status = map_nt_error_from_unix(ret); + } else { + status = NT_STATUS_UNSUCCESSFUL; + } + } + + return status; +} + +static NTSTATUS db_ctdb_delete(struct db_record *rec) +{ + NTSTATUS status; + + /* + * We have to store the header with empty data. TODO: Fix the + * tdb-level cleanup + */ + + status = db_ctdb_storev(rec, &tdb_null, 1, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = db_ctdb_send_schedule_for_deletion(rec); + return status; +} + +static int db_ctdb_record_destr(struct db_record* data) +{ + struct db_ctdb_rec *crec = talloc_get_type_abort( + data->private_data, struct db_ctdb_rec); + int threshold; + int ret; + struct timeval before; + double timediff; + + DEBUG(10, (DEBUGLEVEL > 10 + ? "Unlocking db %u key %s\n" + : "Unlocking db %u key %.20s\n", + (int)crec->ctdb_ctx->db_id, + hex_encode_talloc(data, (unsigned char *)data->key.dptr, + data->key.dsize))); + + before = timeval_current(); + + ret = tdb_chainunlock(crec->ctdb_ctx->wtdb->tdb, data->key); + + timediff = timeval_elapsed(&before); + timediff *= 1000; /* get us milliseconds */ + + if (timediff > crec->ctdb_ctx->warn_unlock_msecs) { + char *key; + key = hex_encode_talloc(talloc_tos(), + (unsigned char *)data->key.dptr, + data->key.dsize); + DEBUG(0, ("tdb_chainunlock on db %s, key %s took %f milliseconds\n", + tdb_name(crec->ctdb_ctx->wtdb->tdb), key, + timediff)); + TALLOC_FREE(key); + } + + if (ret != 0) { + DEBUG(0, ("tdb_chainunlock failed\n")); + return -1; + } + + threshold = crec->ctdb_ctx->warn_locktime_msecs; + if (threshold != 0) { + timediff = timeval_elapsed(&crec->lock_time) * 1000; + if (timediff > threshold) { + const char *key; + + key = hex_encode_talloc(data, + (unsigned char *)data->key.dptr, + data->key.dsize); + DEBUG(0, ("Held tdb lock on db %s, key %s " + "%f milliseconds\n", + tdb_name(crec->ctdb_ctx->wtdb->tdb), + key, timediff)); + } + } + + return 0; +} + +/** + * Check whether we have a valid local copy of the given record, + * either for reading or for writing. + */ +static bool db_ctdb_can_use_local_hdr(const struct ctdb_ltdb_header *hdr, + uint32_t my_vnn, bool read_only) +{ + if (hdr->dmaster != my_vnn) { + /* If we're not dmaster, it must be r/o copy. */ + return read_only && (hdr->flags & CTDB_REC_RO_HAVE_READONLY); + } + + /* + * If we want write access, no one may have r/o copies. + */ + return read_only || !(hdr->flags & CTDB_REC_RO_HAVE_DELEGATIONS); +} + +static bool db_ctdb_can_use_local_copy(TDB_DATA ctdb_data, uint32_t my_vnn, + bool read_only) +{ + if (ctdb_data.dptr == NULL) { + return false; + } + + if (ctdb_data.dsize < sizeof(struct ctdb_ltdb_header)) { + return false; + } + + return db_ctdb_can_use_local_hdr( + (struct ctdb_ltdb_header *)ctdb_data.dptr, my_vnn, read_only); +} + +static struct db_record *fetch_locked_internal(struct db_ctdb_ctx *ctx, + TALLOC_CTX *mem_ctx, + TDB_DATA key) +{ + struct db_record *result; + struct db_ctdb_rec *crec; + TDB_DATA ctdb_data; + int migrate_attempts; + struct timeval migrate_start; + struct timeval chainlock_start; + struct timeval ctdb_start_time; + double chainlock_time = 0; + double ctdb_time = 0; + int duration_msecs; + int lockret; + int ret; + + if (!(result = talloc(mem_ctx, struct db_record))) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + if (!(crec = talloc_zero(result, struct db_ctdb_rec))) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + result->db = ctx->db; + result->private_data = (void *)crec; + crec->ctdb_ctx = ctx; + + result->key.dsize = key.dsize; + result->key.dptr = (uint8_t *)talloc_memdup(result, key.dptr, + key.dsize); + if (result->key.dptr == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + migrate_attempts = 0; + GetTimeOfDay(&migrate_start); + + /* + * Do a blocking lock on the record + */ +again: + + if (DEBUGLEVEL >= 10) { + char *keystr = hex_encode_talloc(result, key.dptr, key.dsize); + DEBUG(10, (DEBUGLEVEL > 10 + ? "Locking db %u key %s\n" + : "Locking db %u key %.20s\n", + (int)crec->ctdb_ctx->db_id, keystr)); + TALLOC_FREE(keystr); + } + + GetTimeOfDay(&chainlock_start); + lockret = tdb_chainlock(ctx->wtdb->tdb, key); + chainlock_time += timeval_elapsed(&chainlock_start); + + if (lockret != 0) { + DEBUG(3, ("tdb_chainlock failed\n")); + TALLOC_FREE(result); + return NULL; + } + + result->storev = db_ctdb_storev; + result->delete_rec = db_ctdb_delete; + talloc_set_destructor(result, db_ctdb_record_destr); + + ctdb_data = tdb_fetch(ctx->wtdb->tdb, key); + + /* + * See if we have a valid record and we are the dmaster. If so, we can + * take the shortcut and just return it. + */ + + if (!db_ctdb_can_use_local_copy(ctdb_data, get_my_vnn(), false)) { + SAFE_FREE(ctdb_data.dptr); + tdb_chainunlock(ctx->wtdb->tdb, key); + talloc_set_destructor(result, NULL); + + migrate_attempts += 1; + + DEBUG(10, ("ctdb_data.dptr = %p, dmaster = %"PRIu32" " + "(%"PRIu32") %"PRIu32"\n", + ctdb_data.dptr, ctdb_data.dptr ? + ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster : + UINT32_MAX, + get_my_vnn(), + ctdb_data.dptr ? + ((struct ctdb_ltdb_header *)ctdb_data.dptr)->flags : 0)); + + GetTimeOfDay(&ctdb_start_time); + ret = ctdbd_migrate(messaging_ctdb_connection(), ctx->db_id, + key); + ctdb_time += timeval_elapsed(&ctdb_start_time); + + if (ret != 0) { + DEBUG(5, ("ctdbd_migrate failed: %s\n", + strerror(ret))); + TALLOC_FREE(result); + return NULL; + } + /* now its migrated, try again */ + goto again; + } + + { + double duration; + duration = timeval_elapsed(&migrate_start); + + /* + * Convert the duration to milliseconds to avoid a + * floating-point division of + * lp_parm_int("migrate_duration") by 1000. + */ + duration_msecs = duration * 1000; + } + + if ((migrate_attempts > ctx->warn_migrate_attempts) || + (duration_msecs > ctx->warn_migrate_msecs)) { + int chain = 0; + + if (tdb_get_flags(ctx->wtdb->tdb) & TDB_INCOMPATIBLE_HASH) { + chain = tdb_jenkins_hash(&key) % + tdb_hash_size(ctx->wtdb->tdb); + } + + DEBUG(0, ("db_ctdb_fetch_locked for %s key %s, chain %d " + "needed %d attempts, %d milliseconds, " + "chainlock: %f ms, CTDB %f ms\n", + tdb_name(ctx->wtdb->tdb), + hex_encode_talloc(talloc_tos(), + (unsigned char *)key.dptr, + key.dsize), + chain, + migrate_attempts, duration_msecs, + chainlock_time * 1000.0, + ctdb_time * 1000.0)); + } + + GetTimeOfDay(&crec->lock_time); + + memcpy(&crec->header, ctdb_data.dptr, sizeof(crec->header)); + + result->value.dsize = ctdb_data.dsize - sizeof(crec->header); + result->value.dptr = NULL; + + if (result->value.dsize != 0) { + result->value.dptr = talloc_memdup( + result, ctdb_data.dptr + sizeof(crec->header), + result->value.dsize); + if (result->value.dptr == NULL) { + DBG_ERR("talloc failed\n"); + TALLOC_FREE(result); + return NULL; + } + } + result->value_valid = true; + + SAFE_FREE(ctdb_data.dptr); + + return result; +} + +static struct db_record *db_ctdb_fetch_locked(struct db_context *db, + TALLOC_CTX *mem_ctx, + TDB_DATA key) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + + if (ctx->transaction != NULL) { + return db_ctdb_fetch_locked_transaction(ctx, mem_ctx, key); + } + + if (db->persistent) { + return db_ctdb_fetch_locked_persistent(ctx, mem_ctx, key); + } + + return fetch_locked_internal(ctx, mem_ctx, key); +} + +struct db_ctdb_parse_record_state { + void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data); + void *private_data; + uint32_t my_vnn; + bool ask_for_readonly_copy; + bool done; + bool empty_record; +}; + +static void db_ctdb_parse_record_parser( + TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data) +{ + struct db_ctdb_parse_record_state *state = + (struct db_ctdb_parse_record_state *)private_data; + state->parser(key, data, state->private_data); +} + +static void db_ctdb_parse_record_parser_nonpersistent( + TDB_DATA key, struct ctdb_ltdb_header *header, + TDB_DATA data, void *private_data) +{ + struct db_ctdb_parse_record_state *state = + (struct db_ctdb_parse_record_state *)private_data; + + if (db_ctdb_can_use_local_hdr(header, state->my_vnn, true)) { + /* + * A record consisting only of the ctdb header can be + * a validly created empty record or a tombstone + * record of a deleted record (not vacuumed yet). Mark + * it accordingly. + */ + state->empty_record = (data.dsize == 0); + if (!state->empty_record) { + state->parser(key, data, state->private_data); + } + state->done = true; + } else { + /* + * We found something in the db, so it seems that this record, + * while not usable locally right now, is popular. Ask for a + * R/O copy. + */ + state->ask_for_readonly_copy = true; + } +} + +static NTSTATUS db_ctdb_try_parse_local_record(struct db_ctdb_ctx *ctx, + TDB_DATA key, + struct db_ctdb_parse_record_state *state) +{ + NTSTATUS status; + + if (ctx->transaction != NULL) { + struct db_ctdb_transaction_handle *h = ctx->transaction; + bool found; + + /* + * Transactions only happen for persistent db's. + */ + + found = parse_newest_in_marshall_buffer( + h->m_write, key, db_ctdb_parse_record_parser, state); + + if (found) { + return NT_STATUS_OK; + } + } + + if (ctx->db->persistent) { + /* + * Persistent db, but not found in the transaction buffer + */ + return db_ctdb_ltdb_parse( + ctx, key, db_ctdb_parse_record_parser, state); + } + + state->done = false; + state->ask_for_readonly_copy = false; + + status = db_ctdb_ltdb_parse( + ctx, key, db_ctdb_parse_record_parser_nonpersistent, state); + if (NT_STATUS_IS_OK(status) && state->done) { + if (state->empty_record) { + /* + * We know authoritatively, that this is an empty + * record. Since ctdb does not distinguish between empty + * and deleted records, this can be a record stored as + * empty or a not-yet-vacuumed tombstone record of a + * deleted record. Now Samba right now can live without + * empty records, so we can safely report this record + * as non-existing. + * + * See bugs 10008 and 12005. + */ + return NT_STATUS_NOT_FOUND; + } + return NT_STATUS_OK; + } + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +static NTSTATUS db_ctdb_parse_record(struct db_context *db, TDB_DATA key, + void (*parser)(TDB_DATA key, + TDB_DATA data, + void *private_data), + void *private_data) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_ctdb_ctx); + struct db_ctdb_parse_record_state state; + NTSTATUS status; + int ret; + + state.parser = parser; + state.private_data = private_data; + state.my_vnn = get_my_vnn(); + state.empty_record = false; + + status = db_ctdb_try_parse_local_record(ctx, key, &state); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return status; + } + + ret = ctdbd_parse(messaging_ctdb_connection(), ctx->db_id, key, + state.ask_for_readonly_copy, parser, private_data); + if (ret != 0) { + if (ret == ENOENT) { + /* + * This maps to + * NT_STATUS_OBJECT_NAME_NOT_FOUND. Our upper + * layers expect NT_STATUS_NOT_FOUND for "no + * record around". We need to convert dbwrap + * to 0/errno away from NTSTATUS ... :-) + */ + return NT_STATUS_NOT_FOUND; + } + return map_nt_error_from_unix(ret); + } + return NT_STATUS_OK; +} + +static void db_ctdb_parse_record_done(struct tevent_req *subreq); + +static struct tevent_req *db_ctdb_parse_record_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct db_context *db, + TDB_DATA key, + void (*parser)(TDB_DATA key, + TDB_DATA data, + void *private_data), + void *private_data, + enum dbwrap_req_state *req_state) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_ctdb_ctx); + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct db_ctdb_parse_record_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct db_ctdb_parse_record_state); + if (req == NULL) { + *req_state = DBWRAP_REQ_ERROR; + return NULL; + + } + + *state = (struct db_ctdb_parse_record_state) { + .parser = parser, + .private_data = private_data, + .my_vnn = get_my_vnn(), + .empty_record = false, + }; + + status = db_ctdb_try_parse_local_record(ctx, key, state); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + if (tevent_req_nterror(req, status)) { + *req_state = DBWRAP_REQ_ERROR; + return tevent_req_post(req, ev); + } + *req_state = DBWRAP_REQ_DONE; + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + subreq = ctdbd_parse_send(state, + ev, + ctdb_async_ctx.async_conn, + ctx->db_id, + key, + state->ask_for_readonly_copy, + parser, + private_data, + req_state); + if (tevent_req_nomem(subreq, req)) { + *req_state = DBWRAP_REQ_ERROR; + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, db_ctdb_parse_record_done, req); + + return req; +} + +static void db_ctdb_parse_record_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret; + + ret = ctdbd_parse_recv(subreq); + TALLOC_FREE(subreq); + if (ret != 0) { + if (ret == ENOENT) { + /* + * This maps to NT_STATUS_OBJECT_NAME_NOT_FOUND. Our + * upper layers expect NT_STATUS_NOT_FOUND for "no + * record around". We need to convert dbwrap to 0/errno + * away from NTSTATUS ... :-) + */ + tevent_req_nterror(req, NT_STATUS_NOT_FOUND); + return; + } + tevent_req_nterror(req, map_nt_error_from_unix(ret)); + return; + } + + tevent_req_done(req); + return; +} + +static NTSTATUS db_ctdb_parse_record_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct traverse_state { + struct db_context *db; + int (*fn)(struct db_record *rec, void *private_data); + void *private_data; + int count; +}; + +static void traverse_callback(TDB_DATA key, TDB_DATA data, void *private_data) +{ + struct traverse_state *state = (struct traverse_state *)private_data; + struct db_record *rec = NULL; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(state->db); + if (tmp_ctx == NULL) { + DBG_ERR("talloc_new failed\n"); + return; + } + + /* we have to give them a locked record to prevent races */ + rec = db_ctdb_fetch_locked(state->db, tmp_ctx, key); + if (rec != NULL && rec->value.dsize > 0) { + state->fn(rec, state->private_data); + state->count++; + } + talloc_free(tmp_ctx); +} + +static int traverse_persistent_callback(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *private_data) +{ + struct traverse_state *state = (struct traverse_state *)private_data; + struct db_record *rec; + TALLOC_CTX *tmp_ctx = talloc_new(state->db); + int ret = 0; + + /* + * Skip the __db_sequence_number__ key: + * This is used for persistent transactions internally. + */ + if (kbuf.dsize == strlen(CTDB_DB_SEQNUM_KEY) + 1 && + strcmp((const char*)kbuf.dptr, CTDB_DB_SEQNUM_KEY) == 0) + { + goto done; + } + + /* we have to give them a locked record to prevent races */ + rec = db_ctdb_fetch_locked(state->db, tmp_ctx, kbuf); + if (rec && rec->value.dsize > 0) { + ret = state->fn(rec, state->private_data); + } + +done: + talloc_free(tmp_ctx); + return ret; +} + +/* wrapper to use traverse_persistent_callback with dbwrap */ +static int traverse_persistent_callback_dbwrap(struct db_record *rec, void* data) +{ + return traverse_persistent_callback(NULL, rec->key, rec->value, data); +} + +static int db_ctdbd_traverse(uint32_t db_id, + void (*fn)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + struct ctdbd_connection *conn; + int ret; + + become_root(); + ret = ctdbd_init_connection(talloc_tos(), lp_ctdbd_socket(), + lp_ctdb_timeout(), &conn); + unbecome_root(); + if (ret != 0) { + DBG_WARNING("ctdbd_init_connection failed: %s\n", + strerror(ret)); + return ret; + } + + ret = ctdbd_traverse(conn, db_id, fn, private_data); + TALLOC_FREE(conn); + + if (ret != 0) { + DBG_WARNING("ctdbd_traverse failed: %s\n", + strerror(ret)); + return ret; + } + + return 0; +} + + +static int db_ctdb_traverse(struct db_context *db, + int (*fn)(struct db_record *rec, + void *private_data), + void *private_data) +{ + int ret; + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + struct traverse_state state; + + state = (struct traverse_state) { + .db = db, + .fn = fn, + .private_data = private_data, + }; + + if (db->persistent) { + struct tdb_context *ltdb = ctx->wtdb->tdb; + + /* for persistent databases we don't need to do a ctdb traverse, + we can do a faster local traverse */ + ret = tdb_traverse(ltdb, traverse_persistent_callback, &state); + if (ret < 0) { + return ret; + } + if (ctx->transaction && ctx->transaction->m_write) { + /* + * we now have to handle keys not yet + * present at transaction start + */ + struct db_context *newkeys = db_open_rbt(talloc_tos()); + struct ctdb_marshall_buffer *mbuf = ctx->transaction->m_write; + struct ctdb_rec_data_old *rec=NULL; + uint32_t i; + int count = 0; + NTSTATUS status; + + if (newkeys == NULL) { + return -1; + } + + for (i=0; i<mbuf->count; i++) { + TDB_DATA key; + rec = db_ctdb_marshall_loop_next_key( + mbuf, rec, &key); + SMB_ASSERT(rec != NULL); + + if (!tdb_exists(ltdb, key)) { + dbwrap_store(newkeys, key, tdb_null, 0); + } + } + status = dbwrap_traverse(newkeys, + traverse_persistent_callback_dbwrap, + &state, + &count); + talloc_free(newkeys); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + ret += count; + } + return ret; + } + + ret = db_ctdbd_traverse(ctx->db_id, traverse_callback, &state); + if (ret != 0) { + return -1; + } + return state.count; +} + +static NTSTATUS db_ctdb_storev_deny(struct db_record *rec, + const TDB_DATA *dbufs, int num_dbufs, int flag) +{ + return NT_STATUS_MEDIA_WRITE_PROTECTED; +} + +static NTSTATUS db_ctdb_delete_deny(struct db_record *rec) +{ + return NT_STATUS_MEDIA_WRITE_PROTECTED; +} + +static void traverse_read_callback(TDB_DATA key, TDB_DATA data, void *private_data) +{ + struct traverse_state *state = (struct traverse_state *)private_data; + struct db_record rec; + + ZERO_STRUCT(rec); + rec.db = state->db; + rec.key = key; + rec.value = data; + rec.storev = db_ctdb_storev_deny; + rec.delete_rec = db_ctdb_delete_deny; + rec.private_data = NULL; + rec.value_valid = true; + state->fn(&rec, state->private_data); + state->count++; +} + +static int traverse_persistent_callback_read(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *private_data) +{ + struct traverse_state *state = (struct traverse_state *)private_data; + struct db_record rec; + + /* + * Skip the __db_sequence_number__ key: + * This is used for persistent transactions internally. + */ + if (kbuf.dsize == strlen(CTDB_DB_SEQNUM_KEY) + 1 && + strcmp((const char*)kbuf.dptr, CTDB_DB_SEQNUM_KEY) == 0) + { + return 0; + } + + ZERO_STRUCT(rec); + rec.db = state->db; + rec.key = kbuf; + rec.value = dbuf; + rec.value_valid = true; + rec.storev = db_ctdb_storev_deny; + rec.delete_rec = db_ctdb_delete_deny; + rec.private_data = NULL; + + if (rec.value.dsize <= sizeof(struct ctdb_ltdb_header)) { + /* a deleted record */ + return 0; + } + rec.value.dsize -= sizeof(struct ctdb_ltdb_header); + rec.value.dptr += sizeof(struct ctdb_ltdb_header); + + state->count++; + return state->fn(&rec, state->private_data); +} + +static int db_ctdb_traverse_read(struct db_context *db, + int (*fn)(struct db_record *rec, + void *private_data), + void *private_data) +{ + int ret; + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + struct traverse_state state; + + state = (struct traverse_state) { + .db = db, + .fn = fn, + .private_data = private_data, + }; + + if (db->persistent) { + /* for persistent databases we don't need to do a ctdb traverse, + we can do a faster local traverse */ + int nrecs; + + nrecs = tdb_traverse_read(ctx->wtdb->tdb, + traverse_persistent_callback_read, + &state); + if (nrecs == -1) { + return -1; + } + return state.count; + } + + ret = db_ctdbd_traverse(ctx->db_id, traverse_read_callback, &state); + if (ret != 0) { + return -1; + } + return state.count; +} + +static int db_ctdb_get_seqnum(struct db_context *db) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_ctdb_ctx); + return tdb_get_seqnum(ctx->wtdb->tdb); +} + +static size_t db_ctdb_id(struct db_context *db, uint8_t *id, size_t idlen) +{ + struct db_ctdb_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_ctdb_ctx); + + if (idlen >= sizeof(ctx->db_id)) { + memcpy(id, &ctx->db_id, sizeof(ctx->db_id)); + } + + return sizeof(ctx->db_id); +} + +struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *name, + int hash_size, int tdb_flags, + int open_flags, mode_t mode, + enum dbwrap_lock_order lock_order, + uint64_t dbwrap_flags) +{ + struct db_context *result; + struct db_ctdb_ctx *db_ctdb; + char *db_path; + struct loadparm_context *lp_ctx; + TDB_DATA data; + TDB_DATA outdata = {0}; + bool persistent = (tdb_flags & TDB_CLEAR_IF_FIRST) == 0; + int32_t cstatus; + int ret; + + if (!lp_clustering()) { + DEBUG(10, ("Clustering disabled -- no ctdb\n")); + return NULL; + } + + if (!(result = talloc_zero(mem_ctx, struct db_context))) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + if (!(db_ctdb = talloc(result, struct db_ctdb_ctx))) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + result->name = talloc_strdup(result, name); + if (result->name == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + db_ctdb->transaction = NULL; + db_ctdb->db = result; + + ret = ctdbd_db_attach(messaging_ctdb_connection(), name, + &db_ctdb->db_id, persistent); + if (ret != 0) { + DEBUG(0, ("ctdbd_db_attach failed for %s: %s\n", name, + strerror(ret))); + TALLOC_FREE(result); + return NULL; + } + + if (tdb_flags & TDB_SEQNUM) { + data.dptr = (uint8_t *)&db_ctdb->db_id; + data.dsize = sizeof(db_ctdb->db_id); + + ret = ctdbd_control_local(messaging_ctdb_connection(), + CTDB_CONTROL_ENABLE_SEQNUM, + 0, 0, data, + NULL, NULL, &cstatus); + if ((ret != 0) || cstatus != 0) { + DBG_ERR("ctdb_control for enable seqnum " + "failed: %s\n", strerror(ret)); + TALLOC_FREE(result); + return NULL; + } + } + + db_path = ctdbd_dbpath(messaging_ctdb_connection(), db_ctdb, + db_ctdb->db_id); + if (db_path == NULL) { + DBG_ERR("ctdbd_dbpath failed\n"); + TALLOC_FREE(result); + return NULL; + } + + result->persistent = persistent; + result->lock_order = lock_order; + + data.dptr = (uint8_t *)&db_ctdb->db_id; + data.dsize = sizeof(db_ctdb->db_id); + + ret = ctdbd_control_local(messaging_ctdb_connection(), + CTDB_CONTROL_DB_OPEN_FLAGS, + 0, 0, data, NULL, &outdata, &cstatus); + if (ret != 0) { + DBG_ERR(" ctdb control for db_open_flags " + "failed: %s\n", strerror(ret)); + TALLOC_FREE(result); + return NULL; + } + + if (cstatus != 0 || outdata.dsize != sizeof(int)) { + DBG_ERR("ctdb_control for db_open_flags failed\n"); + TALLOC_FREE(outdata.dptr); + TALLOC_FREE(result); + return NULL; + } + + tdb_flags = *(int *)outdata.dptr; + TALLOC_FREE(outdata.dptr); + + if (!result->persistent) { + ret = ctdb_async_ctx_init(NULL, messaging_tevent_context(msg_ctx)); + if (ret != 0) { + DBG_ERR("ctdb_async_ctx_init failed: %s\n", strerror(ret)); + TALLOC_FREE(result); + return NULL; + } + } + + if (!result->persistent && + (dbwrap_flags & DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS)) + { + TDB_DATA indata; + + indata = make_tdb_data((uint8_t *)&db_ctdb->db_id, + sizeof(db_ctdb->db_id)); + + ret = ctdbd_control_local( + messaging_ctdb_connection(), + CTDB_CONTROL_SET_DB_READONLY, 0, 0, + indata, NULL, NULL, &cstatus); + if ((ret != 0) || (cstatus != 0)) { + DEBUG(1, ("CTDB_CONTROL_SET_DB_READONLY failed: " + "%s, %"PRIi32"\n", strerror(ret), cstatus)); + TALLOC_FREE(result); + return NULL; + } + } + + lp_ctx = loadparm_init_s3(db_path, loadparm_s3_helpers()); + + if (hash_size == 0) { + hash_size = lpcfg_tdb_hash_size(lp_ctx, db_path); + } + + db_ctdb->wtdb = tdb_wrap_open(db_ctdb, db_path, hash_size, + lpcfg_tdb_flags(lp_ctx, tdb_flags), + O_RDWR, 0); + talloc_unlink(db_path, lp_ctx); + if (db_ctdb->wtdb == NULL) { + DEBUG(0, ("Could not open tdb %s: %s\n", db_path, strerror(errno))); + TALLOC_FREE(result); + return NULL; + } + talloc_free(db_path); + + /* honor permissions if user has specified O_CREAT */ + if (open_flags & O_CREAT) { + int fd; + fd = tdb_fd(db_ctdb->wtdb->tdb); + ret = fchmod(fd, mode); + if (ret == -1) { + DBG_WARNING("fchmod failed: %s\n", + strerror(errno)); + TALLOC_FREE(result); + return NULL; + } + } + + if (result->persistent) { + db_ctdb->lock_ctx = g_lock_ctx_init(db_ctdb, msg_ctx); + if (db_ctdb->lock_ctx == NULL) { + DEBUG(0, ("g_lock_ctx_init failed\n")); + TALLOC_FREE(result); + return NULL; + } + } + + db_ctdb->warn_unlock_msecs = lp_parm_int(-1, "ctdb", + "unlock_warn_threshold", 5); + db_ctdb->warn_migrate_attempts = lp_parm_int(-1, "ctdb", + "migrate_attempts", 10); + db_ctdb->warn_migrate_msecs = lp_parm_int(-1, "ctdb", + "migrate_duration", 5000); + db_ctdb->warn_locktime_msecs = lp_ctdb_locktime_warn_threshold(); + + result->private_data = (void *)db_ctdb; + result->fetch_locked = db_ctdb_fetch_locked; + result->parse_record = db_ctdb_parse_record; + result->parse_record_send = db_ctdb_parse_record_send; + result->parse_record_recv = db_ctdb_parse_record_recv; + result->traverse = db_ctdb_traverse; + result->traverse_read = db_ctdb_traverse_read; + result->get_seqnum = db_ctdb_get_seqnum; + result->transaction_start = db_ctdb_transaction_start; + result->transaction_commit = db_ctdb_transaction_commit; + result->transaction_cancel = db_ctdb_transaction_cancel; + result->id = db_ctdb_id; + + DEBUG(3,("db_open_ctdb: opened database '%s' with dbid 0x%x\n", + name, db_ctdb->db_id)); + + return result; +} diff --git a/source3/lib/dbwrap/dbwrap_ctdb.h b/source3/lib/dbwrap/dbwrap_ctdb.h new file mode 100644 index 0000000..0b82479 --- /dev/null +++ b/source3/lib/dbwrap/dbwrap_ctdb.h @@ -0,0 +1,40 @@ +/* + Unix SMB/CIFS implementation. + Database interface wrapper around ctdbd + Copyright (C) Volker Lendecke 2007-2009 + Copyright (C) Michael Adam 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __DBWRAP_CTDB_H__ +#define __DBWRAP_CTDB_H__ + +#include <talloc.h> + +#include "dbwrap/dbwrap_private.h" + +struct db_context; +struct ctdbd_connection; + +struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *name, + int hash_size, int tdb_flags, + int open_flags, mode_t mode, + enum dbwrap_lock_order lock_order, + uint64_t dbwrap_flags); +int ctdb_async_ctx_reinit(TALLOC_CTX *mem_ctx, struct tevent_context *ev); + +#endif /* __DBWRAP_CTDB_H__ */ diff --git a/source3/lib/dbwrap/dbwrap_open.c b/source3/lib/dbwrap/dbwrap_open.c new file mode 100644 index 0000000..52c8a94 --- /dev/null +++ b/source3/lib/dbwrap/dbwrap_open.c @@ -0,0 +1,197 @@ +/* + Unix SMB/CIFS implementation. + Database interface wrapper + + Copyright (C) Volker Lendecke 2005-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_private.h" +#include "dbwrap/dbwrap_open.h" +#include "dbwrap/dbwrap_tdb.h" +#include "dbwrap/dbwrap_ctdb.h" +#include "lib/param/param.h" +#include "lib/cluster_support.h" +#include "lib/messages_ctdb.h" +#include "util_tdb.h" +#include "ctdbd_conn.h" +#include "global_contexts.h" + +bool db_is_local(const char *name) +{ + const char *sockname = lp_ctdbd_socket(); + + if (lp_clustering() && socket_exist(sockname)) { + const char *partname; + /* ctdb only wants the file part of the name */ + partname = strrchr(name, '/'); + if (partname) { + partname++; + } else { + partname = name; + } + /* allow ctdb for individual databases to be disabled */ + if (lp_parm_bool(-1, "ctdb", partname, True)) { + return false; + } + } + + return true; +} + +/** + * open a database + */ +struct db_context *db_open(TALLOC_CTX *mem_ctx, + const char *name, + int hash_size, int tdb_flags, + int open_flags, mode_t mode, + enum dbwrap_lock_order lock_order, + uint64_t dbwrap_flags) +{ + struct db_context *result = NULL; + const char *base; + struct loadparm_context *lp_ctx = NULL; + + if ((lock_order != DBWRAP_LOCK_ORDER_NONE) && + !DBWRAP_LOCK_ORDER_VALID(lock_order)) { + errno = EINVAL; + return NULL; + } + + base = strrchr_m(name, '/'); + if (base != NULL) { + base++; + } else { + base = name; + } + + if (tdb_flags & TDB_CLEAR_IF_FIRST) { + bool try_readonly = false; + + if (dbwrap_flags & DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS) { + try_readonly = true; + } + + try_readonly = lp_parm_bool(-1, "dbwrap_optimize_readonly", "*", try_readonly); + try_readonly = lp_parm_bool(-1, "dbwrap_optimize_readonly", base, try_readonly); + + if (try_readonly) { + dbwrap_flags |= DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS; + } else { + dbwrap_flags &= ~DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS; + } + } + + if (tdb_flags & TDB_CLEAR_IF_FIRST) { + bool try_mutex = true; + bool require_mutex = false; + + try_mutex = lp_parm_bool(-1, "dbwrap_tdb_mutexes", "*", try_mutex); + try_mutex = lp_parm_bool(-1, "dbwrap_tdb_mutexes", base, try_mutex); + + if (!lp_use_mmap()) { + /* + * Mutexes require mmap. "use mmap = no" can + * be a debugging tool, so let it override the + * mutex parameters + */ + try_mutex = false; + } + + if (try_mutex && tdb_runtime_check_for_robust_mutexes()) { + tdb_flags |= TDB_MUTEX_LOCKING; + } + + require_mutex = lp_parm_bool(-1, "dbwrap_tdb_require_mutexes", + "*", require_mutex); + require_mutex = lp_parm_bool(-1, "dbwrap_tdb_require_mutexes", + base, require_mutex); + + if (require_mutex) { + tdb_flags |= TDB_MUTEX_LOCKING; + } + } + + if (lp_clustering()) { + const char *sockname; + + sockname = lp_ctdbd_socket(); + if (!socket_exist(sockname)) { + DBG_WARNING("ctdb socket does %s not exist - " + "is ctdb not running?\n", + sockname); + return NULL; + } + + /* allow ctdb for individual databases to be disabled */ + if (lp_parm_bool(-1, "ctdb", base, true)) { + struct messaging_context *msg_ctx; + struct ctdbd_connection *conn; + + /* + * Initialize messaging before getting the ctdb + * connection, as the ctdb connection requires messaging + * to be initialized. + */ + msg_ctx = global_messaging_context(); + if (msg_ctx == NULL) { + DBG_ERR("Failed to initialize messaging\n"); + return NULL; + } + + conn = messaging_ctdb_connection(); + if (conn == NULL) { + DBG_WARNING("No ctdb connection\n"); + errno = EIO; + return NULL; + } + + result = db_open_ctdb(mem_ctx, msg_ctx, base, + hash_size, + tdb_flags, open_flags, mode, + lock_order, dbwrap_flags); + if (result == NULL) { + DBG_ERR("failed to attach to ctdb %s\n", base); + if (errno == 0) { + errno = EIO; + } + return NULL; + } + + return result; + } + } + + lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers()); + + if (hash_size == 0) { + hash_size = lpcfg_tdb_hash_size(lp_ctx, name); + } + tdb_flags = lpcfg_tdb_flags(lp_ctx, tdb_flags); + + result = dbwrap_local_open(mem_ctx, + name, + hash_size, + tdb_flags, + open_flags, + mode, + lock_order, + dbwrap_flags); + talloc_unlink(mem_ctx, lp_ctx); + return result; +} diff --git a/source3/lib/dbwrap/dbwrap_open.h b/source3/lib/dbwrap/dbwrap_open.h new file mode 100644 index 0000000..d14794e --- /dev/null +++ b/source3/lib/dbwrap/dbwrap_open.h @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Database interface wrapper around tdb + + Copyright (C) Volker Lendecke 2005-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __DBWRAP_OPEN_H__ +#define __DBWRAP_OPEN_H__ + +struct db_context; + +/** + * Convenience function to check whether a tdb database + * is local or clustered (ctdb) in a clustered environment. + */ +bool db_is_local(const char *name); + +/** + * Convenience function that will determine whether to + * open a tdb database via the tdb backend or via the ctdb + * backend, based on lp_clustering() and a db-specific + * settings. + */ +struct db_context *db_open(TALLOC_CTX *mem_ctx, + const char *name, + int hash_size, int tdb_flags, + int open_flags, mode_t mode, + enum dbwrap_lock_order lock_order, + uint64_t dbwrap_flags); + +#endif /* __DBWRAP_OPEN_H__ */ diff --git a/source3/lib/dbwrap/dbwrap_watch.c b/source3/lib/dbwrap/dbwrap_watch.c new file mode 100644 index 0000000..df93119 --- /dev/null +++ b/source3/lib/dbwrap/dbwrap_watch.c @@ -0,0 +1,1285 @@ +/* + Unix SMB/CIFS implementation. + Watch dbwrap record changes + Copyright (C) Volker Lendecke 2012 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/util/server_id.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap_watch.h" +#include "dbwrap_open.h" +#include "lib/util/util_tdb.h" +#include "lib/util/tevent_ntstatus.h" +#include "serverid.h" +#include "server_id_watch.h" +#include "lib/dbwrap/dbwrap_private.h" + +struct dbwrap_watcher { + /* + * Process watching this record + */ + struct server_id pid; + /* + * Individual instance inside the waiter, incremented each + * time a watcher is created + */ + uint64_t instance; +}; + +#define DBWRAP_WATCHER_BUF_LENGTH (SERVER_ID_BUF_LENGTH + sizeof(uint64_t)) +#define DBWRAP_MAX_WATCHERS (INT32_MAX/DBWRAP_WATCHER_BUF_LENGTH) + +/* + * Watched records contain a header of: + * + * [uint32] num_records + * 0 [DBWRAP_WATCHER_BUF_LENGTH] \ + * 1 [DBWRAP_WATCHER_BUF_LENGTH] | + * .. |- Array of watchers + * (num_records-1)[DBWRAP_WATCHER_BUF_LENGTH] / + * + * [Remainder of record....] + * + * If this header is absent then this is a + * fresh record of length zero (no watchers). + */ + +static bool dbwrap_watch_rec_parse( + TDB_DATA data, + uint8_t **pwatchers, + size_t *pnum_watchers, + TDB_DATA *pdata) +{ + size_t num_watchers; + + if (data.dsize == 0) { + /* Fresh record */ + if (pwatchers != NULL) { + *pwatchers = NULL; + } + if (pnum_watchers != NULL) { + *pnum_watchers = 0; + } + if (pdata != NULL) { + *pdata = (TDB_DATA) { .dptr = NULL }; + } + return true; + } + + if (data.dsize < sizeof(uint32_t)) { + /* Invalid record */ + return false; + } + + num_watchers = IVAL(data.dptr, 0); + + data.dptr += sizeof(uint32_t); + data.dsize -= sizeof(uint32_t); + + if (num_watchers > data.dsize/DBWRAP_WATCHER_BUF_LENGTH) { + /* Invalid record */ + return false; + } + + if (pwatchers != NULL) { + *pwatchers = data.dptr; + } + if (pnum_watchers != NULL) { + *pnum_watchers = num_watchers; + } + if (pdata != NULL) { + size_t watchers_len = num_watchers * DBWRAP_WATCHER_BUF_LENGTH; + *pdata = (TDB_DATA) { + .dptr = data.dptr + watchers_len, + .dsize = data.dsize - watchers_len + }; + } + + return true; +} + +static void dbwrap_watcher_get(struct dbwrap_watcher *w, + const uint8_t buf[DBWRAP_WATCHER_BUF_LENGTH]) +{ + server_id_get(&w->pid, buf); + w->instance = BVAL(buf, SERVER_ID_BUF_LENGTH); +} + +static void dbwrap_watcher_put(uint8_t buf[DBWRAP_WATCHER_BUF_LENGTH], + const struct dbwrap_watcher *w) +{ + server_id_put(buf, w->pid); + SBVAL(buf, SERVER_ID_BUF_LENGTH, w->instance); +} + +static void dbwrap_watch_log_invalid_record( + struct db_context *db, TDB_DATA key, TDB_DATA value) +{ + DBG_ERR("Found invalid record in %s\n", dbwrap_name(db)); + dump_data(1, key.dptr, key.dsize); + dump_data(1, value.dptr, value.dsize); +} + +struct db_watched_ctx { + struct db_context *backend; + struct messaging_context *msg; +}; + +struct db_watched_record { + struct db_record *rec; + struct server_id self; + struct { + struct db_record *rec; + TDB_DATA initial_value; + bool initial_valid; + } backend; + bool force_fini_store; + struct dbwrap_watcher added; + bool removed_first; + struct { + /* + * The is the number of watcher records + * parsed from backend.initial_value + */ + size_t count; + /* + * This is the pointer to + * the optentially first watcher record + * parsed from backend.initial_value + * + * The pointer actually points to memory + * in backend.initial_value. + * + * Note it might be NULL, if count is 0. + */ + uint8_t *first; + /* + * This remembers if we already + * notified the watchers. + * + * As we only need to do that once during: + * do_locked + * or: + * between rec = fetch_locked + * and + * TALLOC_FREE(rec) + */ + bool alerted; + } watchers; + struct { + struct dbwrap_watcher watcher; + } wakeup; +}; + +static struct db_watched_record *db_record_get_watched_record(struct db_record *rec) +{ + /* + * we can't use wrec = talloc_get_type_abort() here! + * because wrec is likely a stack variable in + * dbwrap_watched_do_locked_fn() + * + * In order to have a least some protection + * we verify the cross reference pointers + * between rec and wrec + */ + struct db_watched_record *wrec = + (struct db_watched_record *)rec->private_data; + SMB_ASSERT(wrec->rec == rec); + return wrec; +} + +static NTSTATUS dbwrap_watched_record_storev( + struct db_watched_record *wrec, + const TDB_DATA *dbufs, int num_dbufs, int flags); +static NTSTATUS dbwrap_watched_storev(struct db_record *rec, + const TDB_DATA *dbufs, int num_dbufs, + int flags); +static NTSTATUS dbwrap_watched_delete(struct db_record *rec); +static void dbwrap_watched_trigger_wakeup(struct messaging_context *msg_ctx, + struct dbwrap_watcher *watcher); +static int db_watched_record_destructor(struct db_watched_record *wrec); + +static void db_watched_record_init(struct db_context *db, + struct messaging_context *msg_ctx, + struct db_record *rec, + struct db_watched_record *wrec, + struct db_record *backend_rec, + TDB_DATA backend_value) +{ + bool ok; + + *rec = (struct db_record) { + .db = db, + .key = dbwrap_record_get_key(backend_rec), + .storev = dbwrap_watched_storev, + .delete_rec = dbwrap_watched_delete, + .private_data = wrec, + }; + + *wrec = (struct db_watched_record) { + .rec = rec, + .self = messaging_server_id(msg_ctx), + .backend = { + .rec = backend_rec, + .initial_value = backend_value, + .initial_valid = true, + }, + }; + + ok = dbwrap_watch_rec_parse(backend_value, + &wrec->watchers.first, + &wrec->watchers.count, + &rec->value); + if (!ok) { + dbwrap_watch_log_invalid_record(rec->db, rec->key, backend_value); + /* wipe invalid data */ + rec->value = (TDB_DATA) { .dptr = NULL, .dsize = 0 }; + } +} + +static struct db_record *dbwrap_watched_fetch_locked( + struct db_context *db, TALLOC_CTX *mem_ctx, TDB_DATA key) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct db_record *rec = NULL; + struct db_watched_record *wrec = NULL; + struct db_record *backend_rec = NULL; + TDB_DATA backend_value = { .dptr = NULL, }; + + rec = talloc_zero(mem_ctx, struct db_record); + if (rec == NULL) { + return NULL; + } + wrec = talloc_zero(rec, struct db_watched_record); + if (wrec == NULL) { + TALLOC_FREE(rec); + return NULL; + } + + backend_rec = dbwrap_fetch_locked(ctx->backend, wrec, key); + if (backend_rec == NULL) { + TALLOC_FREE(rec); + return NULL; + } + backend_value = dbwrap_record_get_value(backend_rec); + + db_watched_record_init(db, ctx->msg, + rec, wrec, + backend_rec, backend_value); + rec->value_valid = true; + talloc_set_destructor(wrec, db_watched_record_destructor); + + return rec; +} + +struct db_watched_record_fini_state { + struct db_watched_record *wrec; + TALLOC_CTX *frame; + TDB_DATA dbufs[2]; + int num_dbufs; + bool ok; +}; + +static void db_watched_record_fini_fetcher(TDB_DATA key, + TDB_DATA backend_value, + void *private_data) +{ + struct db_watched_record_fini_state *state = + (struct db_watched_record_fini_state *)private_data; + struct db_watched_record *wrec = state->wrec; + struct db_record *rec = wrec->rec; + TDB_DATA value = {}; + bool ok; + size_t copy_size; + + /* + * We're within dbwrap_parse_record() + * and backend_value directly points into + * the mmap'ed tdb, so we need to copy the + * parts we require. + */ + + ok = dbwrap_watch_rec_parse(backend_value, NULL, NULL, &value); + if (!ok) { + struct db_context *db = dbwrap_record_get_db(rec); + + dbwrap_watch_log_invalid_record(db, key, backend_value); + + /* wipe invalid data */ + value = (TDB_DATA) { .dptr = NULL, .dsize = 0 }; + } + + copy_size = MIN(rec->value.dsize, value.dsize); + if (copy_size != 0) { + /* + * First reuse the buffer we already had + * as much as we can. + */ + memcpy(rec->value.dptr, value.dptr, copy_size); + state->dbufs[state->num_dbufs++] = rec->value; + value.dsize -= copy_size; + value.dptr += copy_size; + } + + if (value.dsize != 0) { + uint8_t *p = NULL; + + /* + * There's still new data left + * allocate it on callers stackframe + */ + p = talloc_memdup(state->frame, value.dptr, value.dsize); + if (p == NULL) { + DBG_WARNING("failed to allocate %zu bytes\n", + value.dsize); + return; + } + + state->dbufs[state->num_dbufs++] = (TDB_DATA) { + .dptr = p, .dsize = value.dsize, + }; + } + + state->ok = true; +} + +static void db_watched_record_fini(struct db_watched_record *wrec) +{ + struct db_watched_record_fini_state state = { .wrec = wrec, }; + struct db_context *backend = dbwrap_record_get_db(wrec->backend.rec); + struct db_record *rec = wrec->rec; + TDB_DATA key = dbwrap_record_get_key(wrec->backend.rec); + NTSTATUS status; + + if (!wrec->force_fini_store) { + return; + } + + if (wrec->backend.initial_valid) { + if (rec->value.dsize != 0) { + state.dbufs[state.num_dbufs++] = rec->value; + } + } else { + /* + * We need to fetch the current + * value from the backend again, + * which may need to allocate memory + * on the provided stackframe. + */ + + state.frame = talloc_stackframe(); + + status = dbwrap_parse_record(backend, key, + db_watched_record_fini_fetcher, &state); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_parse_record failed: %s\n", + nt_errstr(status)); + TALLOC_FREE(state.frame); + return; + } + if (!state.ok) { + TALLOC_FREE(state.frame); + return; + } + } + + /* + * We don't want to wake up others just because + * we added ourself as new watcher. But if we + * removed outself from the first position + * we need to alert the next one. + */ + if (!wrec->removed_first) { + dbwrap_watched_watch_skip_alerting(rec); + } + + status = dbwrap_watched_record_storev(wrec, state.dbufs, state.num_dbufs, 0); + TALLOC_FREE(state.frame); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_watched_record_storev failed: %s\n", + nt_errstr(status)); + return; + } + + return; +} + +static int db_watched_record_destructor(struct db_watched_record *wrec) +{ + struct db_record *rec = wrec->rec; + struct db_watched_ctx *ctx = talloc_get_type_abort( + rec->db->private_data, struct db_watched_ctx); + + db_watched_record_fini(wrec); + TALLOC_FREE(wrec->backend.rec); + dbwrap_watched_trigger_wakeup(ctx->msg, &wrec->wakeup.watcher); + return 0; +} + +struct dbwrap_watched_do_locked_state { + struct db_context *db; + struct messaging_context *msg_ctx; + struct db_watched_record *wrec; + struct db_record *rec; + void (*fn)(struct db_record *rec, + TDB_DATA value, + void *private_data); + void *private_data; +}; + +static void dbwrap_watched_do_locked_fn( + struct db_record *backend_rec, + TDB_DATA backend_value, + void *private_data) +{ + struct dbwrap_watched_do_locked_state *state = + (struct dbwrap_watched_do_locked_state *)private_data; + + db_watched_record_init(state->db, state->msg_ctx, + state->rec, state->wrec, + backend_rec, backend_value); + + state->fn(state->rec, state->rec->value, state->private_data); + + db_watched_record_fini(state->wrec); +} + +static NTSTATUS dbwrap_watched_do_locked(struct db_context *db, TDB_DATA key, + void (*fn)(struct db_record *rec, + TDB_DATA value, + void *private_data), + void *private_data) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct db_watched_record wrec; + struct db_record rec; + struct dbwrap_watched_do_locked_state state = { + .db = db, .msg_ctx = ctx->msg, + .rec = &rec, .wrec = &wrec, + .fn = fn, .private_data = private_data, + }; + NTSTATUS status; + + status = dbwrap_do_locked( + ctx->backend, key, dbwrap_watched_do_locked_fn, &state); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status)); + return status; + } + + DBG_DEBUG("dbwrap_watched_do_locked_fn returned\n"); + + dbwrap_watched_trigger_wakeup(state.msg_ctx, &wrec.wakeup.watcher); + + return NT_STATUS_OK; +} + +static void dbwrap_watched_record_prepare_wakeup( + struct db_watched_record *wrec) +{ + /* + * Wakeup only needs to happen once (if at all) + */ + if (wrec->watchers.alerted) { + /* already done */ + return; + } + wrec->watchers.alerted = true; + + if (wrec->watchers.count == 0) { + DBG_DEBUG("No watchers\n"); + return; + } + + while (wrec->watchers.count != 0) { + struct server_id_buf tmp; + bool exists; + + dbwrap_watcher_get(&wrec->wakeup.watcher, wrec->watchers.first); + exists = serverid_exists(&wrec->wakeup.watcher.pid); + if (!exists) { + DBG_DEBUG("Discard non-existing waiter %s:%"PRIu64"\n", + server_id_str_buf(wrec->wakeup.watcher.pid, &tmp), + wrec->wakeup.watcher.instance); + wrec->watchers.first += DBWRAP_WATCHER_BUF_LENGTH; + wrec->watchers.count -= 1; + continue; + } + + /* + * We will only wakeup the first waiter, via + * dbwrap_watched_trigger_wakeup(), but keep + * all (including the first one) in the list that + * will be flushed back to the backend record + * again. Waiters are removing their entries + * via dbwrap_watched_watch_remove_instance() + * when they no longer want to monitor the record. + */ + DBG_DEBUG("Will alert first waiter %s:%"PRIu64"\n", + server_id_str_buf(wrec->wakeup.watcher.pid, &tmp), + wrec->wakeup.watcher.instance); + break; + } +} + +static void dbwrap_watched_trigger_wakeup(struct messaging_context *msg_ctx, + struct dbwrap_watcher *watcher) +{ + struct server_id_buf tmp; + uint8_t instance_buf[8]; + NTSTATUS status; + + if (watcher->instance == 0) { + DBG_DEBUG("No one to wakeup\n"); + return; + } + + DBG_DEBUG("Alerting %s:%"PRIu64"\n", + server_id_str_buf(watcher->pid, &tmp), + watcher->instance); + + SBVAL(instance_buf, 0, watcher->instance); + + status = messaging_send_buf( + msg_ctx, + watcher->pid, + MSG_DBWRAP_MODIFIED, + instance_buf, + sizeof(instance_buf)); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("messaging_send_buf to %s failed: %s - ignoring...\n", + server_id_str_buf(watcher->pid, &tmp), + nt_errstr(status)); + } +} + +static NTSTATUS dbwrap_watched_record_storev( + struct db_watched_record *wrec, + const TDB_DATA *dbufs, int num_dbufs, int flags) +{ + uint8_t num_watchers_buf[4] = { 0 }; + uint8_t add_buf[DBWRAP_WATCHER_BUF_LENGTH]; + size_t num_store_watchers; + TDB_DATA my_dbufs[num_dbufs+3]; + int num_my_dbufs = 0; + NTSTATUS status; + size_t add_count = 0; + + dbwrap_watched_record_prepare_wakeup(wrec); + + wrec->backend.initial_valid = false; + wrec->force_fini_store = false; + + if (wrec->added.pid.pid != 0) { + dbwrap_watcher_put(add_buf, &wrec->added); + add_count = 1; + } + + num_store_watchers = wrec->watchers.count + add_count; + if (num_store_watchers == 0 && num_dbufs == 0) { + status = dbwrap_record_delete(wrec->backend.rec); + return status; + } + if (num_store_watchers >= DBWRAP_MAX_WATCHERS) { + DBG_WARNING("Can't handle %zu watchers\n", + num_store_watchers); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + SIVAL(num_watchers_buf, 0, num_store_watchers); + + my_dbufs[num_my_dbufs++] = (TDB_DATA) { + .dptr = num_watchers_buf, .dsize = sizeof(num_watchers_buf), + }; + if (wrec->watchers.count != 0) { + my_dbufs[num_my_dbufs++] = (TDB_DATA) { + .dptr = wrec->watchers.first, .dsize = wrec->watchers.count * DBWRAP_WATCHER_BUF_LENGTH, + }; + } + if (add_count != 0) { + my_dbufs[num_my_dbufs++] = (TDB_DATA) { + .dptr = add_buf, + .dsize = sizeof(add_buf), + }; + } + if (num_dbufs != 0) { + memcpy(my_dbufs+num_my_dbufs, dbufs, num_dbufs * sizeof(*dbufs)); + num_my_dbufs += num_dbufs; + } + + SMB_ASSERT(num_my_dbufs <= ARRAY_SIZE(my_dbufs)); + + status = dbwrap_record_storev( + wrec->backend.rec, my_dbufs, num_my_dbufs, flags); + return status; +} + +static NTSTATUS dbwrap_watched_storev(struct db_record *rec, + const TDB_DATA *dbufs, int num_dbufs, + int flags) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + + return dbwrap_watched_record_storev(wrec, dbufs, num_dbufs, flags); +} + +static NTSTATUS dbwrap_watched_delete(struct db_record *rec) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + + /* + * dbwrap_watched_record_storev() will figure out + * if the record should be deleted or if there are still + * watchers to be stored. + */ + return dbwrap_watched_record_storev(wrec, NULL, 0, 0); +} + +struct dbwrap_watched_traverse_state { + int (*fn)(struct db_record *rec, void *private_data); + void *private_data; +}; + +static int dbwrap_watched_traverse_fn(struct db_record *rec, + void *private_data) +{ + struct dbwrap_watched_traverse_state *state = private_data; + struct db_record prec = *rec; + bool ok; + + ok = dbwrap_watch_rec_parse(rec->value, NULL, NULL, &prec.value); + if (!ok) { + return 0; + } + if (prec.value.dsize == 0) { + return 0; + } + prec.value_valid = true; + + return state->fn(&prec, state->private_data); +} + +static int dbwrap_watched_traverse(struct db_context *db, + int (*fn)(struct db_record *rec, + void *private_data), + void *private_data) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct dbwrap_watched_traverse_state state = { + .fn = fn, .private_data = private_data }; + NTSTATUS status; + int ret; + + status = dbwrap_traverse( + ctx->backend, dbwrap_watched_traverse_fn, &state, &ret); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + return ret; +} + +static int dbwrap_watched_traverse_read(struct db_context *db, + int (*fn)(struct db_record *rec, + void *private_data), + void *private_data) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct dbwrap_watched_traverse_state state = { + .fn = fn, .private_data = private_data }; + NTSTATUS status; + int ret; + + status = dbwrap_traverse_read( + ctx->backend, dbwrap_watched_traverse_fn, &state, &ret); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + return ret; +} + +static int dbwrap_watched_get_seqnum(struct db_context *db) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + return dbwrap_get_seqnum(ctx->backend); +} + +static int dbwrap_watched_transaction_start(struct db_context *db) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + return dbwrap_transaction_start(ctx->backend); +} + +static int dbwrap_watched_transaction_commit(struct db_context *db) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + return dbwrap_transaction_commit(ctx->backend); +} + +static int dbwrap_watched_transaction_cancel(struct db_context *db) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + return dbwrap_transaction_cancel(ctx->backend); +} + +struct dbwrap_watched_parse_record_state { + struct db_context *db; + void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data); + void *private_data; + bool ok; +}; + +static void dbwrap_watched_parse_record_parser(TDB_DATA key, TDB_DATA data, + void *private_data) +{ + struct dbwrap_watched_parse_record_state *state = private_data; + TDB_DATA userdata; + + state->ok = dbwrap_watch_rec_parse(data, NULL, NULL, &userdata); + if (!state->ok) { + dbwrap_watch_log_invalid_record(state->db, key, data); + return; + } + + state->parser(key, userdata, state->private_data); +} + +static NTSTATUS dbwrap_watched_parse_record( + struct db_context *db, TDB_DATA key, + void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), + void *private_data) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct dbwrap_watched_parse_record_state state = { + .db = db, + .parser = parser, + .private_data = private_data, + }; + NTSTATUS status; + + status = dbwrap_parse_record( + ctx->backend, key, dbwrap_watched_parse_record_parser, &state); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!state.ok) { + return NT_STATUS_NOT_FOUND; + } + return NT_STATUS_OK; +} + +static void dbwrap_watched_parse_record_done(struct tevent_req *subreq); + +static struct tevent_req *dbwrap_watched_parse_record_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct db_context *db, + TDB_DATA key, + void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), + void *private_data, + enum dbwrap_req_state *req_state) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct dbwrap_watched_parse_record_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct dbwrap_watched_parse_record_state); + if (req == NULL) { + *req_state = DBWRAP_REQ_ERROR; + return NULL; + } + + *state = (struct dbwrap_watched_parse_record_state) { + .parser = parser, + .private_data = private_data, + .ok = true, + }; + + subreq = dbwrap_parse_record_send(state, + ev, + ctx->backend, + key, + dbwrap_watched_parse_record_parser, + state, + req_state); + if (tevent_req_nomem(subreq, req)) { + *req_state = DBWRAP_REQ_ERROR; + return tevent_req_post(req, ev); + } + + tevent_req_set_callback(subreq, dbwrap_watched_parse_record_done, req); + return req; +} + +static void dbwrap_watched_parse_record_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dbwrap_watched_parse_record_state *state = tevent_req_data( + req, struct dbwrap_watched_parse_record_state); + NTSTATUS status; + + status = dbwrap_parse_record_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (!state->ok) { + tevent_req_nterror(req, NT_STATUS_NOT_FOUND); + return; + } + + tevent_req_done(req); + return; +} + +static NTSTATUS dbwrap_watched_parse_record_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +static int dbwrap_watched_exists(struct db_context *db, TDB_DATA key) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + + return dbwrap_exists(ctx->backend, key); +} + +static size_t dbwrap_watched_id(struct db_context *db, uint8_t *id, + size_t idlen) +{ + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + + return dbwrap_db_id(ctx->backend, id, idlen); +} + +struct db_context *db_open_watched(TALLOC_CTX *mem_ctx, + struct db_context **backend, + struct messaging_context *msg) +{ + struct db_context *db; + struct db_watched_ctx *ctx; + + db = talloc_zero(mem_ctx, struct db_context); + if (db == NULL) { + return NULL; + } + ctx = talloc_zero(db, struct db_watched_ctx); + if (ctx == NULL) { + TALLOC_FREE(db); + return NULL; + } + db->private_data = ctx; + + ctx->msg = msg; + + ctx->backend = talloc_move(ctx, backend); + db->lock_order = ctx->backend->lock_order; + ctx->backend->lock_order = DBWRAP_LOCK_ORDER_NONE; + + db->fetch_locked = dbwrap_watched_fetch_locked; + db->do_locked = dbwrap_watched_do_locked; + db->traverse = dbwrap_watched_traverse; + db->traverse_read = dbwrap_watched_traverse_read; + db->get_seqnum = dbwrap_watched_get_seqnum; + db->transaction_start = dbwrap_watched_transaction_start; + db->transaction_commit = dbwrap_watched_transaction_commit; + db->transaction_cancel = dbwrap_watched_transaction_cancel; + db->parse_record = dbwrap_watched_parse_record; + db->parse_record_send = dbwrap_watched_parse_record_send; + db->parse_record_recv = dbwrap_watched_parse_record_recv; + db->exists = dbwrap_watched_exists; + db->id = dbwrap_watched_id; + db->name = dbwrap_name(ctx->backend); + + return db; +} + +uint64_t dbwrap_watched_watch_add_instance(struct db_record *rec) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + static uint64_t global_instance = 1; + + SMB_ASSERT(wrec->added.instance == 0); + + wrec->added = (struct dbwrap_watcher) { + .pid = wrec->self, + .instance = global_instance++, + }; + + wrec->force_fini_store = true; + + return wrec->added.instance; +} + +void dbwrap_watched_watch_remove_instance(struct db_record *rec, uint64_t instance) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + struct dbwrap_watcher clear_watcher = { + .pid = wrec->self, + .instance = instance, + }; + size_t i; + struct server_id_buf buf; + + if (instance == 0) { + return; + } + + if (wrec->added.instance == instance) { + SMB_ASSERT(server_id_equal(&wrec->added.pid, &wrec->self)); + DBG_DEBUG("Watcher %s:%"PRIu64" reverted from adding\n", + server_id_str_buf(clear_watcher.pid, &buf), + clear_watcher.instance); + ZERO_STRUCT(wrec->added); + } + + for (i=0; i < wrec->watchers.count; i++) { + struct dbwrap_watcher watcher; + size_t off = i*DBWRAP_WATCHER_BUF_LENGTH; + size_t next_off; + size_t full_len; + size_t move_len; + + dbwrap_watcher_get(&watcher, wrec->watchers.first + off); + + if (clear_watcher.instance != watcher.instance) { + continue; + } + if (!server_id_equal(&clear_watcher.pid, &watcher.pid)) { + continue; + } + + wrec->force_fini_store = true; + + if (i == 0) { + DBG_DEBUG("Watcher %s:%"PRIu64" removed from first position of %zu\n", + server_id_str_buf(clear_watcher.pid, &buf), + clear_watcher.instance, + wrec->watchers.count); + wrec->watchers.first += DBWRAP_WATCHER_BUF_LENGTH; + wrec->watchers.count -= 1; + wrec->removed_first = true; + return; + } + if (i == (wrec->watchers.count-1)) { + DBG_DEBUG("Watcher %s:%"PRIu64" removed from last position of %zu\n", + server_id_str_buf(clear_watcher.pid, &buf), + clear_watcher.instance, + wrec->watchers.count); + wrec->watchers.count -= 1; + return; + } + + DBG_DEBUG("Watcher %s:%"PRIu64" cleared at position %zu from %zu\n", + server_id_str_buf(clear_watcher.pid, &buf), + clear_watcher.instance, i+1, + wrec->watchers.count); + + next_off = off + DBWRAP_WATCHER_BUF_LENGTH; + full_len = wrec->watchers.count * DBWRAP_WATCHER_BUF_LENGTH; + move_len = full_len - next_off; + memmove(wrec->watchers.first + off, + wrec->watchers.first + next_off, + move_len); + wrec->watchers.count -= 1; + return; + } + + DBG_DEBUG("Watcher %s:%"PRIu64" not found in %zu watchers\n", + server_id_str_buf(clear_watcher.pid, &buf), + clear_watcher.instance, + wrec->watchers.count); + return; +} + +void dbwrap_watched_watch_skip_alerting(struct db_record *rec) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + + wrec->wakeup.watcher = (struct dbwrap_watcher) { .instance = 0, }; + wrec->watchers.alerted = true; +} + +void dbwrap_watched_watch_reset_alerting(struct db_record *rec) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + + wrec->wakeup.watcher = (struct dbwrap_watcher) { .instance = 0, }; + wrec->watchers.alerted = false; +} + +void dbwrap_watched_watch_force_alerting(struct db_record *rec) +{ + struct db_watched_record *wrec = db_record_get_watched_record(rec); + + dbwrap_watched_record_prepare_wakeup(wrec); +} + +struct dbwrap_watched_watch_state { + struct db_context *db; + TDB_DATA key; + struct dbwrap_watcher watcher; + struct server_id blocker; + bool blockerdead; +}; + +static bool dbwrap_watched_msg_filter(struct messaging_rec *rec, + void *private_data); +static void dbwrap_watched_watch_done(struct tevent_req *subreq); +static void dbwrap_watched_watch_blocker_died(struct tevent_req *subreq); +static int dbwrap_watched_watch_state_destructor( + struct dbwrap_watched_watch_state *state); + +struct tevent_req *dbwrap_watched_watch_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct db_record *rec, + uint64_t resumed_instance, + struct server_id blocker) +{ + struct db_context *db = dbwrap_record_get_db(rec); + struct db_watched_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_watched_ctx); + struct db_watched_record *wrec = db_record_get_watched_record(rec); + struct tevent_req *req, *subreq; + struct dbwrap_watched_watch_state *state; + uint64_t instance; + + req = tevent_req_create(mem_ctx, &state, + struct dbwrap_watched_watch_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->blocker = blocker; + + if (ctx->msg == NULL) { + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + if (resumed_instance == 0 && wrec->added.instance == 0) { + /* + * Adding a new instance + */ + instance = dbwrap_watched_watch_add_instance(rec); + } else if (resumed_instance != 0 && wrec->added.instance == 0) { + /* + * Resuming an existing instance that was + * already present before do_locked started + */ + instance = resumed_instance; + } else if (resumed_instance == wrec->added.instance) { + /* + * The caller used dbwrap_watched_watch_add_instance() + * already during this do_locked() invocation. + */ + instance = resumed_instance; + } else { + tevent_req_nterror(req, NT_STATUS_REQUEST_NOT_ACCEPTED); + return tevent_req_post(req, ev); + } + + state->watcher = (struct dbwrap_watcher) { + .pid = messaging_server_id(ctx->msg), + .instance = instance, + }; + + state->key = tdb_data_talloc_copy(state, rec->key); + if (tevent_req_nomem(state->key.dptr, req)) { + return tevent_req_post(req, ev); + } + + subreq = messaging_filtered_read_send( + state, ev, ctx->msg, dbwrap_watched_msg_filter, state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, dbwrap_watched_watch_done, req); + + talloc_set_destructor(state, dbwrap_watched_watch_state_destructor); + + if (blocker.pid != 0) { + subreq = server_id_watch_send(state, ev, blocker); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, dbwrap_watched_watch_blocker_died, req); + } + + return req; +} + +static void dbwrap_watched_watch_blocker_died(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dbwrap_watched_watch_state *state = tevent_req_data( + req, struct dbwrap_watched_watch_state); + int ret; + + ret = server_id_watch_recv(subreq, NULL); + TALLOC_FREE(subreq); + if (ret != 0) { + tevent_req_nterror(req, map_nt_error_from_unix(ret)); + return; + } + state->blockerdead = true; + tevent_req_done(req); +} + +static void dbwrap_watched_watch_state_destructor_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct dbwrap_watched_watch_state *state = talloc_get_type_abort( + private_data, struct dbwrap_watched_watch_state); + + /* + * Here we just remove ourself from the in memory + * watchers array and let db_watched_record_fini() + * call dbwrap_watched_record_storev() to do the magic + * of writing back the modified in memory copy. + */ + dbwrap_watched_watch_remove_instance(rec, state->watcher.instance); + return; +} + +static int dbwrap_watched_watch_state_destructor( + struct dbwrap_watched_watch_state *state) +{ + NTSTATUS status; + + status = dbwrap_do_locked( + state->db, + state->key, + dbwrap_watched_watch_state_destructor_fn, + state); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_do_locked failed: %s\n", + nt_errstr(status)); + } + return 0; +} + +static bool dbwrap_watched_msg_filter(struct messaging_rec *rec, + void *private_data) +{ + struct dbwrap_watched_watch_state *state = talloc_get_type_abort( + private_data, struct dbwrap_watched_watch_state); + uint64_t instance; + + if (rec->msg_type != MSG_DBWRAP_MODIFIED) { + return false; + } + if (rec->num_fds != 0) { + return false; + } + + if (rec->buf.length != sizeof(instance)) { + DBG_DEBUG("Got size %zu, expected %zu\n", + rec->buf.length, + sizeof(instance)); + return false; + } + + instance = BVAL(rec->buf.data, 0); + + if (instance != state->watcher.instance) { + DBG_DEBUG("Got instance %"PRIu64", expected %"PRIu64"\n", + instance, + state->watcher.instance); + return false; + } + + return true; +} + +static void dbwrap_watched_watch_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dbwrap_watched_watch_state *state = tevent_req_data( + req, struct dbwrap_watched_watch_state); + struct messaging_rec *rec; + int ret; + + ret = messaging_filtered_read_recv(subreq, state, &rec); + TALLOC_FREE(subreq); + if (ret != 0) { + tevent_req_nterror(req, map_nt_error_from_unix(ret)); + return; + } + tevent_req_done(req); +} + +NTSTATUS dbwrap_watched_watch_recv(struct tevent_req *req, + uint64_t *pkeep_instance, + bool *blockerdead, + struct server_id *blocker) +{ + struct dbwrap_watched_watch_state *state = tevent_req_data( + req, struct dbwrap_watched_watch_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + if (pkeep_instance != NULL) { + *pkeep_instance = state->watcher.instance; + /* + * No need to remove ourselves anymore, + * the caller will take care of removing itself. + */ + talloc_set_destructor(state, NULL); + } + if (blockerdead != NULL) { + *blockerdead = state->blockerdead; + } + if (blocker != NULL) { + *blocker = state->blocker; + } + tevent_req_received(req); + return NT_STATUS_OK; +} + diff --git a/source3/lib/dbwrap/dbwrap_watch.h b/source3/lib/dbwrap/dbwrap_watch.h new file mode 100644 index 0000000..1b93e13 --- /dev/null +++ b/source3/lib/dbwrap/dbwrap_watch.h @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Watch dbwrap record changes + Copyright (C) Volker Lendecke 2012 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __DBWRAP_WATCH_H__ +#define __DBWRAP_WATCH_H__ + +#include <tevent.h> +#include "dbwrap/dbwrap.h" +#include "messages.h" + +struct db_context *db_open_watched(TALLOC_CTX *mem_ctx, + struct db_context **backend, + struct messaging_context *msg); +uint64_t dbwrap_watched_watch_add_instance(struct db_record *rec); +void dbwrap_watched_watch_remove_instance(struct db_record *rec, uint64_t instance); +void dbwrap_watched_watch_skip_alerting(struct db_record *rec); +void dbwrap_watched_watch_reset_alerting(struct db_record *rec); +void dbwrap_watched_watch_force_alerting(struct db_record *rec); +struct tevent_req *dbwrap_watched_watch_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct db_record *rec, + uint64_t resume_instance, + struct server_id blocker); +NTSTATUS dbwrap_watched_watch_recv(struct tevent_req *req, + uint64_t *pkeep_instance, + bool *blockerdead, + struct server_id *blocker); + +#endif /* __DBWRAP_WATCH_H__ */ diff --git a/source3/lib/dmallocmsg.c b/source3/lib/dmallocmsg.c new file mode 100644 index 0000000..889f4ed --- /dev/null +++ b/source3/lib/dmallocmsg.c @@ -0,0 +1,79 @@ +/* + samba -- Unix SMB/CIFS implementation. + Copyright (C) 2002 by Martin Pool + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "messages.h" + +/** + * @file dmallocmsg.c + * + * Glue code to cause dmalloc info to come out when we receive a prod + * over samba messaging. + **/ + +#ifdef ENABLE_DMALLOC +static unsigned long our_dm_mark = 0; +#endif /* ENABLE_DMALLOC */ + + +/** + * Respond to a POOL_USAGE message by sending back string form of memory + * usage stats. + **/ +static void msg_req_dmalloc_mark(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ +#ifdef ENABLE_DMALLOC + our_dm_mark = dmalloc_mark(); + DEBUG(2,("Got MSG_REQ_DMALLOC_MARK: mark set\n")); +#else + DEBUG(2,("Got MSG_REQ_DMALLOC_MARK but dmalloc not in this process\n")); +#endif +} + + + +static void msg_req_dmalloc_log_changed(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ +#ifdef ENABLE_DMALLOC + dmalloc_log_changed(our_dm_mark, True, True, True); + DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED: done\n")); +#else + DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED but dmalloc not in this process\n")); +#endif +} + + +/** + * Register handler for MSG_REQ_POOL_USAGE + **/ +void register_dmalloc_msgs(struct messaging_context *msg_ctx) +{ + messaging_register(msg_ctx, NULL, MSG_REQ_DMALLOC_MARK, + msg_req_dmalloc_mark); + messaging_register(msg_ctx, NULL, MSG_REQ_DMALLOC_LOG_CHANGED, + msg_req_dmalloc_log_changed); + DEBUG(2, ("Registered MSG_REQ_DMALLOC_MARK and LOG_CHANGED\n")); +} diff --git a/source3/lib/dumpcore.c b/source3/lib/dumpcore.c new file mode 100644 index 0000000..0c91206 --- /dev/null +++ b/source3/lib/dumpcore.c @@ -0,0 +1,343 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + + Copyright (C) Andrew Tridgell 1992-2011 + + based on old fault.c code, which had: + + Copyright (C) Jeremy Allison 2001-2007 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + Copyright (C) James Peach 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" + +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif + +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + +static char *corepath; +static bool using_helper_binary = false; + +/** + * Build up the default corepath as "<logbase>/cores/<progname>" + */ +static char *get_default_corepath(const char *logbase, const char *progname) +{ + const mode_t mode = 0700; + const uid_t uid = getuid(); + char *tmp_corepath; + + /* Setup core dir in logbase. */ + tmp_corepath = talloc_asprintf(NULL, "%s/cores", logbase); + if (!tmp_corepath) { + DEBUG(0, ("Out of memory\n")); + return NULL; + } + + if (!directory_create_or_exist_strict(tmp_corepath, uid, mode)) { + DEBUG(0, ("Failed to create %s for user %d with mode 0%o\n", + tmp_corepath, (int)uid, (int)mode)); + goto err_out; + } + + /* Setup progname-specific core subdir */ + tmp_corepath = talloc_asprintf_append(tmp_corepath, "/%s", progname); + if (!tmp_corepath) { + DEBUG(0, ("Out of memory\n")); + goto err_out; + } + + if (!directory_create_or_exist(tmp_corepath, mode)) { + DEBUG(0, ("Failed to create %s for user %d with mode 0%o\n", + tmp_corepath, (int)uid, (int)mode)); + goto err_out; + } + + return tmp_corepath; + + err_out: + talloc_free(tmp_corepath); + return NULL; +} + + +/** + * Get the FreeBSD corepath. + * + * On FreeBSD the current working directory is ignored when creating a core + * file. Instead the core directory is controlled via sysctl. This consults + * the value of "kern.corefile" so the correct corepath can be printed out + * before dump_core() calls abort. + */ +#if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME)) +static char *get_freebsd_corepath(void) +{ + char *tmp_corepath = NULL; + char *end = NULL; + size_t len = 128; + int ret; + + /* Loop with increasing sizes so we don't allocate too much. */ + do { + if (len > 1024) { + goto err_out; + } + + tmp_corepath = (char *)talloc_realloc(NULL, tmp_corepath, + char, len); + if (!tmp_corepath) { + return NULL; + } + + ret = sysctlbyname("kern.corefile", tmp_corepath, &len, NULL, + 0); + if (ret == -1) { + if (errno != ENOMEM) { + DEBUG(0, ("sysctlbyname failed getting " + "kern.corefile %s\n", + strerror(errno))); + goto err_out; + } + + /* Not a large enough array, try a bigger one. */ + len = len << 1; + } + } while (ret == -1); + + /* Strip off the common filename expansion */ + if ((end = strrchr_m(tmp_corepath, '/'))) { + *end = '\0'; + } + + return tmp_corepath; + + err_out: + if (tmp_corepath) { + talloc_free(tmp_corepath); + } + return NULL; +} +#endif + +#if defined(HAVE_SYS_KERNEL_PROC_CORE_PATTERN) + +/** + * Get the Linux corepath. + * + * On Linux the contents of /proc/sys/kernel/core_pattern indicates the + * location of the core path. + */ +static char *get_linux_corepath(void) +{ + char *end; + int fd; + char *result; + + fd = open("/proc/sys/kernel/core_pattern", O_RDONLY, 0); + if (fd == -1) { + return NULL; + } + + result = afdgets(fd, NULL, 0); + close(fd); + + if (result == NULL) { + return NULL; + } + + if (result[0] != '/') { + /* + * No absolute path, use the default (cwd) + */ + if (result[0] == '|') { + /* + * Core dump handled by helper binaries + */ + using_helper_binary = true; + } + TALLOC_FREE(result); + return NULL; + } + /* Strip off the common filename expansion */ + + end = strrchr_m(result, '/'); + + if ((end != result) /* this would be the only / */ + && (end != NULL)) { + *end = '\0'; + } + return result; +} +#endif + + +/** + * Try getting system-specific corepath if one exists. + * + * If the system doesn't define a corepath, then the default is used. + */ +static char *get_corepath(const char *logbase, const char *progname) +{ +#if (defined(FREEBSD) && defined(HAVE_SYSCTLBYNAME)) + char *tmp_corepath = NULL; + tmp_corepath = get_freebsd_corepath(); + + /* If this has been set correctly, we're done. */ + if (tmp_corepath) { + return tmp_corepath; + } +#endif + +#if defined(HAVE_SYS_KERNEL_PROC_CORE_PATTERN) + char *tmp_corepath = NULL; + tmp_corepath = get_linux_corepath(); + + /* If this has been set correctly, we're done. */ + if (tmp_corepath) { + return tmp_corepath; + } +#endif + + /* Fall back to the default. */ + return get_default_corepath(logbase, progname); +} + +/******************************************************************* +make all the preparations to safely dump a core file +********************************************************************/ + +void dump_core_setup(const char *progname, const char *log_file) +{ + char *logbase = NULL; + char *end = NULL; + + if (log_file && *log_file) { + if (asprintf(&logbase, "%s", log_file) < 0) { + return; + } + if ((end = strrchr_m(logbase, '/'))) { + *end = '\0'; + } + } else { + /* We will end up here if the log file is given on the command + * line by the -l option but the "log file" option is not set + * in smb.conf. + */ + if (asprintf(&logbase, "%s", get_dyn_LOGFILEBASE()) < 0) { + return; + } + } + + SMB_ASSERT(progname != NULL); + + corepath = get_corepath(logbase, progname); + if (!corepath) { + DEBUG(0, ("Unable to setup corepath for %s: %s\n", progname, + strerror(errno))); + goto out; + } + + /* FIXME: if we have a core-plus-pid facility, configurably set + * this up here. + */ + out: + SAFE_FREE(logbase); +} + + void dump_core(void) +{ + static bool called; + + if (called) { + DEBUG(0, ("dump_core() called recursive\n")); + exit(1); + } + called = true; + + /* Note that even if core dumping has been disabled, we still set up + * the core path. This is to handle the case where core dumping is + * turned on in smb.conf and the relevant daemon is not restarted. + */ + if (!lp_enable_core_files()) { + DEBUG(0, ("Exiting on internal error (core file administratively disabled)\n")); + exit(1); + } + +#if DUMP_CORE + /* If we're running as non root we might not be able to dump the core + * file to the corepath. There must not be an unbecome_root() before + * we call abort(). */ + if (geteuid() != sec_initial_uid()) { + become_root(); + } + + if (corepath == NULL) { + DEBUG(0, ("Can not dump core: corepath not set up\n")); + exit(1); + } + + if (*corepath != '\0') { + /* + * Check whether coredump is handled by helper binaries or not. + * If so skip chdir(). + */ + if (!using_helper_binary) { + /* The chdir might fail if we dump core before we finish + * processing the config file. + */ + if (chdir(corepath) != 0) { + DEBUG(0, ("unable to change to %s\n", corepath)); + DEBUGADD(0, ("refusing to dump core\n")); + exit(1); + } + + DEBUG(0,("dumping core in %s\n", corepath)); + } else { + DEBUG(0,("coredump is handled by helper binary " + "specified at /proc/sys/kernel/core_pattern\n")); + } + } + + umask(~(0700)); + dbgflush(); + +#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) + /* On Linux we lose the ability to dump core when we change our user + * ID. We know how to dump core safely, so let's make sure we have our + * dumpable flag set. + */ + prctl(PR_SET_DUMPABLE, 1); +#endif + + /* Ensure we don't have a signal handler for abort. */ +#ifdef SIGABRT + CatchSignal(SIGABRT, SIG_DFL); +#endif + + abort(); + +#else /* DUMP_CORE */ + exit(1); +#endif /* DUMP_CORE */ +} diff --git a/source3/lib/errmap_unix.c b/source3/lib/errmap_unix.c new file mode 100644 index 0000000..3aa2cd1 --- /dev/null +++ b/source3/lib/errmap_unix.c @@ -0,0 +1,158 @@ +/* + * Unix SMB/CIFS implementation. + * map unix to NT errors, an excerpt of libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Tim Potter 2000 + * Copyright (C) Jeremy Allison 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +/* Mapping from Unix, to NT error numbers */ + +static const struct { + int unix_error; + NTSTATUS nt_error; +} unix_nt_errmap[] = { + { EAGAIN, NT_STATUS_NETWORK_BUSY }, + { EINTR, NT_STATUS_RETRY }, +#ifdef ENOBUFS + { ENOBUFS, NT_STATUS_INSUFFICIENT_RESOURCES }, +#endif +#ifdef EWOULDBLOCK + { EWOULDBLOCK, NT_STATUS_NETWORK_BUSY }, +#endif + { EPERM, NT_STATUS_ACCESS_DENIED }, + { EACCES, NT_STATUS_ACCESS_DENIED }, + { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { ENOTDIR, NT_STATUS_NOT_A_DIRECTORY }, + { EIO, NT_STATUS_IO_DEVICE_ERROR }, + { EBADF, NT_STATUS_INVALID_HANDLE }, + { EINVAL, NT_STATUS_INVALID_PARAMETER }, + { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION}, + { ENFILE, NT_STATUS_TOO_MANY_OPENED_FILES }, + { EMFILE, NT_STATUS_TOO_MANY_OPENED_FILES }, + { ENOSPC, NT_STATUS_DISK_FULL }, + { ENOMEM, NT_STATUS_NO_MEMORY }, + { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY}, + { EMSGSIZE, NT_STATUS_PORT_MESSAGE_TOO_LONG }, +#ifdef EPIPE + { EPIPE, NT_STATUS_CONNECTION_DISCONNECTED}, +#endif + { EMLINK, NT_STATUS_TOO_MANY_LINKS }, + { ENOSYS, NT_STATUS_NOT_SUPPORTED }, +#ifdef ELOOP + { ELOOP, NT_STATUS_OBJECT_PATH_NOT_FOUND }, +#endif +#ifdef EFTYPE + { EFTYPE, NT_STATUS_OBJECT_PATH_NOT_FOUND }, +#endif +#ifdef EDQUOT + { EDQUOT, NT_STATUS_DISK_FULL }, /* Windows apps need this, not NT_STATUS_QUOTA_EXCEEDED */ +#endif +#ifdef ENOTEMPTY + { ENOTEMPTY, NT_STATUS_DIRECTORY_NOT_EMPTY }, +#endif +#ifdef EXDEV + { EXDEV, NT_STATUS_NOT_SAME_DEVICE }, +#endif +#ifdef EROFS + { EROFS, NT_STATUS_MEDIA_WRITE_PROTECTED }, +#endif +#ifdef ENAMETOOLONG + { ENAMETOOLONG, NT_STATUS_OBJECT_NAME_INVALID }, +#endif +#ifdef EFBIG + { EFBIG, NT_STATUS_DISK_FULL }, +#endif +#ifdef EADDRINUSE + { EADDRINUSE, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, +#endif +#ifdef ENETUNREACH + { ENETUNREACH, NT_STATUS_NETWORK_UNREACHABLE}, +#endif +#ifdef EHOSTUNREACH + { EHOSTUNREACH, NT_STATUS_HOST_UNREACHABLE}, +#endif +#ifdef ECONNREFUSED + { ECONNREFUSED, NT_STATUS_CONNECTION_REFUSED}, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, NT_STATUS_IO_TIMEOUT}, +#endif +#ifdef ECONNABORTED + { ECONNABORTED, NT_STATUS_CONNECTION_ABORTED}, +#endif +#ifdef ECONNRESET + { ECONNRESET, NT_STATUS_CONNECTION_RESET}, +#endif +#ifdef ENODEV + { ENODEV, NT_STATUS_DEVICE_DOES_NOT_EXIST}, +#endif +#ifdef ENOATTR + { ENOATTR, NT_STATUS_NOT_FOUND }, +#endif +#ifdef ECANCELED + { ECANCELED, NT_STATUS_CANCELLED}, +#endif +#ifdef ENOTSUP + { ENOTSUP, NT_STATUS_NOT_SUPPORTED}, +#endif +#ifdef ETXTBSY + { ETXTBSY, NT_STATUS_SHARING_VIOLATION }, +#endif +#ifdef EOVERFLOW + { EOVERFLOW, NT_STATUS_ALLOTTED_SPACE_EXCEEDED }, +#endif + { EINPROGRESS, NT_STATUS_MORE_PROCESSING_REQUIRED }, +#ifdef ERANGE + { ERANGE, NT_STATUS_INTEGER_OVERFLOW }, +#endif +#ifdef ENXIO + { ENXIO, NT_STATUS_ILLEGAL_FUNCTION }, +#endif +}; + +/********************************************************************* + Map an NT error code from a Unix error code. +*********************************************************************/ + +NTSTATUS map_nt_error_from_unix(int unix_error) +{ + size_t i = 0; + + if (unix_error == 0) { + /* we map this to an error, not success, as this + function is only called in an error path. Lots of + our virtualised functions may fail without making a + unix system call that fails (such as when they are + checking for some handle existing), so unix_error + may be unset + */ + return NT_STATUS_UNSUCCESSFUL; + } + + /* Look through list */ + for (i=0;i<ARRAY_SIZE(unix_nt_errmap);i++) { + if (unix_nt_errmap[i].unix_error == unix_error) { + return unix_nt_errmap[i].nt_error; + } + } + + /* Default return */ + return NT_STATUS_ACCESS_DENIED; +} diff --git a/source3/lib/eventlog/eventlog.c b/source3/lib/eventlog/eventlog.c new file mode 100644 index 0000000..031c749 --- /dev/null +++ b/source3/lib/eventlog/eventlog.c @@ -0,0 +1,1066 @@ +/* + * Unix SMB/CIFS implementation. + * Eventlog utility routines + * Copyright (C) Marcin Krzysztof Porwit 2005, + * Copyright (C) Brian Moran 2005. + * Copyright (C) Gerald (Jerry) Carter 2005. + * Copyright (C) Guenther Deschner 2009. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/eventlog/eventlog.h" +#include "../libcli/security/security.h" +#include "util_tdb.h" + +/* maintain a list of open eventlog tdbs with reference counts */ + +static ELOG_TDB *open_elog_list; + +/******************************************************************** + Init an Eventlog TDB, and return it. If null, something bad + happened. +********************************************************************/ + +TDB_CONTEXT *elog_init_tdb( char *tdbfilename ) +{ + TDB_CONTEXT *tdb; + + DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n", + tdbfilename)); + + tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT, + O_RDWR|O_CREAT|O_TRUNC, 0660 ); + + if ( !tdb ) { + DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) ); + return NULL; + } + + /* initialize with defaults, copy real values in here from registry */ + + tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 ); + tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 ); + tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 ); + tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 ); + + tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 ); + + return tdb; +} + +/******************************************************************** + make the tdb file name for an event log, given destination buffer + and size. Caller must free memory. +********************************************************************/ + +char *elog_tdbname(TALLOC_CTX *ctx, const char *name ) +{ + char *path; + char *file; + char *tdbname; + + path = state_path(talloc_tos(), "eventlog"); + if (!path) { + return NULL; + } + + file = talloc_asprintf_strlower_m(path, "%s.tdb", name); + if (!file) { + talloc_free(path); + return NULL; + } + + tdbname = talloc_asprintf(ctx, "%s/%s", path, file); + if (!tdbname) { + talloc_free(path); + return NULL; + } + + talloc_free(path); + return tdbname; +} + + +/******************************************************************** + this function is used to count up the number of bytes in a + particular TDB +********************************************************************/ + +struct trav_size_struct { + int size; + int rec_count; +}; + +static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data, + void *state ) +{ + struct trav_size_struct *tsize = (struct trav_size_struct *)state; + + tsize->size += data.dsize; + tsize->rec_count++; + + return 0; +} + +/******************************************************************** + returns the size of the eventlog, and if MaxSize is a non-null + ptr, puts the MaxSize there. This is purely a way not to have yet + another function that solely reads the maxsize of the eventlog. + Yeah, that's it. +********************************************************************/ + +int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention ) +{ + struct trav_size_struct tsize; + + if ( !tdb ) + return 0; + + ZERO_STRUCT( tsize ); + + tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize ); + + if ( MaxSize != NULL ) { + *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE ); + } + + if ( Retention != NULL ) { + *Retention = tdb_fetch_int32( tdb, EVT_RETENTION ); + } + + DEBUG( 1, + ( "eventlog size: [%d] for [%d] records\n", tsize.size, + tsize.rec_count ) ); + return tsize.size; +} + +/******************************************************************** + Discard early event logs until we have enough for 'needed' bytes... + NO checking done beforehand to see that we actually need to do + this, and it's going to pluck records one-by-one. So, it's best + to determine that this needs to be done before doing it. + + Setting whack_by_date to True indicates that eventlogs falling + outside of the retention range need to go... + + return True if we made enough room to accommodate needed bytes +********************************************************************/ + +static bool make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32_t needed, + bool whack_by_date ) +{ + int32_t start_record, i, new_start; + int32_t end_record; + int32_t reclen, tresv1, trecnum, timegen, timewr; + int nbytes, len, Retention, MaxSize; + TDB_DATA key, ret; + time_t current_time, exp_time; + + /* discard some eventlogs */ + + /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos, + although records not necessarily guaranteed to have successive times */ + /* */ + + /* lock */ + tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 ); + /* read */ + end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD ); + start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY ); + Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION ); + MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE ); + + time( ¤t_time ); + + /* calculate ... */ + exp_time = current_time - Retention; /* discard older than exp_time */ + + /* todo - check for sanity in next_record */ + nbytes = 0; + + DEBUG( 3, + ( "MaxSize [%d] Retention [%d] Current Time [%u] exp_time [%u]\n", + MaxSize, Retention, (unsigned int)current_time, (unsigned int)exp_time ) ); + DEBUG( 3, + ( "Start Record [%u] End Record [%u]\n", + (unsigned int)start_record, + (unsigned int)end_record )); + + for ( i = start_record; i < end_record; i++ ) { + /* read a record, add the amt to nbytes */ + key.dsize = sizeof(int32_t); + key.dptr = (unsigned char *)&i; + ret = tdb_fetch( the_tdb, key ); + if ( ret.dsize == 0 ) { + DEBUG( 8, + ( "Can't find a record for the key, record [%d]\n", + i ) ); + tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD ); + return False; + } + nbytes += ret.dsize; /* note this includes overhead */ + + len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen, + &tresv1, &trecnum, &timegen, &timewr ); + if (len == -1) { + DEBUG( 10,("make_way_for_eventlogs: tdb_unpack failed.\n")); + tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD ); + SAFE_FREE( ret.dptr ); + return False; + } + + DEBUG( 8, + ( "read record %u, record size is [%d], total so far [%d]\n", + (unsigned int)i, reclen, nbytes ) ); + + SAFE_FREE( ret.dptr ); + + /* note that other servers may just stop writing records when the size limit + is reached, and there are no records older than 'retention'. This doesn't + like a very useful thing to do, so instead we whack (as in sleeps with the + fishes) just enough records to fit the what we need. This behavior could + be changed to 'match', if the need arises. */ + + if ( !whack_by_date && ( nbytes >= needed ) ) + break; /* done */ + if ( whack_by_date && ( timegen >= exp_time ) ) + break; /* done */ + } + + DEBUG( 3, + ( "nbytes [%d] needed [%d] start_record is [%u], should be set to [%u]\n", + nbytes, needed, (unsigned int)start_record, (unsigned int)i ) ); + /* todo - remove eventlog entries here and set starting record to start_record... */ + new_start = i; + if ( start_record != new_start ) { + for ( i = start_record; i < new_start; i++ ) { + key.dsize = sizeof(int32_t); + key.dptr = (unsigned char *)&i; + tdb_delete( the_tdb, key ); + } + + tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start ); + } + tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD ); + return True; +} + +/******************************************************************** + some hygiene for an eventlog - see how big it is, and then + calculate how many bytes we need to remove +********************************************************************/ + +bool prune_eventlog( TDB_CONTEXT * tdb ) +{ + int MaxSize, Retention, CalcdSize; + + if ( !tdb ) { + DEBUG( 4, ( "No eventlog tdb handle\n" ) ); + return False; + } + + CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention ); + DEBUG( 3, + ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize, + MaxSize ) ); + + if ( CalcdSize > MaxSize ) { + return make_way_for_eventlogs( tdb, CalcdSize - MaxSize, + False ); + } + + return make_way_for_eventlogs( tdb, 0, True ); +} + +/******************************************************************** +********************************************************************/ + +static bool can_write_to_eventlog( TDB_CONTEXT * tdb, int32_t needed ) +{ + int calcd_size; + int MaxSize, Retention; + + /* see if we can write to the eventlog -- do a policy enforcement */ + if ( !tdb ) + return False; /* tdb is null, so we can't write to it */ + + + if ( needed < 0 ) + return False; + MaxSize = 0; + Retention = 0; + + calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention ); + + if ( calcd_size <= MaxSize ) + return True; /* you betcha */ + if ( calcd_size + needed < MaxSize ) + return True; + + if ( Retention == 0xffffffff ) { + return False; /* see msdn - we can't write no room, discard */ + } + /* + note don't have to test, but always good to show intent, in case changes needed + later + */ + + if ( Retention == 0x00000000 ) { + /* discard record(s) */ + /* todo - decide when to remove a bunch vs. just what we need... */ + return make_way_for_eventlogs( tdb, calcd_size - MaxSize, + True ); + } + + return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False ); +} + +/******************************************************************* +*******************************************************************/ + +ELOG_TDB *elog_open_tdb( const char *logname, bool force_clear, bool read_only ) +{ + TDB_CONTEXT *tdb = NULL; + uint32_t vers_id; + ELOG_TDB *ptr; + char *tdbpath = NULL; + ELOG_TDB *tdb_node = NULL; + char *eventlogdir; + TALLOC_CTX *ctx = talloc_tos(); + bool ok; + + /* check for invalid options */ + + if (force_clear && read_only) { + DEBUG(1,("elog_open_tdb: Invalid flags\n")); + return NULL; + } + + /* first see if we have an open context */ + + for ( ptr=open_elog_list; ptr; ptr=ptr->next ) { + if ( strequal( ptr->name, logname ) ) { + ptr->ref_count++; + + /* trick to allow clearing of the eventlog tdb. + The force_clear flag should imply that someone + has done a force close. So make sure the tdb + is NULL. If this is a normal open, then just + return the existing reference */ + + if ( force_clear ) { + SMB_ASSERT( ptr->tdb == NULL ); + break; + } + else + return ptr; + } + } + + /* make sure that the eventlog dir exists */ + + eventlogdir = state_path(talloc_tos(), "eventlog"); + if (eventlogdir == NULL) { + return NULL; + } + ok = directory_create_or_exist(eventlogdir, 0755); + TALLOC_FREE(eventlogdir); + if (!ok) { + return NULL; + } + + /* get the path on disk */ + + tdbpath = elog_tdbname(ctx, logname); + if (!tdbpath) { + return NULL; + } + + DEBUG(7,("elog_open_tdb: Opening %s...(force_clear == %s)\n", + tdbpath, force_clear?"True":"False" )); + + /* the tdb wasn't already open or this is a forced clear open */ + + if ( !force_clear ) { + + tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, read_only ? O_RDONLY : O_RDWR , 0 ); + if ( tdb ) { + vers_id = tdb_fetch_int32( tdb, EVT_VERSION ); + + if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) { + DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n", + vers_id, tdbpath)); + tdb_close( tdb ); + tdb = elog_init_tdb( tdbpath ); + } + } + } + + if ( !tdb ) + tdb = elog_init_tdb( tdbpath ); + + /* if we got a valid context, then add it to the list */ + + if ( tdb ) { + /* on a forced clear, just reset the tdb context if we already + have an open entry in the list */ + + if ( ptr ) { + ptr->tdb = tdb; + return ptr; + } + + if ( !(tdb_node = talloc_zero( NULL, ELOG_TDB)) ) { + DEBUG(0,("elog_open_tdb: talloc() failure!\n")); + tdb_close( tdb ); + return NULL; + } + + tdb_node->name = talloc_strdup( tdb_node, logname ); + tdb_node->tdb = tdb; + tdb_node->ref_count = 1; + + DLIST_ADD( open_elog_list, tdb_node ); + } + + return tdb_node; +} + +/******************************************************************* + Wrapper to handle reference counts to the tdb +*******************************************************************/ + +int elog_close_tdb( ELOG_TDB *etdb, bool force_close ) +{ + TDB_CONTEXT *tdb; + + if ( !etdb ) + return 0; + + etdb->ref_count--; + + SMB_ASSERT( etdb->ref_count >= 0 ); + + if ( etdb->ref_count == 0 ) { + tdb = etdb->tdb; + DLIST_REMOVE( open_elog_list, etdb ); + TALLOC_FREE( etdb ); + return tdb_close( tdb ); + } + + if ( force_close ) { + tdb = etdb->tdb; + etdb->tdb = NULL; + return tdb_close( tdb ); + } + + return 0; +} + +/******************************************************************** + Note that it's a pretty good idea to initialize the Eventlog_entry + structure to zero's before calling parse_logentry on an batch of + lines that may resolve to a record. ALSO, it's a good idea to + remove any linefeeds (that's EOL to you and me) on the lines + going in. +********************************************************************/ + +bool parse_logentry( TALLOC_CTX *mem_ctx, char *line, struct eventlog_Record_tdb *entry, bool * eor ) +{ + char *start = NULL, *stop = NULL; + + start = line; + + /* empty line signifying record delimiter, or we're at the end of the buffer */ + if ( start == NULL || strlen( start ) == 0 ) { + DEBUG( 6, + ( "parse_logentry: found end-of-record indicator.\n" ) ); + *eor = True; + return True; + } + if ( !( stop = strchr( line, ':' ) ) ) { + return False; + } + + DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) ); + + if ( 0 == strncmp( start, "LEN", stop - start ) ) { + /* This will get recomputed later anyway -- probably not necessary */ + entry->size = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "RS1", stop - start ) ) { + /* For now all these reserved entries seem to have the same value, + which can be hardcoded to int(1699505740) for now */ + entry->reserved = talloc_strdup(mem_ctx, "eLfL"); + } else if ( 0 == strncmp( start, "RCN", stop - start ) ) { + entry->record_number = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "TMG", stop - start ) ) { + entry->time_generated = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "TMW", stop - start ) ) { + entry->time_written = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "EID", stop - start ) ) { + entry->event_id = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "ETP", stop - start ) ) { + if ( strstr( start, "ERROR" ) ) { + entry->event_type = EVENTLOG_ERROR_TYPE; + } else if ( strstr( start, "WARNING" ) ) { + entry->event_type = EVENTLOG_WARNING_TYPE; + } else if ( strstr( start, "INFO" ) ) { + entry->event_type = EVENTLOG_INFORMATION_TYPE; + } else if ( strstr( start, "AUDIT_SUCCESS" ) ) { + entry->event_type = EVENTLOG_AUDIT_SUCCESS; + } else if ( strstr( start, "AUDIT_FAILURE" ) ) { + entry->event_type = EVENTLOG_AUDIT_FAILURE; + } else if ( strstr( start, "SUCCESS" ) ) { + entry->event_type = EVENTLOG_SUCCESS; + } else { + /* some other eventlog type -- currently not defined in MSDN docs, so error out */ + return False; + } + } + +/* + else if(0 == strncmp(start, "NST", stop - start)) + { + entry->num_of_strings = atoi(stop + 1); + } +*/ + else if ( 0 == strncmp( start, "ECT", stop - start ) ) { + entry->event_category = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "RS2", stop - start ) ) { + entry->reserved_flags = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "CRN", stop - start ) ) { + entry->closing_record_number = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "USL", stop - start ) ) { + entry->sid_length = atoi( stop + 1 ); + } else if ( 0 == strncmp( start, "SRC", stop - start ) ) { + stop++; + while ( isspace( stop[0] ) ) { + stop++; + } + entry->source_name_len = strlen_m_term(stop); + entry->source_name = talloc_strdup(mem_ctx, stop); + if (entry->source_name_len == (uint32_t)-1 || + entry->source_name == NULL) { + return false; + } + } else if ( 0 == strncmp( start, "SRN", stop - start ) ) { + stop++; + while ( isspace( stop[0] ) ) { + stop++; + } + entry->computer_name_len = strlen_m_term(stop); + entry->computer_name = talloc_strdup(mem_ctx, stop); + if (entry->computer_name_len == (uint32_t)-1 || + entry->computer_name == NULL) { + return false; + } + } else if ( 0 == strncmp( start, "SID", stop - start ) ) { + smb_ucs2_t *dummy = NULL; + stop++; + while ( isspace( stop[0] ) ) { + stop++; + } + entry->sid_length = rpcstr_push_talloc(mem_ctx, + &dummy, + stop); + if (entry->sid_length == (uint32_t)-1) { + return false; + } + entry->sid = data_blob_talloc(mem_ctx, dummy, entry->sid_length); + if (entry->sid.data == NULL) { + return false; + } + } else if ( 0 == strncmp( start, "STR", stop - start ) ) { + size_t tmp_len; + size_t num_of_strings; + /* skip past initial ":" */ + stop++; + /* now skip any other leading whitespace */ + while ( isspace(stop[0])) { + stop++; + } + tmp_len = strlen_m_term(stop); + if (tmp_len == (size_t)-1) { + return false; + } + num_of_strings = entry->num_of_strings; + if (!add_string_to_array(mem_ctx, stop, &entry->strings, + &num_of_strings)) { + return false; + } + if (num_of_strings > 0xffff) { + return false; + } + entry->num_of_strings = num_of_strings; + entry->strings_len += tmp_len; + } else if ( 0 == strncmp( start, "DAT", stop - start ) ) { + /* skip past initial ":" */ + stop++; + /* now skip any other leading whitespace */ + while ( isspace( stop[0] ) ) { + stop++; + } + entry->data_length = strlen_m(stop); + entry->data = data_blob_talloc(mem_ctx, stop, entry->data_length); + if (!entry->data.data) { + return false; + } + } else { + /* some other eventlog entry -- not implemented, so dropping on the floor */ + DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) ); + /* For now return true so that we can keep on parsing this mess. Eventually + we will return False here. */ + return true; + } + return true; +} + +/******************************************************************* + calculate the correct fields etc for an eventlog entry +*******************************************************************/ + +size_t fixup_eventlog_record_tdb(struct eventlog_Record_tdb *r) +{ + size_t size = 56; /* static size of integers before buffers start */ + + r->source_name_len = strlen_m_term(r->source_name) * 2; + r->computer_name_len = strlen_m_term(r->computer_name) * 2; + r->strings_len = ndr_size_string_array(r->strings, + r->num_of_strings, LIBNDR_FLAG_STR_NULLTERM) * 2; + + /* fix up the eventlog entry structure as necessary */ + r->sid_padding = ( ( 4 - ( ( r->source_name_len + r->computer_name_len ) % 4 ) ) % 4 ); + r->padding = ( 4 - ( ( r->strings_len + r->data_length ) % 4 ) ) % 4; + + if (r->sid_length == 0) { + /* Should not pad to a DWORD boundary for writing out the sid if there is + no SID, so just propagate the padding to pad the data */ + r->padding += r->sid_padding; + r->sid_padding = 0; + } + + size += r->source_name_len; + size += r->computer_name_len; + size += r->sid_padding; + size += r->sid_length; + size += r->strings_len; + size += r->data_length; + size += r->padding; + /* need another copy of length at the end of the data */ + size += sizeof(r->size); + + r->size = size; + + return size; +} + + +/******************************************************************** + ********************************************************************/ + +struct eventlog_Record_tdb *evlog_pull_record_tdb(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + uint32_t record_number) +{ + struct eventlog_Record_tdb *r; + TDB_DATA data, key; + + int32_t srecno; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + + srecno = record_number; + key.dptr = (unsigned char *)&srecno; + key.dsize = sizeof(int32_t); + + data = tdb_fetch(tdb, key); + if (data.dsize == 0) { + DEBUG(8,("evlog_pull_record_tdb: " + "Can't find a record for the key, record %d\n", + record_number)); + return NULL; + } + + r = talloc_zero(mem_ctx, struct eventlog_Record_tdb); + if (!r) { + goto done; + } + + blob = data_blob_const(data.dptr, data.dsize); + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, r, + (ndr_pull_flags_fn_t)ndr_pull_eventlog_Record_tdb); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(10,("evlog_pull_record_tdb: failed to decode record %d\n", + record_number)); + TALLOC_FREE(r); + goto done; + } + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(eventlog_Record_tdb, r); + } + + DEBUG(10,("evlog_pull_record_tdb: retrieved entry for record %d\n", + record_number)); + done: + SAFE_FREE(data.dptr); + + return r; +} + +/******************************************************************** + ********************************************************************/ + +struct EVENTLOGRECORD *evlog_pull_record(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + uint32_t record_number) +{ + struct eventlog_Record_tdb *t; + struct EVENTLOGRECORD *r; + NTSTATUS status; + + r = talloc_zero(mem_ctx, struct EVENTLOGRECORD); + if (!r) { + return NULL; + } + + t = evlog_pull_record_tdb(r, tdb, record_number); + if (!t) { + talloc_free(r); + return NULL; + } + + status = evlog_tdb_entry_to_evt_entry(r, t, r); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(r); + return NULL; + } + + r->Length = r->Length2 = ndr_size_EVENTLOGRECORD(r, 0); + + return r; +} + +/******************************************************************** + write an eventlog entry. Note that we have to lock, read next + eventlog, increment, write, write the record, unlock + + coming into this, ee has the eventlog record, and the auxiliary date + (computer name, etc.) filled into the other structure. Before packing + into a record, this routine will calc the appropriate padding, etc., + and then blast out the record in a form that can be read back in + ********************************************************************/ + +NTSTATUS evlog_push_record_tdb(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + struct eventlog_Record_tdb *r, + uint32_t *record_number) +{ + TDB_DATA kbuf, ebuf; + DATA_BLOB blob; + enum ndr_err_code ndr_err; + int ret; + + if (!r) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!can_write_to_eventlog(tdb, r->size)) { + return NT_STATUS_EVENTLOG_CANT_START; + } + + /* need to read the record number and insert it into the entry here */ + + /* lock */ + ret = tdb_lock_bystring_with_timeout(tdb, EVT_NEXT_RECORD, 1); + if (ret != 0) { + return NT_STATUS_LOCK_NOT_GRANTED; + } + + /* read */ + r->record_number = tdb_fetch_int32(tdb, EVT_NEXT_RECORD); + + ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r, + (ndr_push_flags_fn_t)ndr_push_eventlog_Record_tdb); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + tdb_unlock_bystring(tdb, EVT_NEXT_RECORD); + return ndr_map_error2ntstatus(ndr_err); + } + + /* increment the record count */ + + kbuf.dsize = sizeof(int32_t); + kbuf.dptr = (uint8_t *)&r->record_number; + + ebuf.dsize = blob.length; + ebuf.dptr = blob.data; + + ret = tdb_store(tdb, kbuf, ebuf, 0); + if (ret != 0) { + tdb_unlock_bystring(tdb, EVT_NEXT_RECORD); + return NT_STATUS_EVENTLOG_FILE_CORRUPT; + } + + ret = tdb_store_int32(tdb, EVT_NEXT_RECORD, r->record_number + 1); + if (ret != 0) { + tdb_unlock_bystring(tdb, EVT_NEXT_RECORD); + return NT_STATUS_EVENTLOG_FILE_CORRUPT; + } + tdb_unlock_bystring(tdb, EVT_NEXT_RECORD); + + if (record_number) { + *record_number = r->record_number; + } + + return NT_STATUS_OK; +} + +/******************************************************************** + ********************************************************************/ + +NTSTATUS evlog_push_record(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + struct EVENTLOGRECORD *r, + uint32_t *record_number) +{ + struct eventlog_Record_tdb *t; + NTSTATUS status; + + t = talloc_zero(mem_ctx, struct eventlog_Record_tdb); + if (!t) { + return NT_STATUS_NO_MEMORY; + } + + status = evlog_evt_entry_to_tdb_entry(t, r, t); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(t); + return status; + } + + status = evlog_push_record_tdb(mem_ctx, tdb, t, record_number); + talloc_free(t); + + return status; +} + +/******************************************************************** + ********************************************************************/ + +NTSTATUS evlog_evt_entry_to_tdb_entry(TALLOC_CTX *mem_ctx, + const struct EVENTLOGRECORD *e, + struct eventlog_Record_tdb *t) +{ + uint32_t i; + + ZERO_STRUCTP(t); + + t->size = e->Length; + t->reserved = e->Reserved; + t->record_number = e->RecordNumber; + t->time_generated = e->TimeGenerated; + t->time_written = e->TimeWritten; + t->event_id = e->EventID; + t->event_type = e->EventType; + t->num_of_strings = e->NumStrings; + t->event_category = e->EventCategory; + t->reserved_flags = e->ReservedFlags; + t->closing_record_number = e->ClosingRecordNumber; + + t->stringoffset = e->StringOffset; + t->sid_length = e->UserSidLength; + t->sid_offset = e->UserSidOffset; + t->data_length = e->DataLength; + t->data_offset = e->DataOffset; + + t->source_name_len = 2 * strlen_m_term(e->SourceName); + t->source_name = talloc_strdup(mem_ctx, e->SourceName); + NT_STATUS_HAVE_NO_MEMORY(t->source_name); + + t->computer_name_len = 2 * strlen_m_term(e->Computername); + t->computer_name = talloc_strdup(mem_ctx, e->Computername); + NT_STATUS_HAVE_NO_MEMORY(t->computer_name); + + /* t->sid_padding; */ + if (e->UserSidLength > 0) { + struct dom_sid_buf sid_str; + smb_ucs2_t *dummy = NULL; + t->sid_length = rpcstr_push_talloc( + mem_ctx, + &dummy, + dom_sid_str_buf(&e->UserSid, &sid_str)); + if (t->sid_length == -1) { + return NT_STATUS_NO_MEMORY; + } + t->sid = data_blob_talloc(mem_ctx, (uint8_t *)dummy, t->sid_length); + NT_STATUS_HAVE_NO_MEMORY(t->sid.data); + } + + t->strings = talloc_array(mem_ctx, const char *, e->NumStrings); + for (i=0; i < e->NumStrings; i++) { + t->strings[i] = talloc_strdup(t->strings, e->Strings[i]); + NT_STATUS_HAVE_NO_MEMORY(t->strings[i]); + } + + t->strings_len = 2 * ndr_size_string_array(t->strings, t->num_of_strings, LIBNDR_FLAG_STR_NULLTERM); + t->data = data_blob_talloc(mem_ctx, e->Data, e->DataLength); + /* t->padding = r->Pad; */ + + return NT_STATUS_OK; +} + +/******************************************************************** + ********************************************************************/ + +NTSTATUS evlog_tdb_entry_to_evt_entry(TALLOC_CTX *mem_ctx, + const struct eventlog_Record_tdb *t, + struct EVENTLOGRECORD *e) +{ + uint32_t i; + + ZERO_STRUCTP(e); + + e->Length = t->size; + e->Reserved = t->reserved; + e->RecordNumber = t->record_number; + e->TimeGenerated = t->time_generated; + e->TimeWritten = t->time_written; + e->EventID = t->event_id; + e->EventType = t->event_type; + e->NumStrings = t->num_of_strings; + e->EventCategory = t->event_category; + e->ReservedFlags = t->reserved_flags; + e->ClosingRecordNumber = t->closing_record_number; + + e->StringOffset = t->stringoffset; + e->UserSidLength = t->sid_length; + e->UserSidOffset = t->sid_offset; + e->DataLength = t->data_length; + e->DataOffset = t->data_offset; + + e->SourceName = talloc_strdup(mem_ctx, t->source_name); + NT_STATUS_HAVE_NO_MEMORY(e->SourceName); + + e->Computername = talloc_strdup(mem_ctx, t->computer_name); + NT_STATUS_HAVE_NO_MEMORY(e->Computername); + + if (t->sid_length > 0) { + char *sid_str = NULL; + size_t len; + if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, + t->sid.data, t->sid.length, + (void *)&sid_str, &len)) { + return NT_STATUS_INVALID_SID; + } + if (len > 0) { + bool ok = string_to_sid(&e->UserSid, sid_str); + if (!ok) { + return NT_STATUS_INVALID_SID; + } + } + TALLOC_FREE(sid_str); + } + + e->Strings = talloc_array(mem_ctx, const char *, t->num_of_strings); + for (i=0; i < t->num_of_strings; i++) { + e->Strings[i] = talloc_strdup(e->Strings, t->strings[i]); + NT_STATUS_HAVE_NO_MEMORY(e->Strings[i]); + } + + e->Data = (uint8_t *)talloc_memdup(mem_ctx, t->data.data, t->data_length); + e->Pad = talloc_strdup(mem_ctx, ""); + NT_STATUS_HAVE_NO_MEMORY(e->Pad); + + e->Length2 = t->size; + + return NT_STATUS_OK; +} + +/******************************************************************** + ********************************************************************/ + +NTSTATUS evlog_convert_tdb_to_evt(TALLOC_CTX *mem_ctx, + ELOG_TDB *etdb, + DATA_BLOB *blob_p, + uint32_t *num_records_p) +{ + NTSTATUS status = NT_STATUS_OK; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + uint32_t num_records = 0; + struct EVENTLOG_EVT_FILE evt; + uint32_t count = 1; + size_t endoffset = 0; + + ZERO_STRUCT(evt); + + while (1) { + + struct eventlog_Record_tdb *r; + struct EVENTLOGRECORD e; + + r = evlog_pull_record_tdb(mem_ctx, etdb->tdb, count); + if (!r) { + break; + } + + status = evlog_tdb_entry_to_evt_entry(mem_ctx, r, &e); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + endoffset += ndr_size_EVENTLOGRECORD(&e, 0); + + ADD_TO_ARRAY(mem_ctx, struct EVENTLOGRECORD, e, &evt.records, &num_records); + count++; + } + + evt.hdr.StartOffset = 0x30; + evt.hdr.EndOffset = evt.hdr.StartOffset + endoffset; + evt.hdr.CurrentRecordNumber = count; + evt.hdr.OldestRecordNumber = 1; + evt.hdr.MaxSize = tdb_fetch_int32(etdb->tdb, EVT_MAXSIZE); + evt.hdr.Flags = 0; + evt.hdr.Retention = tdb_fetch_int32(etdb->tdb, EVT_RETENTION); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(EVENTLOGHEADER, &evt.hdr); + } + + evt.eof.BeginRecord = 0x30; + evt.eof.EndRecord = evt.hdr.StartOffset + endoffset; + evt.eof.CurrentRecordNumber = evt.hdr.CurrentRecordNumber; + evt.eof.OldestRecordNumber = evt.hdr.OldestRecordNumber; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(EVENTLOGEOF, &evt.eof); + } + + ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &evt, + (ndr_push_flags_fn_t)ndr_push_EVENTLOG_EVT_FILE); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + goto done; + } + + *blob_p = blob; + *num_records_p = num_records; + + done: + return status; +} diff --git a/source3/lib/eventlog/eventlog.h b/source3/lib/eventlog/eventlog.h new file mode 100644 index 0000000..b485bfe --- /dev/null +++ b/source3/lib/eventlog/eventlog.h @@ -0,0 +1,45 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Marcin Krzysztof Porwit 2005. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <tdb.h> + +/* Defines for TDB keys */ +#define EVT_OLDEST_ENTRY "INFO/oldest_entry" +#define EVT_NEXT_RECORD "INFO/next_record" +#define EVT_VERSION "INFO/version" +#define EVT_MAXSIZE "INFO/maxsize" +#define EVT_RETENTION "INFO/retention" + +#define ELOG_APPL "Application" +#define ELOG_SYS "System" +#define ELOG_SEC "Security" + +typedef struct elog_tdb { + struct elog_tdb *prev, *next; + char *name; + TDB_CONTEXT *tdb; + int ref_count; +} ELOG_TDB; + +#define ELOG_TDB_CTX(x) ((x)->tdb) + +#define EVENTLOG_DATABASE_VERSION_V1 1 + +#include "../librpc/gen_ndr/ndr_eventlog.h" +#include "lib/eventlog/proto.h" diff --git a/source3/lib/eventlog/proto.h b/source3/lib/eventlog/proto.h new file mode 100644 index 0000000..d3341ce --- /dev/null +++ b/source3/lib/eventlog/proto.h @@ -0,0 +1,62 @@ +/* + * Unix SMB/CIFS implementation. + * Eventlog utility routines + * + * Copyright (C) Marcin Krzysztof Porwit 2005 + * Copyright (C) Brian Moran 2005 + * Copyright (C) Gerald (Jerry) Carter 2005 + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _LIB_EVENTLOG_PROTO_H_ +#define _LIB_EVENTLOG_PROTO_H_ + +/* The following definitions come from lib/eventlog/eventlog.c */ + +TDB_CONTEXT *elog_init_tdb( char *tdbfilename ); +char *elog_tdbname(TALLOC_CTX *ctx, const char *name ); +int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention ); +bool prune_eventlog( TDB_CONTEXT * tdb ); +ELOG_TDB *elog_open_tdb( const char *logname, bool force_clear, bool read_only ); +int elog_close_tdb( ELOG_TDB *etdb, bool force_close ); +bool parse_logentry( TALLOC_CTX *mem_ctx, char *line, struct eventlog_Record_tdb *entry, bool * eor ); +size_t fixup_eventlog_record_tdb(struct eventlog_Record_tdb *r); +struct eventlog_Record_tdb *evlog_pull_record_tdb(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + uint32_t record_number); +NTSTATUS evlog_push_record_tdb(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + struct eventlog_Record_tdb *r, + uint32_t *record_number); +NTSTATUS evlog_push_record(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + struct EVENTLOGRECORD *r, + uint32_t *record_number); +struct EVENTLOGRECORD *evlog_pull_record(TALLOC_CTX *mem_ctx, + TDB_CONTEXT *tdb, + uint32_t record_number); +NTSTATUS evlog_evt_entry_to_tdb_entry(TALLOC_CTX *mem_ctx, + const struct EVENTLOGRECORD *e, + struct eventlog_Record_tdb *t); +NTSTATUS evlog_tdb_entry_to_evt_entry(TALLOC_CTX *mem_ctx, + const struct eventlog_Record_tdb *t, + struct EVENTLOGRECORD *e); +NTSTATUS evlog_convert_tdb_to_evt(TALLOC_CTX *mem_ctx, + ELOG_TDB *etdb, + DATA_BLOB *blob_p, + uint32_t *num_records_p); + +#endif /* _LIB_EVENTLOG_PROTO_H_ */ diff --git a/source3/lib/file_id.c b/source3/lib/file_id.c new file mode 100644 index 0000000..545437a --- /dev/null +++ b/source3/lib/file_id.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + + file_id structure handling + + Copyright (C) Andrew Tridgell 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/file_id.h" + +/* + return True if two file_id structures are equal + */ +bool file_id_equal(const struct file_id *id1, const struct file_id *id2) +{ + return id1->inode == id2->inode && id1->devid == id2->devid && + id1->extid == id2->extid; +} + +char *file_id_str_buf(struct file_id fid, struct file_id_buf *dst) +{ + snprintf(dst->buf, + sizeof(dst->buf), + "%"PRIu64":%"PRIu64":%"PRIu64, + fid.devid, + fid.inode, + fid.extid); + return dst->buf; +} + +/* + push a 16 byte version of a file id into a buffer. This ignores the extid + and is needed when dev/inodes are stored in persistent storage (tdbs). + */ +void push_file_id_16(char *buf, const struct file_id *id) +{ + SIVAL(buf, 0, id->devid&0xFFFFFFFF); + SIVAL(buf, 4, id->devid>>32); + SIVAL(buf, 8, id->inode&0xFFFFFFFF); + SIVAL(buf, 12, id->inode>>32); +} + +/* + push a 24 byte version of a file id into a buffer + */ +void push_file_id_24(char *buf, const struct file_id *id) +{ + SIVAL(buf, 0, id->devid&0xFFFFFFFF); + SIVAL(buf, 4, id->devid>>32); + SIVAL(buf, 8, id->inode&0xFFFFFFFF); + SIVAL(buf, 12, id->inode>>32); + SIVAL(buf, 16, id->extid&0xFFFFFFFF); + SIVAL(buf, 20, id->extid>>32); +} + +/* + pull a 24 byte version of a file id from a buffer + */ +void pull_file_id_24(const char *buf, struct file_id *id) +{ + ZERO_STRUCTP(id); + id->devid = IVAL(buf, 0); + id->devid |= ((uint64_t)IVAL(buf,4))<<32; + id->inode = IVAL(buf, 8); + id->inode |= ((uint64_t)IVAL(buf,12))<<32; + id->extid = IVAL(buf, 16); + id->extid |= ((uint64_t)IVAL(buf,20))<<32; +} diff --git a/source3/lib/file_id.h b/source3/lib/file_id.h new file mode 100644 index 0000000..255b1dd --- /dev/null +++ b/source3/lib/file_id.h @@ -0,0 +1,44 @@ +/* + Unix SMB/CIFS implementation. + + file_id structure handling + + Copyright (C) Andrew Tridgell 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __LIB_FILE_ID_H__ +#define __LIB_FILE_ID_H__ + +#include "librpc/gen_ndr/file_id.h" + +/* The following definitions come from lib/file_id.c */ + +bool file_id_equal(const struct file_id *id1, const struct file_id *id2); + +/* + * strlen("18446744073709551615")=20 times 3 plus 2 colons plus trailing 0 + */ +struct file_id_buf { char buf[63]; }; +char *file_id_str_buf(struct file_id fid, struct file_id_buf *dst); + +/* + an allocated string for a file_id structure + */ +void push_file_id_16(char *buf, const struct file_id *id); +void push_file_id_24(char *buf, const struct file_id *id); +void pull_file_id_24(const char *buf, struct file_id *id); + +#endif diff --git a/source3/lib/filename_util.c b/source3/lib/filename_util.c new file mode 100644 index 0000000..3f8e903 --- /dev/null +++ b/source3/lib/filename_util.c @@ -0,0 +1,406 @@ +/* + Unix SMB/CIFS implementation. + Filename utility functions. + Copyright (C) Tim Prouty 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" + +/** + * XXX: This is temporary and there should be no callers of this outside of + * this file once smb_filename is plumbed through all path based operations. + * The one legitimate caller currently is smb_fname_str_dbg(), which this + * could be made static for. + */ +NTSTATUS get_full_smb_filename(TALLOC_CTX *ctx, + const struct smb_filename *smb_fname, + char **full_name) +{ + if (smb_fname->stream_name) { + /* stream_name must always be NULL if there is no stream. */ + SMB_ASSERT(smb_fname->stream_name[0] != '\0'); + + *full_name = talloc_asprintf(ctx, "%s%s", smb_fname->base_name, + smb_fname->stream_name); + } else { + *full_name = talloc_strdup(ctx, smb_fname->base_name); + } + + if (!*full_name) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +/** + * There are actually legitimate callers of this such as functions that + * enumerate streams using the vfs_streaminfo interface and then want to + * operate on each stream. + */ +struct smb_filename *synthetic_smb_fname(TALLOC_CTX *mem_ctx, + const char *base_name, + const char *stream_name, + const SMB_STRUCT_STAT *psbuf, + NTTIME twrp, + uint32_t flags) +{ + /* Setup the base_name/stream_name. */ + + struct smb_filename smb_fname_loc = { + .base_name = discard_const_p(char, base_name), + .stream_name = discard_const_p(char, stream_name), + .flags = flags, + .twrp = twrp, + }; + + /* Copy the psbuf if one was given. */ + if (psbuf) + smb_fname_loc.st = *psbuf; + + /* Let cp_smb_filename() do the heavy lifting. */ + return cp_smb_filename(mem_ctx, &smb_fname_loc); +} + +/** + * Utility function used by VFS calls that must *NOT* operate + * on a stream filename, only the base_name. + */ +struct smb_filename *cp_smb_filename_nostream(TALLOC_CTX *mem_ctx, + const struct smb_filename *smb_fname_in) +{ + struct smb_filename smb_fname_loc = *smb_fname_in; + struct smb_filename *smb_fname = NULL; + + smb_fname_loc.stream_name = NULL; + + smb_fname = cp_smb_filename(mem_ctx, &smb_fname_loc); + return smb_fname; +} + +/** + * There are a few legitimate users of this. + */ +struct smb_filename *synthetic_smb_fname_split(TALLOC_CTX *ctx, + const char *fname, + bool posix_path) +{ + char *stream_name = NULL; + char *base_name = NULL; + struct smb_filename *ret; + bool ok; + + if (posix_path) { + /* No stream name looked for. */ + return synthetic_smb_fname(ctx, + fname, + NULL, + NULL, + 0, + SMB_FILENAME_POSIX_PATH); + } + + ok = split_stream_filename(ctx, + fname, + &base_name, + &stream_name); + if (!ok) { + return NULL; + } + + ret = synthetic_smb_fname(ctx, + base_name, + stream_name, + NULL, + 0, + 0); + TALLOC_FREE(base_name); + TALLOC_FREE(stream_name); + return ret; +} + +/** + * Return a string using the talloc_tos() + */ +const char *smb_fname_str_dbg(const struct smb_filename *smb_fname) +{ + char *fname = NULL; + time_t t; + struct tm tm; + struct tm *ptm = NULL; + fstring tstr; + ssize_t slen; + NTSTATUS status; + + if (smb_fname == NULL) { + return ""; + } + status = get_full_smb_filename(talloc_tos(), smb_fname, &fname); + if (!NT_STATUS_IS_OK(status)) { + return ""; + } + if (smb_fname->twrp == 0) { + return fname; + } + + t = nt_time_to_unix(smb_fname->twrp); + ptm = gmtime_r(&t, &tm); + if (ptm == NULL) { + return ""; + } + + slen = strftime(tstr, sizeof(tstr), GMT_FORMAT, &tm); + if (slen == 0) { + return ""; + } + + fname = talloc_asprintf_append_buffer( + fname, " {%s}", tstr); + if (fname == NULL) { + return ""; + } + return fname; +} + +/** + * Return a debug string of the path name of an fsp using the talloc_tos(). + */ +const char *fsp_str_dbg(const struct files_struct *fsp) +{ + const char *name = NULL; + + name = smb_fname_str_dbg(fsp->fsp_name); + if (name == NULL) { + return ""; + } + + return name; +} + +/** + * Create a debug string for the fnum of an fsp. + * + * This is allocated to talloc_tos() or a string constant + * in certain corner cases. The returned string should + * hence not be free'd directly but only via the talloc stack. + */ +const char *fsp_fnum_dbg(const struct files_struct *fsp) +{ + char *str; + + if (fsp == NULL) { + return "fnum [fsp is NULL]"; + } + + if (fsp->fnum == FNUM_FIELD_INVALID) { + return "fnum [invalid value]"; + } + + str = talloc_asprintf(talloc_tos(), "fnum %"PRIu64, fsp->fnum); + if (str == NULL) { + DEBUG(1, ("%s: talloc_asprintf failed\n", __FUNCTION__)); + return "fnum [talloc failed!]"; + } + + return str; +} + +struct smb_filename *cp_smb_filename(TALLOC_CTX *mem_ctx, + const struct smb_filename *in) +{ + struct smb_filename *out; + size_t base_len = 0; + size_t stream_len = 0; + int num = 0; + + /* stream_name must always be NULL if there is no stream. */ + if (in->stream_name) { + SMB_ASSERT(in->stream_name[0] != '\0'); + } + + if (in->base_name != NULL) { + base_len = strlen(in->base_name) + 1; + num += 1; + } + if (in->stream_name != NULL) { + stream_len = strlen(in->stream_name) + 1; + num += 1; + } + + out = talloc_pooled_object(mem_ctx, struct smb_filename, + num, stream_len + base_len); + if (out == NULL) { + return NULL; + } + ZERO_STRUCTP(out); + + /* + * The following allocations cannot fail as we + * pre-allocated space for them in the out pooled + * object. + */ + if (in->base_name != NULL) { + out->base_name = talloc_memdup( + out, in->base_name, base_len); + talloc_set_name_const(out->base_name, + out->base_name); + } + if (in->stream_name != NULL) { + out->stream_name = talloc_memdup( + out, in->stream_name, stream_len); + talloc_set_name_const(out->stream_name, + out->stream_name); + } + out->flags = in->flags; + out->st = in->st; + out->twrp = in->twrp; + return out; +} + +static void assert_valid_stream_smb_fname(const struct smb_filename *smb_fname) +{ + /* stream_name must always be NULL if there is no stream. */ + if (smb_fname->stream_name) { + SMB_ASSERT(smb_fname->stream_name[0] != '\0'); + } + + if (smb_fname->flags & SMB_FILENAME_POSIX_PATH) { + SMB_ASSERT(smb_fname->stream_name == NULL); + } +} + +/**************************************************************************** + Simple check to determine if a smb_fname is a real named stream or the + default stream. + ***************************************************************************/ + +bool is_ntfs_stream_smb_fname(const struct smb_filename *smb_fname) +{ + assert_valid_stream_smb_fname(smb_fname); + + return (smb_fname->stream_name != NULL); +} + +/**************************************************************************** + Simple check to determine if a smb_fname is pointing to a normal file or + a named stream that is not the default stream "::$DATA". + + foo -> false + foo::$DATA -> false + foo:bar -> true + foo:bar:$DATA -> true + + ***************************************************************************/ + +bool is_named_stream(const struct smb_filename *smb_fname) +{ + assert_valid_stream_smb_fname(smb_fname); + + if (smb_fname->stream_name == NULL) { + return false; + } + + if (strequal_m(smb_fname->stream_name, "::$DATA")) { + return false; + } + + return true; +} + +/**************************************************************************** + Returns true if the filename's stream == "::$DATA" + ***************************************************************************/ +bool is_ntfs_default_stream_smb_fname(const struct smb_filename *smb_fname) +{ + assert_valid_stream_smb_fname(smb_fname); + + if (smb_fname->stream_name == NULL) { + return false; + } + + return strequal_m(smb_fname->stream_name, "::$DATA"); +} + +/**************************************************************************** + Filter out Windows invalid EA names (list probed from Windows 2012). +****************************************************************************/ + +static const char bad_ea_name_chars[] = "\"*+,/:;<=>?[\\]|"; + +bool is_invalid_windows_ea_name(const char *name) +{ + int i; + /* EA name is pulled as ascii so we can examine + individual bytes here. */ + for (i = 0; name[i] != 0; i++) { + int val = (name[i] & 0xff); + if (val < ' ' || strchr(bad_ea_name_chars, val)) { + return true; + } + } + return false; +} + +bool ea_list_has_invalid_name(struct ea_list *ea_list) +{ + for (;ea_list; ea_list = ea_list->next) { + if (is_invalid_windows_ea_name(ea_list->ea.name)) { + return true; + } + } + return false; +} + +/**************************************************************************** + Split an incoming name into tallocd filename and stream components. + Returns true on success, false on out of memory. +****************************************************************************/ + +bool split_stream_filename(TALLOC_CTX *ctx, + const char *filename_in, + char **filename_out, + char **streamname_out) +{ + const char *stream_name = NULL; + char *stream_out = NULL; + char *file_out = NULL; + + stream_name = strchr_m(filename_in, ':'); + + if (stream_name) { + stream_out = talloc_strdup(ctx, stream_name); + if (stream_out == NULL) { + return false; + } + file_out = talloc_strndup(ctx, + filename_in, + PTR_DIFF(stream_name, filename_in)); + } else { + file_out = talloc_strdup(ctx, filename_in); + } + + if (file_out == NULL) { + TALLOC_FREE(stream_out); + return false; + } + + if (filename_out) { + *filename_out = file_out; + } + if (streamname_out) { + *streamname_out = stream_out; + } + return true; +} diff --git a/source3/lib/fstring.c b/source3/lib/fstring.c new file mode 100644 index 0000000..3ed1db1 --- /dev/null +++ b/source3/lib/fstring.c @@ -0,0 +1,67 @@ +/* + Unix SMB/CIFS implementation. + + fixed string functions + + Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Simo Sorce 2001 + Copyright (C) Martin Pool 2003 + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" + +size_t push_ascii_fstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE); +} + +/******************************************************************** + Push an nstring (a netbios string) + this function uses convert_string_error() to avoid common debug + warnings where is unable to convert strings to CH_DOS. The target + string is truncated at the first character that cannot be converted + The target is always null terminated. +********************************************************************/ + +size_t push_ascii_nstring(void *dest, const char *src) +{ + size_t converted_size = 0; + bool ret; + + errno = 0; + ret = convert_string_error(CH_UNIX, CH_DOS, src, -1, dest, sizeof(nstring), &converted_size); + if (ret || errno == E2BIG) { + SCVAL(dest, sizeof(nstring)-1, 0); + } else { + SCVAL(dest, 0, 0); + } + return ret ? converted_size : (size_t)-1; +} + +size_t pull_ascii_fstring(char *dest, const void *src) +{ + return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE); +} + +/* When pulling an nstring it can expand into a larger size (dos cp -> utf8). Cope with this. */ + +size_t pull_ascii_nstring(char *dest, size_t dest_len, const void *src) +{ + return pull_ascii(dest, src, dest_len, sizeof(nstring), STR_TERMINATE); +} + diff --git a/source3/lib/g_lock.c b/source3/lib/g_lock.c new file mode 100644 index 0000000..33f088b --- /dev/null +++ b/source3/lib/g_lock.c @@ -0,0 +1,1984 @@ +/* + Unix SMB/CIFS implementation. + global locks based on dbwrap and messaging + Copyright (C) 2009 by Volker Lendecke + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "lib/util/server_id.h" +#include "lib/util/debug.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/samba_util.h" +#include "lib/util_path.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "dbwrap/dbwrap_watch.h" +#include "g_lock.h" +#include "util_tdb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "messages.h" +#include "serverid.h" + +struct g_lock_ctx { + struct db_context *db; + struct messaging_context *msg; + enum dbwrap_lock_order lock_order; + bool busy; +}; + +struct g_lock { + struct server_id exclusive; + size_t num_shared; + uint8_t *shared; + uint64_t unique_lock_epoch; + uint64_t unique_data_epoch; + size_t datalen; + uint8_t *data; +}; + +static bool g_lock_parse(uint8_t *buf, size_t buflen, struct g_lock *lck) +{ + struct server_id exclusive; + size_t num_shared, shared_len; + uint64_t unique_lock_epoch; + uint64_t unique_data_epoch; + + if (buflen < (SERVER_ID_BUF_LENGTH + /* exclusive */ + sizeof(uint64_t) + /* seqnum */ + sizeof(uint32_t))) { /* num_shared */ + struct g_lock ret = { + .exclusive.pid = 0, + .unique_lock_epoch = generate_unique_u64(0), + .unique_data_epoch = generate_unique_u64(0), + }; + *lck = ret; + return true; + } + + server_id_get(&exclusive, buf); + buf += SERVER_ID_BUF_LENGTH; + buflen -= SERVER_ID_BUF_LENGTH; + + unique_lock_epoch = BVAL(buf, 0); + buf += sizeof(uint64_t); + buflen -= sizeof(uint64_t); + + unique_data_epoch = BVAL(buf, 0); + buf += sizeof(uint64_t); + buflen -= sizeof(uint64_t); + + num_shared = IVAL(buf, 0); + buf += sizeof(uint32_t); + buflen -= sizeof(uint32_t); + + if (num_shared > buflen/SERVER_ID_BUF_LENGTH) { + DBG_DEBUG("num_shared=%zu, buflen=%zu\n", + num_shared, + buflen); + return false; + } + + shared_len = num_shared * SERVER_ID_BUF_LENGTH; + + *lck = (struct g_lock) { + .exclusive = exclusive, + .num_shared = num_shared, + .shared = buf, + .unique_lock_epoch = unique_lock_epoch, + .unique_data_epoch = unique_data_epoch, + .datalen = buflen-shared_len, + .data = buf+shared_len, + }; + + return true; +} + +static void g_lock_get_shared(const struct g_lock *lck, + size_t i, + struct server_id *shared) +{ + if (i >= lck->num_shared) { + abort(); + } + server_id_get(shared, lck->shared + i*SERVER_ID_BUF_LENGTH); +} + +static void g_lock_del_shared(struct g_lock *lck, size_t i) +{ + if (i >= lck->num_shared) { + abort(); + } + lck->num_shared -= 1; + if (i < lck->num_shared) { + memcpy(lck->shared + i*SERVER_ID_BUF_LENGTH, + lck->shared + lck->num_shared*SERVER_ID_BUF_LENGTH, + SERVER_ID_BUF_LENGTH); + } +} + +static NTSTATUS g_lock_store( + struct db_record *rec, + struct g_lock *lck, + struct server_id *new_shared, + const TDB_DATA *new_dbufs, + size_t num_new_dbufs) +{ + uint8_t exclusive[SERVER_ID_BUF_LENGTH]; + uint8_t seqnum_buf[sizeof(uint64_t)*2]; + uint8_t sizebuf[sizeof(uint32_t)]; + uint8_t new_shared_buf[SERVER_ID_BUF_LENGTH]; + + struct TDB_DATA dbufs[6 + num_new_dbufs]; + + dbufs[0] = (TDB_DATA) { + .dptr = exclusive, .dsize = sizeof(exclusive), + }; + dbufs[1] = (TDB_DATA) { + .dptr = seqnum_buf, .dsize = sizeof(seqnum_buf), + }; + dbufs[2] = (TDB_DATA) { + .dptr = sizebuf, .dsize = sizeof(sizebuf), + }; + dbufs[3] = (TDB_DATA) { + .dptr = lck->shared, + .dsize = lck->num_shared * SERVER_ID_BUF_LENGTH, + }; + dbufs[4] = (TDB_DATA) { 0 }; + dbufs[5] = (TDB_DATA) { + .dptr = lck->data, .dsize = lck->datalen, + }; + + if (num_new_dbufs != 0) { + memcpy(&dbufs[6], + new_dbufs, + num_new_dbufs * sizeof(TDB_DATA)); + } + + server_id_put(exclusive, lck->exclusive); + SBVAL(seqnum_buf, 0, lck->unique_lock_epoch); + SBVAL(seqnum_buf, 8, lck->unique_data_epoch); + + if (new_shared != NULL) { + if (lck->num_shared >= UINT32_MAX) { + return NT_STATUS_BUFFER_OVERFLOW; + } + + server_id_put(new_shared_buf, *new_shared); + + dbufs[4] = (TDB_DATA) { + .dptr = new_shared_buf, + .dsize = sizeof(new_shared_buf), + }; + + lck->num_shared += 1; + } + + SIVAL(sizebuf, 0, lck->num_shared); + + return dbwrap_record_storev(rec, dbufs, ARRAY_SIZE(dbufs), 0); +} + +struct g_lock_ctx *g_lock_ctx_init_backend( + TALLOC_CTX *mem_ctx, + struct messaging_context *msg, + struct db_context **backend) +{ + struct g_lock_ctx *result; + + result = talloc_zero(mem_ctx, struct g_lock_ctx); + if (result == NULL) { + return NULL; + } + result->msg = msg; + result->lock_order = DBWRAP_LOCK_ORDER_NONE; + + result->db = db_open_watched(result, backend, msg); + if (result->db == NULL) { + DBG_WARNING("db_open_watched failed\n"); + TALLOC_FREE(result); + return NULL; + } + return result; +} + +void g_lock_set_lock_order(struct g_lock_ctx *ctx, + enum dbwrap_lock_order lock_order) +{ + ctx->lock_order = lock_order; +} + +struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx, + struct messaging_context *msg) +{ + char *db_path = NULL; + struct db_context *backend = NULL; + struct g_lock_ctx *ctx = NULL; + + db_path = lock_path(mem_ctx, "g_lock.tdb"); + if (db_path == NULL) { + return NULL; + } + + backend = db_open( + mem_ctx, + db_path, + 0, + TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH|TDB_VOLATILE, + O_RDWR|O_CREAT, + 0600, + DBWRAP_LOCK_ORDER_3, + DBWRAP_FLAG_NONE); + TALLOC_FREE(db_path); + if (backend == NULL) { + DBG_WARNING("Could not open g_lock.tdb\n"); + return NULL; + } + + ctx = g_lock_ctx_init_backend(mem_ctx, msg, &backend); + return ctx; +} + +static void g_lock_cleanup_dead( + struct g_lock *lck, + struct server_id *dead_blocker) +{ + bool exclusive_died; + struct server_id_buf tmp; + + if (dead_blocker == NULL) { + return; + } + + exclusive_died = server_id_equal(dead_blocker, &lck->exclusive); + + if (exclusive_died) { + DBG_DEBUG("Exclusive holder %s died\n", + server_id_str_buf(lck->exclusive, &tmp)); + lck->exclusive.pid = 0; + } + + if (lck->num_shared != 0) { + bool shared_died; + struct server_id shared; + + g_lock_get_shared(lck, 0, &shared); + shared_died = server_id_equal(dead_blocker, &shared); + + if (shared_died) { + DBG_DEBUG("Shared holder %s died\n", + server_id_str_buf(shared, &tmp)); + g_lock_del_shared(lck, 0); + } + } +} + +static ssize_t g_lock_find_shared( + struct g_lock *lck, + const struct server_id *self) +{ + size_t i; + + for (i=0; i<lck->num_shared; i++) { + struct server_id shared; + bool same; + + g_lock_get_shared(lck, i, &shared); + + same = server_id_equal(self, &shared); + if (same) { + return i; + } + } + + return -1; +} + +static void g_lock_cleanup_shared(struct g_lock *lck) +{ + size_t i; + struct server_id check; + bool exists; + + if (lck->num_shared == 0) { + return; + } + + /* + * Read locks can stay around forever if the process dies. Do + * a heuristic check for process existence: Check one random + * process for existence. Hopefully this will keep runaway + * read locks under control. + */ + i = generate_random() % lck->num_shared; + g_lock_get_shared(lck, i, &check); + + exists = serverid_exists(&check); + if (!exists) { + struct server_id_buf tmp; + DBG_DEBUG("Shared locker %s died -- removing\n", + server_id_str_buf(check, &tmp)); + g_lock_del_shared(lck, i); + } +} + +struct g_lock_lock_cb_state { + struct g_lock_ctx *ctx; + struct db_record *rec; + struct g_lock *lck; + struct server_id *new_shared; + g_lock_lock_cb_fn_t cb_fn; + void *cb_private; + TALLOC_CTX *update_mem_ctx; + TDB_DATA updated_data; + bool existed; + bool modified; + bool unlock; +}; + +NTSTATUS g_lock_lock_cb_dump(struct g_lock_lock_cb_state *cb_state, + void (*fn)(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data), + void *private_data) +{ + struct g_lock *lck = cb_state->lck; + + /* We allow a cn_fn only for G_LOCK_WRITE for now... */ + SMB_ASSERT(lck->num_shared == 0); + + fn(lck->exclusive, + 0, /* num_shared */ + NULL, /* shared */ + lck->data, + lck->datalen, + private_data); + + return NT_STATUS_OK; +} + +NTSTATUS g_lock_lock_cb_writev(struct g_lock_lock_cb_state *cb_state, + const TDB_DATA *dbufs, + size_t num_dbufs) +{ + NTSTATUS status; + + status = dbwrap_merge_dbufs(&cb_state->updated_data, + cb_state->update_mem_ctx, + dbufs, num_dbufs); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + cb_state->modified = true; + cb_state->lck->data = cb_state->updated_data.dptr; + cb_state->lck->datalen = cb_state->updated_data.dsize; + + return NT_STATUS_OK; +} + +void g_lock_lock_cb_unlock(struct g_lock_lock_cb_state *cb_state) +{ + cb_state->unlock = true; +} + +struct g_lock_lock_cb_watch_data_state { + struct tevent_context *ev; + struct g_lock_ctx *ctx; + TDB_DATA key; + struct server_id blocker; + bool blockerdead; + uint64_t unique_lock_epoch; + uint64_t unique_data_epoch; + uint64_t watch_instance; + NTSTATUS status; +}; + +static void g_lock_lock_cb_watch_data_done(struct tevent_req *subreq); + +struct tevent_req *g_lock_lock_cb_watch_data_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct g_lock_lock_cb_state *cb_state, + struct server_id blocker) +{ + struct tevent_req *req = NULL; + struct g_lock_lock_cb_watch_data_state *state = NULL; + struct tevent_req *subreq = NULL; + TDB_DATA key = dbwrap_record_get_key(cb_state->rec); + + req = tevent_req_create( + mem_ctx, &state, struct g_lock_lock_cb_watch_data_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = cb_state->ctx; + state->blocker = blocker; + + state->key = tdb_data_talloc_copy(state, key); + if (tevent_req_nomem(state->key.dptr, req)) { + return tevent_req_post(req, ev); + } + + state->unique_lock_epoch = cb_state->lck->unique_lock_epoch; + state->unique_data_epoch = cb_state->lck->unique_data_epoch; + + DBG_DEBUG("state->unique_data_epoch=%"PRIu64"\n", state->unique_data_epoch); + + subreq = dbwrap_watched_watch_send( + state, state->ev, cb_state->rec, 0, state->blocker); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, g_lock_lock_cb_watch_data_done, req); + + return req; +} + +static void g_lock_lock_cb_watch_data_done_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct g_lock_lock_cb_watch_data_state *state = tevent_req_data( + req, struct g_lock_lock_cb_watch_data_state); + struct tevent_req *subreq = NULL; + struct g_lock lck; + bool ok; + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + + if (lck.unique_data_epoch != state->unique_data_epoch) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + DBG_DEBUG("lck.unique_data_epoch=%"PRIu64", " + "state->unique_data_epoch=%"PRIu64"\n", + lck.unique_data_epoch, + state->unique_data_epoch); + state->status = NT_STATUS_OK; + return; + } + + /* + * The lock epoch changed, so we better + * remove ourself from the waiter list + * (most likely the first position) + * and re-add us at the end of the list. + * + * This gives other lock waiters a change + * to make progress. + * + * Otherwise we'll keep our waiter instance alive, + * keep waiting (most likely at first position). + */ + if (lck.unique_lock_epoch != state->unique_lock_epoch) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + state->watch_instance = dbwrap_watched_watch_add_instance(rec); + state->unique_lock_epoch = lck.unique_lock_epoch; + } + + subreq = dbwrap_watched_watch_send( + state, state->ev, rec, state->watch_instance, state->blocker); + if (subreq == NULL) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + state->status = NT_STATUS_NO_MEMORY; + return; + } + tevent_req_set_callback(subreq, g_lock_lock_cb_watch_data_done, req); + + state->status = NT_STATUS_EVENT_PENDING; +} + +static void g_lock_lock_cb_watch_data_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct g_lock_lock_cb_watch_data_state *state = tevent_req_data( + req, struct g_lock_lock_cb_watch_data_state); + NTSTATUS status; + uint64_t instance = 0; + + status = dbwrap_watched_watch_recv( + subreq, &instance, &state->blockerdead, &state->blocker); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n", + nt_errstr(status)); + return; + } + + state->watch_instance = instance; + + status = dbwrap_do_locked( + state->ctx->db, state->key, g_lock_lock_cb_watch_data_done_fn, req); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status)); + return; + } + if (NT_STATUS_EQUAL(state->status, NT_STATUS_EVENT_PENDING)) { + return; + } + if (tevent_req_nterror(req, state->status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS g_lock_lock_cb_watch_data_recv( + struct tevent_req *req, + bool *blockerdead, + struct server_id *blocker) +{ + struct g_lock_lock_cb_watch_data_state *state = tevent_req_data( + req, struct g_lock_lock_cb_watch_data_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (blockerdead != NULL) { + *blockerdead = state->blockerdead; + } + if (blocker != NULL) { + *blocker = state->blocker; + } + + return NT_STATUS_OK; +} + +void g_lock_lock_cb_wake_watchers(struct g_lock_lock_cb_state *cb_state) +{ + struct g_lock *lck = cb_state->lck; + + lck->unique_data_epoch = generate_unique_u64(lck->unique_data_epoch); + cb_state->modified = true; +} + +static NTSTATUS g_lock_lock_cb_run_and_store(struct g_lock_lock_cb_state *cb_state) +{ + struct g_lock *lck = cb_state->lck; + NTSTATUS success_status = NT_STATUS_OK; + NTSTATUS status; + + if (cb_state->cb_fn != NULL) { + + SMB_ASSERT(lck->num_shared == 0); + SMB_ASSERT(cb_state->new_shared == NULL); + + if (cb_state->ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) { + const char *name = dbwrap_name(cb_state->ctx->db); + dbwrap_lock_order_lock(name, cb_state->ctx->lock_order); + } + + cb_state->ctx->busy = true; + cb_state->cb_fn(cb_state, cb_state->cb_private); + cb_state->ctx->busy = false; + + if (cb_state->ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) { + const char *name = dbwrap_name(cb_state->ctx->db); + dbwrap_lock_order_unlock(name, cb_state->ctx->lock_order); + } + } + + if (cb_state->unlock) { + /* + * Unlocked should wake up watchers. + * + * We no longer need the lock, so + * force a wakeup of the next watchers, + * even if we don't do any update. + */ + dbwrap_watched_watch_reset_alerting(cb_state->rec); + dbwrap_watched_watch_force_alerting(cb_state->rec); + if (!cb_state->modified) { + /* + * The record was not changed at + * all, so we can also avoid + * storing the lck.unique_lock_epoch + * change + */ + return NT_STATUS_WAS_UNLOCKED; + } + lck->exclusive = (struct server_id) { .pid = 0 }; + cb_state->new_shared = NULL; + + if (lck->datalen == 0) { + if (!cb_state->existed) { + return NT_STATUS_WAS_UNLOCKED; + } + + status = dbwrap_record_delete(cb_state->rec); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_record_delete() failed: %s\n", + nt_errstr(status)); + return status; + } + return NT_STATUS_WAS_UNLOCKED; + } + + success_status = NT_STATUS_WAS_UNLOCKED; + } + + status = g_lock_store(cb_state->rec, + cb_state->lck, + cb_state->new_shared, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("g_lock_store() failed: %s\n", + nt_errstr(status)); + return status; + } + + return success_status; +} + +struct g_lock_lock_state { + struct tevent_context *ev; + struct g_lock_ctx *ctx; + TDB_DATA key; + enum g_lock_type type; + bool retry; + g_lock_lock_cb_fn_t cb_fn; + void *cb_private; +}; + +struct g_lock_lock_fn_state { + struct g_lock_lock_state *req_state; + struct server_id *dead_blocker; + + struct tevent_req *watch_req; + uint64_t watch_instance; + NTSTATUS status; +}; + +static int g_lock_lock_state_destructor(struct g_lock_lock_state *s); + +static NTSTATUS g_lock_trylock( + struct db_record *rec, + struct g_lock_lock_fn_state *state, + TDB_DATA data, + struct server_id *blocker) +{ + struct g_lock_lock_state *req_state = state->req_state; + struct server_id self = messaging_server_id(req_state->ctx->msg); + enum g_lock_type type = req_state->type; + bool retry = req_state->retry; + struct g_lock lck = { .exclusive.pid = 0 }; + struct g_lock_lock_cb_state cb_state = { + .ctx = req_state->ctx, + .rec = rec, + .lck = &lck, + .cb_fn = req_state->cb_fn, + .cb_private = req_state->cb_private, + .existed = data.dsize != 0, + .update_mem_ctx = talloc_tos(), + }; + struct server_id_buf tmp; + NTSTATUS status; + bool ok; + + ok = g_lock_parse(data.dptr, data.dsize, &lck); + if (!ok) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + DBG_DEBUG("g_lock_parse failed\n"); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + g_lock_cleanup_dead(&lck, state->dead_blocker); + + lck.unique_lock_epoch = generate_unique_u64(lck.unique_lock_epoch); + + if (lck.exclusive.pid != 0) { + bool self_exclusive = server_id_equal(&self, &lck.exclusive); + + if (!self_exclusive) { + bool exists = serverid_exists(&lck.exclusive); + if (!exists) { + lck.exclusive = (struct server_id) { .pid=0 }; + goto noexclusive; + } + + DBG_DEBUG("%s has an exclusive lock\n", + server_id_str_buf(lck.exclusive, &tmp)); + + if (type == G_LOCK_DOWNGRADE) { + struct server_id_buf tmp2; + + dbwrap_watched_watch_remove_instance(rec, + state->watch_instance); + + DBG_DEBUG("%s: Trying to downgrade %s\n", + server_id_str_buf(self, &tmp), + server_id_str_buf( + lck.exclusive, &tmp2)); + return NT_STATUS_NOT_LOCKED; + } + + if (type == G_LOCK_UPGRADE) { + ssize_t shared_idx; + + dbwrap_watched_watch_remove_instance(rec, + state->watch_instance); + + shared_idx = g_lock_find_shared(&lck, &self); + + if (shared_idx == -1) { + DBG_DEBUG("Trying to upgrade %s " + "without " + "existing shared lock\n", + server_id_str_buf( + self, &tmp)); + return NT_STATUS_NOT_LOCKED; + } + + /* + * We're trying to upgrade, and the + * exclusive lock is taken by someone + * else. This means that someone else + * is waiting for us to give up our + * shared lock. If we now also wait + * for someone to give their shared + * lock, we will deadlock. + */ + + DBG_DEBUG("Trying to upgrade %s while " + "someone else is also " + "trying to upgrade\n", + server_id_str_buf(self, &tmp)); + return NT_STATUS_POSSIBLE_DEADLOCK; + } + + DBG_DEBUG("Waiting for lck.exclusive=%s\n", + server_id_str_buf(lck.exclusive, &tmp)); + + /* + * We will return NT_STATUS_LOCK_NOT_GRANTED + * and need to monitor the record. + * + * If we don't have a watcher instance yet, + * we should add one. + */ + if (state->watch_instance == 0) { + state->watch_instance = + dbwrap_watched_watch_add_instance(rec); + } + + *blocker = lck.exclusive; + return NT_STATUS_LOCK_NOT_GRANTED; + } + + if (type == G_LOCK_DOWNGRADE) { + DBG_DEBUG("Downgrading %s from WRITE to READ\n", + server_id_str_buf(self, &tmp)); + + lck.exclusive = (struct server_id) { .pid = 0 }; + goto do_shared; + } + + if (!retry) { + dbwrap_watched_watch_remove_instance(rec, + state->watch_instance); + + DBG_DEBUG("%s already locked by self\n", + server_id_str_buf(self, &tmp)); + return NT_STATUS_WAS_LOCKED; + } + + g_lock_cleanup_shared(&lck); + + if (lck.num_shared != 0) { + g_lock_get_shared(&lck, 0, blocker); + + DBG_DEBUG("Continue waiting for shared lock %s\n", + server_id_str_buf(*blocker, &tmp)); + + /* + * We will return NT_STATUS_LOCK_NOT_GRANTED + * and need to monitor the record. + * + * If we don't have a watcher instance yet, + * we should add one. + */ + if (state->watch_instance == 0) { + state->watch_instance = + dbwrap_watched_watch_add_instance(rec); + } + + return NT_STATUS_LOCK_NOT_GRANTED; + } + + /* + * Retry after a conflicting lock was released.. + * All pending readers are gone so we got the lock... + */ + goto got_lock; + } + +noexclusive: + + if (type == G_LOCK_UPGRADE) { + ssize_t shared_idx = g_lock_find_shared(&lck, &self); + + if (shared_idx == -1) { + dbwrap_watched_watch_remove_instance(rec, + state->watch_instance); + + DBG_DEBUG("Trying to upgrade %s without " + "existing shared lock\n", + server_id_str_buf(self, &tmp)); + return NT_STATUS_NOT_LOCKED; + } + + g_lock_del_shared(&lck, shared_idx); + type = G_LOCK_WRITE; + } + + if (type == G_LOCK_WRITE) { + ssize_t shared_idx = g_lock_find_shared(&lck, &self); + + if (shared_idx != -1) { + dbwrap_watched_watch_remove_instance(rec, + state->watch_instance); + DBG_DEBUG("Trying to writelock existing shared %s\n", + server_id_str_buf(self, &tmp)); + return NT_STATUS_WAS_LOCKED; + } + + lck.exclusive = self; + + g_lock_cleanup_shared(&lck); + + if (lck.num_shared == 0) { + /* + * If we store ourself as exclusive writer, + * without any pending readers ... + */ + goto got_lock; + } + + if (state->watch_instance == 0) { + /* + * Here we have lck.num_shared != 0. + * + * We will return NT_STATUS_LOCK_NOT_GRANTED + * below. + * + * And don't have a watcher instance yet! + * + * We add it here before g_lock_store() + * in order to trigger just one + * low level dbwrap_do_locked() call. + */ + state->watch_instance = + dbwrap_watched_watch_add_instance(rec); + } + + status = g_lock_store(rec, &lck, NULL, NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("g_lock_store() failed: %s\n", + nt_errstr(status)); + return status; + } + + talloc_set_destructor( + req_state, g_lock_lock_state_destructor); + + g_lock_get_shared(&lck, 0, blocker); + + DBG_DEBUG("Waiting for %zu shared locks, " + "picking blocker %s\n", + lck.num_shared, + server_id_str_buf(*blocker, &tmp)); + + return NT_STATUS_LOCK_NOT_GRANTED; + } + +do_shared: + + g_lock_cleanup_shared(&lck); + cb_state.new_shared = &self; + goto got_lock; + +got_lock: + /* + * We got the lock we asked for, so we no + * longer need to monitor the record. + */ + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + + status = g_lock_lock_cb_run_and_store(&cb_state); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_WAS_UNLOCKED)) + { + DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n", + nt_errstr(status)); + return status; + } + + talloc_set_destructor(req_state, NULL); + return status; +} + +static void g_lock_lock_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct g_lock_lock_fn_state *state = private_data; + struct server_id blocker = {0}; + + /* + * We're trying to get a lock and if we are + * successful in doing that, we should not + * wakeup any other waiters, all they would + * find is that we're holding a lock they + * are conflicting with. + */ + dbwrap_watched_watch_skip_alerting(rec); + + state->status = g_lock_trylock(rec, state, value, &blocker); + if (!NT_STATUS_IS_OK(state->status)) { + DBG_DEBUG("g_lock_trylock returned %s\n", + nt_errstr(state->status)); + } + if (!NT_STATUS_EQUAL(state->status, NT_STATUS_LOCK_NOT_GRANTED)) { + return; + } + + state->watch_req = dbwrap_watched_watch_send( + state->req_state, state->req_state->ev, rec, state->watch_instance, blocker); + if (state->watch_req == NULL) { + state->status = NT_STATUS_NO_MEMORY; + } +} + +static int g_lock_lock_state_destructor(struct g_lock_lock_state *s) +{ + NTSTATUS status = g_lock_unlock(s->ctx, s->key); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("g_lock_unlock failed: %s\n", nt_errstr(status)); + } + return 0; +} + +static void g_lock_lock_retry(struct tevent_req *subreq); + +struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct g_lock_ctx *ctx, + TDB_DATA key, + enum g_lock_type type, + g_lock_lock_cb_fn_t cb_fn, + void *cb_private) +{ + struct tevent_req *req; + struct g_lock_lock_state *state; + struct g_lock_lock_fn_state fn_state; + NTSTATUS status; + bool ok; + + SMB_ASSERT(!ctx->busy); + + req = tevent_req_create(mem_ctx, &state, struct g_lock_lock_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = ctx; + state->key = key; + state->type = type; + state->cb_fn = cb_fn; + state->cb_private = cb_private; + + fn_state = (struct g_lock_lock_fn_state) { + .req_state = state, + }; + + /* + * We allow a cn_fn only for G_LOCK_WRITE for now. + * + * It's all we currently need and it makes a few things + * easier to implement. + */ + if (unlikely(cb_fn != NULL && type != G_LOCK_WRITE)) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_6); + return tevent_req_post(req, ev); + } + + status = dbwrap_do_locked(ctx->db, key, g_lock_lock_fn, &fn_state); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_do_locked failed: %s\n", + nt_errstr(status)); + return tevent_req_post(req, ev); + } + + if (NT_STATUS_IS_OK(fn_state.status)) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + if (!NT_STATUS_EQUAL(fn_state.status, NT_STATUS_LOCK_NOT_GRANTED)) { + tevent_req_nterror(req, fn_state.status); + return tevent_req_post(req, ev); + } + + if (tevent_req_nomem(fn_state.watch_req, req)) { + return tevent_req_post(req, ev); + } + + ok = tevent_req_set_endtime( + fn_state.watch_req, + state->ev, + timeval_current_ofs(5 + generate_random() % 5, 0)); + if (!ok) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(fn_state.watch_req, g_lock_lock_retry, req); + + return req; +} + +static void g_lock_lock_retry(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct g_lock_lock_state *state = tevent_req_data( + req, struct g_lock_lock_state); + struct g_lock_lock_fn_state fn_state; + struct server_id blocker = { .pid = 0 }; + bool blockerdead = false; + NTSTATUS status; + uint64_t instance = 0; + + status = dbwrap_watched_watch_recv(subreq, &instance, &blockerdead, &blocker); + DBG_DEBUG("watch_recv returned %s\n", nt_errstr(status)); + TALLOC_FREE(subreq); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + tevent_req_nterror(req, status); + return; + } + + state->retry = true; + + fn_state = (struct g_lock_lock_fn_state) { + .req_state = state, + .dead_blocker = blockerdead ? &blocker : NULL, + .watch_instance = instance, + }; + + status = dbwrap_do_locked(state->ctx->db, state->key, + g_lock_lock_fn, &fn_state); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_do_locked failed: %s\n", + nt_errstr(status)); + return; + } + + if (NT_STATUS_IS_OK(fn_state.status)) { + tevent_req_done(req); + return; + } + if (!NT_STATUS_EQUAL(fn_state.status, NT_STATUS_LOCK_NOT_GRANTED)) { + tevent_req_nterror(req, fn_state.status); + return; + } + + if (tevent_req_nomem(fn_state.watch_req, req)) { + return; + } + + if (!tevent_req_set_endtime( + fn_state.watch_req, state->ev, + timeval_current_ofs(5 + generate_random() % 5, 0))) { + return; + } + tevent_req_set_callback(fn_state.watch_req, g_lock_lock_retry, req); +} + +NTSTATUS g_lock_lock_recv(struct tevent_req *req) +{ + struct g_lock_lock_state *state = tevent_req_data( + req, struct g_lock_lock_state); + struct g_lock_ctx *ctx = state->ctx; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_WAS_UNLOCKED)) { + return NT_STATUS_OK; + } + return status; + } + + if ((ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) && + ((state->type == G_LOCK_READ) || + (state->type == G_LOCK_WRITE))) { + const char *name = dbwrap_name(ctx->db); + dbwrap_lock_order_lock(name, ctx->lock_order); + } + + return NT_STATUS_OK; +} + +struct g_lock_lock_simple_state { + struct g_lock_ctx *ctx; + struct server_id me; + enum g_lock_type type; + NTSTATUS status; + g_lock_lock_cb_fn_t cb_fn; + void *cb_private; +}; + +static void g_lock_lock_simple_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct g_lock_lock_simple_state *state = private_data; + struct server_id_buf buf; + struct g_lock lck = { .exclusive.pid = 0 }; + struct g_lock_lock_cb_state cb_state = { + .ctx = state->ctx, + .rec = rec, + .lck = &lck, + .cb_fn = state->cb_fn, + .cb_private = state->cb_private, + .existed = value.dsize != 0, + .update_mem_ctx = talloc_tos(), + }; + bool ok; + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + DBG_DEBUG("g_lock_parse failed\n"); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + + if (lck.exclusive.pid != 0) { + DBG_DEBUG("locked by %s\n", + server_id_str_buf(lck.exclusive, &buf)); + goto not_granted; + } + + if (state->type == G_LOCK_WRITE) { + if (lck.num_shared != 0) { + DBG_DEBUG("num_shared=%zu\n", lck.num_shared); + goto not_granted; + } + lck.exclusive = state->me; + } else if (state->type == G_LOCK_READ) { + g_lock_cleanup_shared(&lck); + cb_state.new_shared = &state->me; + } else { + smb_panic(__location__); + } + + lck.unique_lock_epoch = generate_unique_u64(lck.unique_lock_epoch); + + /* + * We are going to store us as owner, + * so we got what we were waiting for. + * + * So we no longer need to monitor the + * record. + */ + dbwrap_watched_watch_skip_alerting(rec); + + state->status = g_lock_lock_cb_run_and_store(&cb_state); + if (!NT_STATUS_IS_OK(state->status) && + !NT_STATUS_EQUAL(state->status, NT_STATUS_WAS_UNLOCKED)) + { + DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n", + nt_errstr(state->status)); + return; + } + + return; + +not_granted: + state->status = NT_STATUS_LOCK_NOT_GRANTED; +} + +NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, TDB_DATA key, + enum g_lock_type type, struct timeval timeout, + g_lock_lock_cb_fn_t cb_fn, + void *cb_private) +{ + TALLOC_CTX *frame; + struct tevent_context *ev; + struct tevent_req *req; + struct timeval end; + NTSTATUS status; + + SMB_ASSERT(!ctx->busy); + + /* + * We allow a cn_fn only for G_LOCK_WRITE for now. + * + * It's all we currently need and it makes a few things + * easier to implement. + */ + if (unlikely(cb_fn != NULL && type != G_LOCK_WRITE)) { + return NT_STATUS_INVALID_PARAMETER_5; + } + + if ((type == G_LOCK_READ) || (type == G_LOCK_WRITE)) { + /* + * This is an abstraction violation: Normally we do + * the sync wrappers around async functions with full + * nested event contexts. However, this is used in + * very hot code paths, so avoid the event context + * creation for the good path where there's no lock + * contention. My benchmark gave a factor of 2 + * improvement for lock/unlock. + */ + struct g_lock_lock_simple_state state = { + .ctx = ctx, + .me = messaging_server_id(ctx->msg), + .type = type, + .cb_fn = cb_fn, + .cb_private = cb_private, + }; + status = dbwrap_do_locked( + ctx->db, key, g_lock_lock_simple_fn, &state); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dbwrap_do_locked() failed: %s\n", + nt_errstr(status)); + return status; + } + + DBG_DEBUG("status=%s, state.status=%s\n", + nt_errstr(status), + nt_errstr(state.status)); + + if (NT_STATUS_IS_OK(state.status)) { + if (ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) { + const char *name = dbwrap_name(ctx->db); + dbwrap_lock_order_lock(name, ctx->lock_order); + } + return NT_STATUS_OK; + } + if (NT_STATUS_EQUAL(state.status, NT_STATUS_WAS_UNLOCKED)) { + /* without dbwrap_lock_order_lock() */ + return NT_STATUS_OK; + } + if (!NT_STATUS_EQUAL( + state.status, NT_STATUS_LOCK_NOT_GRANTED)) { + return state.status; + } + + if (timeval_is_zero(&timeout)) { + return NT_STATUS_LOCK_NOT_GRANTED; + } + + /* + * Fall back to the full g_lock_trylock logic, + * g_lock_lock_simple_fn() called above only covers + * the uncontended path. + */ + } + + frame = talloc_stackframe(); + status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = g_lock_lock_send(frame, ev, ctx, key, type, cb_fn, cb_private); + if (req == NULL) { + goto fail; + } + end = timeval_current_ofs(timeout.tv_sec, timeout.tv_usec); + if (!tevent_req_set_endtime(req, ev, end)) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = g_lock_lock_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct g_lock_unlock_state { + struct server_id self; + NTSTATUS status; +}; + +static void g_lock_unlock_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct g_lock_unlock_state *state = private_data; + struct server_id_buf tmp1, tmp2; + struct g_lock lck; + size_t i; + bool ok, exclusive; + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + DBG_DEBUG("g_lock_parse() failed\n"); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + + exclusive = server_id_equal(&state->self, &lck.exclusive); + + for (i=0; i<lck.num_shared; i++) { + struct server_id shared; + g_lock_get_shared(&lck, i, &shared); + if (server_id_equal(&state->self, &shared)) { + break; + } + } + + if (i < lck.num_shared) { + if (exclusive) { + DBG_DEBUG("%s both exclusive and shared (%zu)\n", + server_id_str_buf(state->self, &tmp1), + i); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + g_lock_del_shared(&lck, i); + } else { + if (!exclusive) { + DBG_DEBUG("Lock not found, self=%s, lck.exclusive=%s, " + "num_shared=%zu\n", + server_id_str_buf(state->self, &tmp1), + server_id_str_buf(lck.exclusive, &tmp2), + lck.num_shared); + state->status = NT_STATUS_NOT_FOUND; + return; + } + lck.exclusive = (struct server_id) { .pid = 0 }; + } + + if ((lck.exclusive.pid == 0) && + (lck.num_shared == 0) && + (lck.datalen == 0)) { + state->status = dbwrap_record_delete(rec); + return; + } + + if (!exclusive && lck.exclusive.pid != 0) { + /* + * We only had a read lock and there's + * someone waiting for an exclusive lock. + * + * Don't alert the exclusive lock waiter + * if there are still other read lock holders. + */ + g_lock_cleanup_shared(&lck); + if (lck.num_shared != 0) { + dbwrap_watched_watch_skip_alerting(rec); + } + } + + lck.unique_lock_epoch = generate_unique_u64(lck.unique_lock_epoch); + + state->status = g_lock_store(rec, &lck, NULL, NULL, 0); +} + +NTSTATUS g_lock_unlock(struct g_lock_ctx *ctx, TDB_DATA key) +{ + struct g_lock_unlock_state state = { + .self = messaging_server_id(ctx->msg), + }; + NTSTATUS status; + + SMB_ASSERT(!ctx->busy); + + status = dbwrap_do_locked(ctx->db, key, g_lock_unlock_fn, &state); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_do_locked failed: %s\n", + nt_errstr(status)); + return status; + } + if (!NT_STATUS_IS_OK(state.status)) { + DBG_WARNING("g_lock_unlock_fn failed: %s\n", + nt_errstr(state.status)); + return state.status; + } + + if (ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) { + const char *name = dbwrap_name(ctx->db); + dbwrap_lock_order_unlock(name, ctx->lock_order); + } + + return NT_STATUS_OK; +} + +struct g_lock_writev_data_state { + TDB_DATA key; + struct server_id self; + const TDB_DATA *dbufs; + size_t num_dbufs; + NTSTATUS status; +}; + +static void g_lock_writev_data_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct g_lock_writev_data_state *state = private_data; + struct g_lock lck; + bool exclusive; + bool ok; + + /* + * We're holding an exclusive write lock. + * + * Now we're updating the content of the record. + * + * We should not wakeup any other waiters, all they + * would find is that we're still holding a lock they + * are conflicting with. + */ + dbwrap_watched_watch_skip_alerting(rec); + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + DBG_DEBUG("g_lock_parse for %s failed\n", + tdb_data_dbg(state->key)); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + + exclusive = server_id_equal(&state->self, &lck.exclusive); + + /* + * Make sure we're really exclusive. We are marked as + * exclusive when we are waiting for an exclusive lock + */ + exclusive &= (lck.num_shared == 0); + + if (!exclusive) { + struct server_id_buf buf1, buf2; + DBG_DEBUG("Not locked by us: self=%s, lck.exclusive=%s, " + "lck.num_shared=%zu\n", + server_id_str_buf(state->self, &buf1), + server_id_str_buf(lck.exclusive, &buf2), + lck.num_shared); + state->status = NT_STATUS_NOT_LOCKED; + return; + } + + lck.unique_data_epoch = generate_unique_u64(lck.unique_data_epoch); + lck.data = NULL; + lck.datalen = 0; + state->status = g_lock_store( + rec, &lck, NULL, state->dbufs, state->num_dbufs); +} + +NTSTATUS g_lock_writev_data( + struct g_lock_ctx *ctx, + TDB_DATA key, + const TDB_DATA *dbufs, + size_t num_dbufs) +{ + struct g_lock_writev_data_state state = { + .key = key, + .self = messaging_server_id(ctx->msg), + .dbufs = dbufs, + .num_dbufs = num_dbufs, + }; + NTSTATUS status; + + SMB_ASSERT(!ctx->busy); + + status = dbwrap_do_locked( + ctx->db, key, g_lock_writev_data_fn, &state); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_do_locked failed: %s\n", + nt_errstr(status)); + return status; + } + if (!NT_STATUS_IS_OK(state.status)) { + DBG_WARNING("g_lock_writev_data_fn failed: %s\n", + nt_errstr(state.status)); + return state.status; + } + + return NT_STATUS_OK; +} + +NTSTATUS g_lock_write_data(struct g_lock_ctx *ctx, TDB_DATA key, + const uint8_t *buf, size_t buflen) +{ + TDB_DATA dbuf = { + .dptr = discard_const_p(uint8_t, buf), + .dsize = buflen, + }; + return g_lock_writev_data(ctx, key, &dbuf, 1); +} + +struct g_lock_locks_state { + int (*fn)(TDB_DATA key, void *private_data); + void *private_data; +}; + +static int g_lock_locks_fn(struct db_record *rec, void *priv) +{ + TDB_DATA key; + struct g_lock_locks_state *state = (struct g_lock_locks_state *)priv; + + key = dbwrap_record_get_key(rec); + return state->fn(key, state->private_data); +} + +int g_lock_locks(struct g_lock_ctx *ctx, + int (*fn)(TDB_DATA key, void *private_data), + void *private_data) +{ + struct g_lock_locks_state state; + NTSTATUS status; + int count; + + SMB_ASSERT(!ctx->busy); + + state.fn = fn; + state.private_data = private_data; + + status = dbwrap_traverse_read(ctx->db, g_lock_locks_fn, &state, &count); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + return count; +} + +struct g_lock_dump_state { + TALLOC_CTX *mem_ctx; + TDB_DATA key; + void (*fn)(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data); + void *private_data; + NTSTATUS status; + enum dbwrap_req_state req_state; +}; + +static void g_lock_dump_fn(TDB_DATA key, TDB_DATA data, + void *private_data) +{ + struct g_lock_dump_state *state = private_data; + struct g_lock lck = (struct g_lock) { .exclusive.pid = 0 }; + struct server_id *shared = NULL; + size_t i; + bool ok; + + ok = g_lock_parse(data.dptr, data.dsize, &lck); + if (!ok) { + DBG_DEBUG("g_lock_parse failed for %s\n", + tdb_data_dbg(state->key)); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + + if (lck.num_shared > 0) { + shared = talloc_array( + state->mem_ctx, struct server_id, lck.num_shared); + if (shared == NULL) { + DBG_DEBUG("talloc failed\n"); + state->status = NT_STATUS_NO_MEMORY; + return; + } + } + + for (i=0; i<lck.num_shared; i++) { + g_lock_get_shared(&lck, i, &shared[i]); + } + + state->fn(lck.exclusive, + lck.num_shared, + shared, + lck.data, + lck.datalen, + state->private_data); + + TALLOC_FREE(shared); + + state->status = NT_STATUS_OK; +} + +NTSTATUS g_lock_dump(struct g_lock_ctx *ctx, TDB_DATA key, + void (*fn)(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data), + void *private_data) +{ + struct g_lock_dump_state state = { + .mem_ctx = ctx, .key = key, + .fn = fn, .private_data = private_data + }; + NTSTATUS status; + + SMB_ASSERT(!ctx->busy); + + status = dbwrap_parse_record(ctx->db, key, g_lock_dump_fn, &state); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dbwrap_parse_record returned %s\n", + nt_errstr(status)); + return status; + } + if (!NT_STATUS_IS_OK(state.status)) { + DBG_DEBUG("g_lock_dump_fn returned %s\n", + nt_errstr(state.status)); + return state.status; + } + return NT_STATUS_OK; +} + +static void g_lock_dump_done(struct tevent_req *subreq); + +struct tevent_req *g_lock_dump_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct g_lock_ctx *ctx, + TDB_DATA key, + void (*fn)(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data), + void *private_data) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct g_lock_dump_state *state = NULL; + + SMB_ASSERT(!ctx->busy); + + req = tevent_req_create(mem_ctx, &state, struct g_lock_dump_state); + if (req == NULL) { + return NULL; + } + state->mem_ctx = state; + state->key = key; + state->fn = fn; + state->private_data = private_data; + + SMB_ASSERT(!ctx->busy); + + subreq = dbwrap_parse_record_send( + state, + ev, + ctx->db, + key, + g_lock_dump_fn, + state, + &state->req_state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, g_lock_dump_done, req); + return req; +} + +static void g_lock_dump_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct g_lock_dump_state *state = tevent_req_data( + req, struct g_lock_dump_state); + NTSTATUS status; + + status = dbwrap_parse_record_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status) || + tevent_req_nterror(req, state->status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS g_lock_dump_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +int g_lock_seqnum(struct g_lock_ctx *ctx) +{ + return dbwrap_get_seqnum(ctx->db); +} + +struct g_lock_watch_data_state { + struct tevent_context *ev; + struct g_lock_ctx *ctx; + TDB_DATA key; + struct server_id blocker; + bool blockerdead; + uint64_t unique_lock_epoch; + uint64_t unique_data_epoch; + uint64_t watch_instance; + NTSTATUS status; +}; + +static void g_lock_watch_data_done(struct tevent_req *subreq); + +static void g_lock_watch_data_send_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct g_lock_watch_data_state *state = tevent_req_data( + req, struct g_lock_watch_data_state); + struct tevent_req *subreq = NULL; + struct g_lock lck; + bool ok; + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + state->unique_lock_epoch = lck.unique_lock_epoch; + state->unique_data_epoch = lck.unique_data_epoch; + + DBG_DEBUG("state->unique_data_epoch=%"PRIu64"\n", state->unique_data_epoch); + + subreq = dbwrap_watched_watch_send( + state, state->ev, rec, 0, state->blocker); + if (subreq == NULL) { + state->status = NT_STATUS_NO_MEMORY; + return; + } + tevent_req_set_callback(subreq, g_lock_watch_data_done, req); + + state->status = NT_STATUS_EVENT_PENDING; +} + +struct tevent_req *g_lock_watch_data_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct g_lock_ctx *ctx, + TDB_DATA key, + struct server_id blocker) +{ + struct tevent_req *req = NULL; + struct g_lock_watch_data_state *state = NULL; + NTSTATUS status; + + SMB_ASSERT(!ctx->busy); + + req = tevent_req_create( + mem_ctx, &state, struct g_lock_watch_data_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = ctx; + state->blocker = blocker; + + state->key = tdb_data_talloc_copy(state, key); + if (tevent_req_nomem(state->key.dptr, req)) { + return tevent_req_post(req, ev); + } + + status = dbwrap_do_locked( + ctx->db, key, g_lock_watch_data_send_fn, req); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status)); + return tevent_req_post(req, ev); + } + + if (NT_STATUS_EQUAL(state->status, NT_STATUS_EVENT_PENDING)) { + return req; + } + if (tevent_req_nterror(req, state->status)) { + return tevent_req_post(req, ev); + } + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static void g_lock_watch_data_done_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct g_lock_watch_data_state *state = tevent_req_data( + req, struct g_lock_watch_data_state); + struct tevent_req *subreq = NULL; + struct g_lock lck; + bool ok; + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; + return; + } + + if (lck.unique_data_epoch != state->unique_data_epoch) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + DBG_DEBUG("lck.unique_data_epoch=%"PRIu64", " + "state->unique_data_epoch=%"PRIu64"\n", + lck.unique_data_epoch, + state->unique_data_epoch); + state->status = NT_STATUS_OK; + return; + } + + /* + * The lock epoch changed, so we better + * remove ourself from the waiter list + * (most likely the first position) + * and re-add us at the end of the list. + * + * This gives other lock waiters a change + * to make progress. + * + * Otherwise we'll keep our waiter instance alive, + * keep waiting (most likely at first position). + */ + if (lck.unique_lock_epoch != state->unique_lock_epoch) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + state->watch_instance = dbwrap_watched_watch_add_instance(rec); + state->unique_lock_epoch = lck.unique_lock_epoch; + } + + subreq = dbwrap_watched_watch_send( + state, state->ev, rec, state->watch_instance, state->blocker); + if (subreq == NULL) { + dbwrap_watched_watch_remove_instance(rec, state->watch_instance); + state->status = NT_STATUS_NO_MEMORY; + return; + } + tevent_req_set_callback(subreq, g_lock_watch_data_done, req); + + state->status = NT_STATUS_EVENT_PENDING; +} + +static void g_lock_watch_data_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct g_lock_watch_data_state *state = tevent_req_data( + req, struct g_lock_watch_data_state); + NTSTATUS status; + uint64_t instance = 0; + + status = dbwrap_watched_watch_recv( + subreq, &instance, &state->blockerdead, &state->blocker); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n", + nt_errstr(status)); + return; + } + + state->watch_instance = instance; + + status = dbwrap_do_locked( + state->ctx->db, state->key, g_lock_watch_data_done_fn, req); + if (tevent_req_nterror(req, status)) { + DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status)); + return; + } + if (NT_STATUS_EQUAL(state->status, NT_STATUS_EVENT_PENDING)) { + return; + } + if (tevent_req_nterror(req, state->status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS g_lock_watch_data_recv( + struct tevent_req *req, + bool *blockerdead, + struct server_id *blocker) +{ + struct g_lock_watch_data_state *state = tevent_req_data( + req, struct g_lock_watch_data_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (blockerdead != NULL) { + *blockerdead = state->blockerdead; + } + if (blocker != NULL) { + *blocker = state->blocker; + } + + return NT_STATUS_OK; +} + +static void g_lock_wake_watchers_fn( + struct db_record *rec, + TDB_DATA value, + void *private_data) +{ + struct g_lock lck = { .exclusive.pid = 0 }; + NTSTATUS status; + bool ok; + + ok = g_lock_parse(value.dptr, value.dsize, &lck); + if (!ok) { + DBG_WARNING("g_lock_parse failed\n"); + return; + } + + lck.unique_data_epoch = generate_unique_u64(lck.unique_data_epoch); + + status = g_lock_store(rec, &lck, NULL, NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("g_lock_store failed: %s\n", nt_errstr(status)); + return; + } +} + +void g_lock_wake_watchers(struct g_lock_ctx *ctx, TDB_DATA key) +{ + NTSTATUS status; + + SMB_ASSERT(!ctx->busy); + + status = dbwrap_do_locked(ctx->db, key, g_lock_wake_watchers_fn, NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dbwrap_do_locked returned %s\n", + nt_errstr(status)); + } +} diff --git a/source3/lib/gencache.c b/source3/lib/gencache.c new file mode 100644 index 0000000..07a08fa --- /dev/null +++ b/source3/lib/gencache.c @@ -0,0 +1,742 @@ +/* + Unix SMB/CIFS implementation. + + Generic, persistent and shared between processes cache mechanism for use + by various parts of the Samba code + + Copyright (C) Rafal Szczesniak 2002 + Copyright (C) Volker Lendecke 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/gencache.h" +#include "system/filesys.h" +#include "system/glob.h" +#include "util_tdb.h" +#include "tdb_wrap/tdb_wrap.h" +#include "zlib.h" +#include "lib/util/strv.h" +#include "lib/util/util_paths.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_TDB + +#define GENCACHE_USER_PATH "~/.cache/samba/gencache.tdb" + +static struct tdb_wrap *cache; + +/** + * @file gencache.c + * @brief Generic, persistent and shared between processes cache mechanism + * for use by various parts of the Samba code + * + **/ + +static bool gencache_pull_timeout(TDB_DATA key, + TDB_DATA data, + time_t *pres, + DATA_BLOB *payload); + +struct gencache_timeout { + time_t timeout; +}; + +bool gencache_timeout_expired(const struct gencache_timeout *t) +{ + return t->timeout <= time(NULL); +} + +/** + * Cache initialisation function. Opens cache tdb file or creates + * it if does not exist. + * + * @return true on successful initialisation of the cache or + * false on failure + **/ + +static bool gencache_init(void) +{ + char* cache_fname = NULL; + int open_flags = O_RDWR|O_CREAT; + int tdb_flags = TDB_INCOMPATIBLE_HASH|TDB_NOSYNC|TDB_MUTEX_LOCKING; + int hash_size; + + /* skip file open if it's already opened */ + if (cache) { + return true; + } + + hash_size = lp_parm_int(-1, "gencache", "hash_size", 10000); + + cache_fname = lock_path(talloc_tos(), "gencache.tdb"); + if (cache_fname == NULL) { + return false; + } + + DEBUG(5, ("Opening cache file at %s\n", cache_fname)); + + cache = tdb_wrap_open(NULL, cache_fname, hash_size, + tdb_flags, + open_flags, 0644); + /* + * Allow client tools to create a gencache in the home directory + * as a normal user. + */ + if (cache == NULL && errno == EACCES && geteuid() != 0) { + char *cache_dname = NULL, *tmp = NULL; + bool ok; + + TALLOC_FREE(cache_fname); + + cache_fname = path_expand_tilde(talloc_tos(), + GENCACHE_USER_PATH); + if (cache_fname == NULL) { + DBG_ERR("Failed to expand path: %s\n", + GENCACHE_USER_PATH); + return false; + } + + tmp = talloc_strdup(talloc_tos(), cache_fname); + if (tmp == NULL) { + DBG_ERR("No memory!\n"); + TALLOC_FREE(cache_fname); + return false; + } + + cache_dname = dirname(tmp); + if (cache_dname == NULL) { + DBG_ERR("Invalid path: %s\n", cache_fname); + TALLOC_FREE(tmp); + TALLOC_FREE(cache_fname); + return false; + } + + ok = directory_create_or_exists_recursive(cache_dname, 0700); + if (!ok) { + DBG_ERR("Failed to create directory: %s - %s\n", + cache_dname, strerror(errno)); + TALLOC_FREE(tmp); + TALLOC_FREE(cache_fname); + return false; + } + TALLOC_FREE(tmp); + + cache = tdb_wrap_open(NULL, + cache_fname, + hash_size, + tdb_flags, + open_flags, + 0644); + if (cache != NULL) { + DBG_INFO("Opening user cache file %s.\n", + cache_fname); + } + } + + if (cache == NULL) { + DEBUG(5, ("Opening %s failed: %s\n", cache_fname, + strerror(errno))); + TALLOC_FREE(cache_fname); + return false; + } + TALLOC_FREE(cache_fname); + + return true; +} + +/* + * Walk the hash chain for "key", deleting all expired entries for + * that hash chain + */ +struct gencache_prune_expired_state { + TALLOC_CTX *mem_ctx; + char *keys; +}; + +static int gencache_prune_expired_fn(struct tdb_context *tdb, + TDB_DATA key, + TDB_DATA data, + void *private_data) +{ + struct gencache_prune_expired_state *state = private_data; + struct gencache_timeout t; + bool ok = false; + bool expired = false; + + if ((key.dsize == 0) || (key.dptr[key.dsize-1] != '\0')) { + /* not a valid record, should never happen */ + return 0; + } + + ok = gencache_pull_timeout(key, data, &t.timeout, NULL); + if (ok) { + expired = gencache_timeout_expired(&t); + } + + if (!ok || expired) { + int ret; + + ret = strv_add(state->mem_ctx, &state->keys, (char *)key.dptr); + if (ret != 0) { + /* + * Exit the loop. It's unlikely that it will + * succeed next time. + */ + return -1; + } + } + + return 0; +} + +static void gencache_prune_expired(struct tdb_context *tdb, + TDB_DATA chain_key) +{ + struct gencache_prune_expired_state state = { + .mem_ctx = talloc_tos(), + }; + char *keystr = NULL; + int ret; + + ret = tdb_traverse_key_chain( + tdb, chain_key, gencache_prune_expired_fn, &state); + if (ret == -1) { + DBG_DEBUG("tdb_traverse_key_chain failed: %s\n", + tdb_errorstr(tdb)); + return; + } + + while ((keystr = strv_next(state.keys, keystr)) != NULL) { + TDB_DATA key = string_term_tdb_data(keystr); + + /* + * We expect the hash chain of "chain_key" to be + * locked. So between gencache_prune_expired_fn + * figuring out "keystr" is expired and the + * tdb_delete, nobody can have reset the timeout. + */ + tdb_delete(tdb, key); + } + + TALLOC_FREE(state.keys); +} + +/** + * Set an entry in the cache file. If there's no such + * one, then add it. + * + * @param keystr string that represents a key of this entry + * @param blob DATA_BLOB value being cached + * @param timeout time when the value is expired + * + * @retval true when entry is successfully stored + * @retval false on failure + **/ + +bool gencache_set_data_blob(const char *keystr, DATA_BLOB blob, + time_t timeout) +{ + TDB_DATA key; + int ret; + TDB_DATA dbufs[3]; + uint32_t crc; + + if ((keystr == NULL) || (blob.data == NULL)) { + return false; + } + + key = string_term_tdb_data(keystr); + + if (!gencache_init()) { + return false; + } + + dbufs[0] = (TDB_DATA) { .dptr = (uint8_t *)&timeout, + .dsize = sizeof(time_t) }; + dbufs[1] = (TDB_DATA) { .dptr = blob.data, .dsize = blob.length }; + + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, key.dptr, key.dsize); + crc = crc32(crc, dbufs[0].dptr, dbufs[0].dsize); + crc = crc32(crc, dbufs[1].dptr, dbufs[1].dsize); + + dbufs[2] = (TDB_DATA) { .dptr = (uint8_t *)&crc, + .dsize = sizeof(crc) }; + + DBG_DEBUG("Adding cache entry with key=[%s] and timeout=" + "[%s] (%ld seconds %s)\n", keystr, + timestring(talloc_tos(), timeout), + ((long int)timeout) - time(NULL), + timeout > time(NULL) ? "ahead" : "in the past"); + + ret = tdb_chainlock(cache->tdb, key); + if (ret == -1) { + DBG_WARNING("tdb_chainlock for key [%s] failed: %s\n", + keystr, tdb_errorstr(cache->tdb)); + return false; + } + + gencache_prune_expired(cache->tdb, key); + + ret = tdb_storev(cache->tdb, key, dbufs, ARRAY_SIZE(dbufs), 0); + + tdb_chainunlock(cache->tdb, key); + + if (ret == 0) { + return true; + } + if (tdb_error(cache->tdb) != TDB_ERR_CORRUPT) { + return false; + } + + ret = tdb_wipe_all(cache->tdb); + SMB_ASSERT(ret == 0); + + return false; +} + +/** + * Delete one entry from the cache file. + * + * @param keystr string that represents a key of this entry + * + * @retval true upon successful deletion + * @retval false in case of failure + **/ + +bool gencache_del(const char *keystr) +{ + TDB_DATA key = string_term_tdb_data(keystr); + int ret; + + if (keystr == NULL) { + return false; + } + + if (!gencache_init()) { + return false; + } + + DEBUG(10, ("Deleting cache entry (key=[%s])\n", keystr)); + + ret = tdb_delete(cache->tdb, key); + + if (ret == 0) { + return true; + } + if (tdb_error(cache->tdb) != TDB_ERR_CORRUPT) { + return false; + } + + ret = tdb_wipe_all(cache->tdb); + SMB_ASSERT(ret == 0); + + return true; /* We've deleted a bit more... */ +} + +static bool gencache_pull_timeout(TDB_DATA key, + TDB_DATA data, + time_t *pres, + DATA_BLOB *payload) +{ + size_t crc_ofs; + uint32_t crc, stored_crc; + + if ((data.dptr == NULL) || + (data.dsize < (sizeof(time_t) + sizeof(uint32_t)))) { + return false; + } + + crc_ofs = data.dsize - sizeof(uint32_t); + + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, key.dptr, key.dsize); + crc = crc32(crc, data.dptr, crc_ofs); + + memcpy(&stored_crc, data.dptr + crc_ofs, sizeof(uint32_t)); + + if (stored_crc != crc) { + return false; + } + + if (pres != NULL) { + memcpy(pres, data.dptr, sizeof(time_t)); + } + if (payload != NULL) { + *payload = (DATA_BLOB) { + .data = data.dptr+sizeof(time_t), + .length = data.dsize-sizeof(time_t)-sizeof(uint32_t), + }; + } + return true; +} + +struct gencache_parse_state { + void (*parser)(const struct gencache_timeout *timeout, + DATA_BLOB blob, + void *private_data); + void *private_data; + bool format_error; +}; + +static int gencache_parse_fn(TDB_DATA key, TDB_DATA data, void *private_data) +{ + struct gencache_parse_state *state = private_data; + struct gencache_timeout t; + DATA_BLOB payload; + bool ret; + + ret = gencache_pull_timeout(key, data, &t.timeout, &payload); + if (!ret) { + state->format_error = true; + return 0; + } + state->parser(&t, payload, state->private_data); + + return 0; +} + +bool gencache_parse(const char *keystr, + void (*parser)(const struct gencache_timeout *timeout, + DATA_BLOB blob, + void *private_data), + void *private_data) +{ + struct gencache_parse_state state = { + .parser = parser, .private_data = private_data + }; + TDB_DATA key = string_term_tdb_data(keystr); + int ret; + + if (keystr == NULL) { + return false; + } + if (!gencache_init()) { + return false; + } + + ret = tdb_parse_record(cache->tdb, key, + gencache_parse_fn, &state); + if ((ret == -1) && (tdb_error(cache->tdb) == TDB_ERR_CORRUPT)) { + goto wipe; + } + if (ret == -1) { + return false; + } + if (state.format_error) { + ret = tdb_delete(cache->tdb, key); + if (ret == -1) { + goto wipe; + } + return false; + } + return true; + +wipe: + ret = tdb_wipe_all(cache->tdb); + SMB_ASSERT(ret == 0); + return false; +} + +struct gencache_get_data_blob_state { + TALLOC_CTX *mem_ctx; + DATA_BLOB *blob; + time_t timeout; + bool result; +}; + +static void gencache_get_data_blob_parser(const struct gencache_timeout *t, + DATA_BLOB blob, + void *private_data) +{ + struct gencache_get_data_blob_state *state = + (struct gencache_get_data_blob_state *)private_data; + + if (t->timeout == 0) { + state->result = false; + return; + } + state->timeout = t->timeout; + + if (state->blob == NULL) { + state->result = true; + return; + } + + *state->blob = data_blob_talloc(state->mem_ctx, blob.data, + blob.length); + if (state->blob->data == NULL) { + state->result = false; + return; + } + state->result = true; +} + +/** + * Get existing entry from the cache file. + * + * @param keystr string that represents a key of this entry + * @param blob DATA_BLOB that is filled with entry's blob + * @param timeout pointer to a time_t that is filled with entry's + * timeout + * + * @retval true when entry is successfully fetched + * @retval false for failure + **/ + +bool gencache_get_data_blob(const char *keystr, TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, + time_t *timeout, bool *was_expired) +{ + struct gencache_get_data_blob_state state; + bool expired = false; + + state.result = false; + state.mem_ctx = mem_ctx; + state.blob = blob; + + if (!gencache_parse(keystr, gencache_get_data_blob_parser, &state)) { + goto fail; + } + if (!state.result) { + goto fail; + } + if (state.timeout <= time(NULL)) { + /* + * We're expired, delete the entry. We can't use gencache_del + * here, because that uses gencache_get_data_blob for checking + * the existence of a record. We know the thing exists and + * directly store an empty value with 0 timeout. + */ + gencache_set(keystr, "", 0); + expired = true; + goto fail; + } + if (timeout) { + *timeout = state.timeout; + } + + return true; + +fail: + if (was_expired != NULL) { + *was_expired = expired; + } + if (state.result && state.blob) { + data_blob_free(state.blob); + } + return false; +} + +/** + * Get existing entry from the cache file. + * + * @param keystr string that represents a key of this entry + * @param valstr buffer that is allocated and filled with the entry value + * buffer's disposing must be done outside + * @param timeout pointer to a time_t that is filled with entry's + * timeout + * + * @retval true when entry is successfully fetched + * @retval false for failure + **/ + +bool gencache_get(const char *keystr, TALLOC_CTX *mem_ctx, char **value, + time_t *ptimeout) +{ + DATA_BLOB blob; + bool ret = false; + + ret = gencache_get_data_blob(keystr, mem_ctx, &blob, ptimeout, NULL); + if (!ret) { + return false; + } + if ((blob.data == NULL) || (blob.length == 0)) { + data_blob_free(&blob); + return false; + } + if (blob.data[blob.length-1] != '\0') { + /* Not NULL terminated, can't be a string */ + data_blob_free(&blob); + return false; + } + if (value) { + /* + * talloc_move generates a type-punned warning here. As we + * leave the function immediately, do a simple talloc_steal. + */ + *value = (char *)talloc_steal(mem_ctx, blob.data); + return true; + } + data_blob_free(&blob); + return true; +} + +/** + * Set an entry in the cache file. If there's no such + * one, then add it. + * + * @param keystr string that represents a key of this entry + * @param value text representation value being cached + * @param timeout time when the value is expired + * + * @retval true when entry is successfully stored + * @retval false on failure + **/ + +bool gencache_set(const char *keystr, const char *value, time_t timeout) +{ + DATA_BLOB blob = data_blob_const(value, strlen(value)+1); + return gencache_set_data_blob(keystr, blob, timeout); +} + +struct gencache_iterate_blobs_state { + void (*fn)(const char *key, DATA_BLOB value, + time_t timeout, void *private_data); + const char *pattern; + void *private_data; +}; + +static int gencache_iterate_blobs_fn(struct tdb_context *tdb, TDB_DATA key, + TDB_DATA data, void *priv) +{ + struct gencache_iterate_blobs_state *state = + (struct gencache_iterate_blobs_state *)priv; + char *keystr; + char *free_key = NULL; + time_t timeout; + DATA_BLOB payload; + + if (key.dptr[key.dsize-1] == '\0') { + keystr = (char *)key.dptr; + } else { + /* ensure 0-termination */ + keystr = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize); + free_key = keystr; + if (keystr == NULL) { + goto done; + } + } + + if (!gencache_pull_timeout(key, data, &timeout, &payload)) { + goto done; + } + + if (timeout == 0) { + /* delete marker */ + goto done; + } + + if (fnmatch(state->pattern, keystr, 0) != 0) { + goto done; + } + + DEBUG(10, ("Calling function with arguments " + "(key=[%s], timeout=[%s])\n", + keystr, timestring(talloc_tos(), timeout))); + + state->fn(keystr, payload, timeout, state->private_data); + + done: + TALLOC_FREE(free_key); + return 0; +} + +void gencache_iterate_blobs(void (*fn)(const char *key, DATA_BLOB value, + time_t timeout, void *private_data), + void *private_data, const char *pattern) +{ + struct gencache_iterate_blobs_state state; + int ret; + + if ((fn == NULL) || (pattern == NULL) || !gencache_init()) { + return; + } + + DEBUG(5, ("Searching cache keys with pattern %s\n", pattern)); + + state.fn = fn; + state.pattern = pattern; + state.private_data = private_data; + + ret = tdb_traverse(cache->tdb, gencache_iterate_blobs_fn, &state); + + if ((ret == -1) && (tdb_error(cache->tdb) == TDB_ERR_CORRUPT)) { + ret = tdb_wipe_all(cache->tdb); + SMB_ASSERT(ret == 0); + } +} + +/** + * Iterate through all entries which key matches to specified pattern + * + * @param fn pointer to the function that will be supplied with each single + * matching cache entry (key, value and timeout) as an arguments + * @param data void pointer to an arbitrary data that is passed directly to the fn + * function on each call + * @param keystr_pattern pattern the existing entries' keys are matched to + * + **/ + +struct gencache_iterate_state { + void (*fn)(const char *key, const char *value, time_t timeout, + void *priv); + void *private_data; +}; + +static void gencache_iterate_fn(const char *key, DATA_BLOB value, + time_t timeout, void *private_data) +{ + struct gencache_iterate_state *state = + (struct gencache_iterate_state *)private_data; + char *valstr; + char *free_val = NULL; + + if (value.data[value.length-1] == '\0') { + valstr = (char *)value.data; + } else { + /* ensure 0-termination */ + valstr = talloc_strndup(talloc_tos(), (char *)value.data, value.length); + free_val = valstr; + if (valstr == NULL) { + goto done; + } + } + + DEBUG(10, ("Calling function with arguments " + "(key=[%s], value=[%s], timeout=[%s])\n", + key, valstr, timestring(talloc_tos(), timeout))); + + state->fn(key, valstr, timeout, state->private_data); + + done: + + TALLOC_FREE(free_val); +} + +void gencache_iterate(void (*fn)(const char *key, const char *value, + time_t timeout, void *dptr), + void *private_data, const char *pattern) +{ + struct gencache_iterate_state state; + + if (fn == NULL) { + return; + } + state.fn = fn; + state.private_data = private_data; + gencache_iterate_blobs(gencache_iterate_fn, &state, pattern); +} diff --git a/source3/lib/gencache.h b/source3/lib/gencache.h new file mode 100644 index 0000000..ccf16e9 --- /dev/null +++ b/source3/lib/gencache.h @@ -0,0 +1,60 @@ +/* + * Unix SMB/CIFS implementation. + * + * Generic, persistent and shared between processes cache mechanism for use + * by various parts of the Samba code + * + * Copyright (C) Rafal Szczesniak 2002 + * Copyright (C) Volker Lendecke 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_GENCACHE_H__ +#define __LIB_GENCACHE_H__ + +#include "replace.h" +#include "system/time.h" +#include "lib/util/data_blob.h" + +bool gencache_set(const char *keystr, const char *value, time_t timeout); +bool gencache_del(const char *keystr); +bool gencache_get(const char *keystr, TALLOC_CTX *mem_ctx, char **value, + time_t *ptimeout); + +/* + * This might look like overkill, but namemap_cache.c shows it's + * necessary :-) + */ +struct gencache_timeout; +bool gencache_timeout_expired(const struct gencache_timeout *t); + +bool gencache_parse(const char *keystr, + void (*parser)(const struct gencache_timeout *timeout, + DATA_BLOB blob, + void *private_data), + void *private_data); +bool gencache_get_data_blob(const char *keystr, TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, + time_t *timeout, bool *was_expired); +bool gencache_set_data_blob(const char *keystr, DATA_BLOB blob, + time_t timeout); +void gencache_iterate_blobs(void (*fn)(const char *key, DATA_BLOB value, + time_t timeout, void *private_data), + void *private_data, const char *pattern); +void gencache_iterate(void (*fn)(const char* key, const char *value, + time_t timeout, void* dptr), + void* data, const char* keystr_pattern); + +#endif diff --git a/source3/lib/global_contexts.c b/source3/lib/global_contexts.c new file mode 100644 index 0000000..4e3bbab --- /dev/null +++ b/source3/lib/global_contexts.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + Global contexts + + Copyright (C) Simo Sorce <idra@samba.org> 2010 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "replace.h" +#include "global_contexts.h" +#include <tevent.h> +#include "lib/util/fault.h" +#include "lib/util/samba_util.h" +#include "messages.h" + +static struct tevent_context *global_event_ctx = NULL; + +struct tevent_context *global_event_context(void) +{ + if (!global_event_ctx) { + /* + * Note we MUST use the NULL context here, not the + * autofree context, to avoid side effects in forked + * children exiting. + */ + global_event_ctx = samba_tevent_context_init(NULL); + } + if (!global_event_ctx) { + smb_panic("Could not init global event context"); + } + return global_event_ctx; +} + +void global_event_context_free(void) +{ + TALLOC_FREE(global_event_ctx); +} + +static struct messaging_context *global_msg_ctx = NULL; + +struct messaging_context *global_messaging_context(void) +{ + if (global_msg_ctx == NULL) { + /* + * Note we MUST use the NULL context here, not the + * autofree context, to avoid side effects in forked + * children exiting. + */ + global_msg_ctx = messaging_init(NULL, + global_event_context()); + } + return global_msg_ctx; +} + +void global_messaging_context_free(void) +{ + TALLOC_FREE(global_msg_ctx); +} diff --git a/source3/lib/global_contexts.h b/source3/lib/global_contexts.h new file mode 100644 index 0000000..8c5cd09 --- /dev/null +++ b/source3/lib/global_contexts.h @@ -0,0 +1,31 @@ +/* + * Unix SMB/CIFS implementation. + * Global contexts + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __GLOBAL_CONTEXTS_H__ +#define __GLOBAL_CONTEXTS_H__ + +struct tevent_context; + +struct tevent_context *global_event_context(void); +void global_event_context_free(void); + +struct messaging_context; +struct messaging_context *global_messaging_context(void); +void global_messaging_context_free(void); + +#endif diff --git a/source3/lib/id_cache.c b/source3/lib/id_cache.c new file mode 100644 index 0000000..adf6ccd --- /dev/null +++ b/source3/lib/id_cache.c @@ -0,0 +1,116 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Notify smbd about idmap changes + * @file msg_idmap.c + * @author Gregor Beck <gb@sernet.de> + * @date Feb 2011 + * + */ + +#include "includes.h" +#include "messages.h" +#include "lib/id_cache.h" +#include "../lib/util/memcache.h" +#include "idmap_cache.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "../libcli/security/dom_sid.h" + +bool id_cache_ref_parse(const char* str, struct id_cache_ref* id) +{ + struct dom_sid sid; + unsigned long ul; + char c, trash; + + if (sscanf(str, "%cID %lu%c", &c, &ul, &trash) == 2) { + switch(c) { + case 'G': + id->id.gid = ul; + id->type = GID; + return true; + case 'U': + id->id.uid = ul; + id->type = UID; + return true; + default: + break; + } + } else if (string_to_sid(&sid, str)) { + id->id.sid = sid; + id->type = SID; + return true; + } else if (strncmp(str, "USER ", 5) == 0) { + id->id.name = str + 5; + id->type = USERNAME; + return true; + } + return false; +} + +static bool delete_getpwnam_cache(const char *username) +{ + DATA_BLOB name = data_blob_string_const_null(username); + DEBUG(6, ("Delete passwd struct for %s from memcache\n", + username)); + memcache_delete(NULL, GETPWNAM_CACHE, name); + return true; +} + +void id_cache_delete_from_cache(const struct id_cache_ref* id) +{ + switch(id->type) { + case UID: + idmap_cache_del_uid(id->id.uid); + break; + case GID: + idmap_cache_del_gid(id->id.gid); + break; + case SID: + idmap_cache_del_sid(&id->id.sid); + break; + case USERNAME: + delete_getpwnam_cache(id->id.name); + break; + default: + break; + } +} + +void id_cache_delete_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB* data) +{ + const char *msg = (data && data->data) ? (const char *)data->data : "<NULL>"; + struct id_cache_ref id; + + if (!id_cache_ref_parse(msg, &id)) { + DEBUG(0, ("Invalid ?ID: %s\n", msg)); + return; + } + + id_cache_delete_from_cache(&id); +} + +void id_cache_register_msgs(struct messaging_context *ctx) +{ + messaging_register(ctx, NULL, ID_CACHE_DELETE, id_cache_delete_message); +} diff --git a/source3/lib/id_cache.h b/source3/lib/id_cache.h new file mode 100644 index 0000000..e77486e --- /dev/null +++ b/source3/lib/id_cache.h @@ -0,0 +1,40 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +struct id_cache_ref { + union { + uid_t uid; + gid_t gid; + struct dom_sid sid; + const char *name; + } id; + enum {UID, GID, SID, USERNAME} type; +}; + +bool id_cache_ref_parse(const char* str, struct id_cache_ref* id); + +void id_cache_delete_from_cache(const struct id_cache_ref* id); + +void id_cache_register_msgs(struct messaging_context *ctx); + +void id_cache_delete_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB* data); diff --git a/source3/lib/idmap_cache.c b/source3/lib/idmap_cache.c new file mode 100644 index 0000000..a4b8861 --- /dev/null +++ b/source3/lib/idmap_cache.c @@ -0,0 +1,442 @@ +/* + Unix SMB/CIFS implementation. + ID Mapping Cache + + Copyright (C) Volker Lendecke 2008 + + 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 <http://www.gnu.org/licenses/>.*/ + +#include "includes.h" +#include "idmap_cache.h" +#include "../libcli/security/security.h" +#include "../librpc/gen_ndr/idmap.h" +#include "lib/gencache.h" +#include "lib/util/string_wrappers.h" + +/** + * Find a sid2xid mapping + * @param[in] sid the sid to map + * @param[out] id where to put the result + * @param[out] expired is the cache entry expired? + * @retval Was anything in the cache at all? + * + * If id->id == -1 this was a negative mapping. + */ + +bool idmap_cache_find_sid2unixid(const struct dom_sid *sid, struct unixid *id, + bool *expired) +{ + struct dom_sid_buf sidstr; + char *key; + char *value = NULL; + char *endptr; + time_t timeout; + bool ret; + struct unixid tmp_id; + + key = talloc_asprintf(talloc_tos(), "IDMAP/SID2XID/%s", + dom_sid_str_buf(sid, &sidstr)); + if (key == NULL) { + return false; + } + ret = gencache_get(key, talloc_tos(), &value, &timeout); + if (!ret) { + goto done; + } + + DEBUG(10, ("Parsing value for key [%s]: value=[%s]\n", key, value)); + + if (value[0] == '\0') { + DEBUG(0, ("Failed to parse value for key [%s]: " + "value is empty\n", key)); + ret = false; + goto done; + } + + tmp_id.id = strtol(value, &endptr, 10); + + if ((value == endptr) && (tmp_id.id == 0)) { + DEBUG(0, ("Failed to parse value for key [%s]: value[%s] does " + "not start with a number\n", key, value)); + ret = false; + goto done; + } + + DEBUG(10, ("Parsing value for key [%s]: id=[%llu], endptr=[%s]\n", + key, (unsigned long long)tmp_id.id, endptr)); + + ret = (*endptr == ':'); + if (ret) { + switch (endptr[1]) { + case 'U': + tmp_id.type = ID_TYPE_UID; + break; + + case 'G': + tmp_id.type = ID_TYPE_GID; + break; + + case 'B': + tmp_id.type = ID_TYPE_BOTH; + break; + + case 'N': + tmp_id.type = ID_TYPE_NOT_SPECIFIED; + break; + + case '\0': + DEBUG(0, ("FAILED to parse value for key [%s] " + "(id=[%llu], endptr=[%s]): " + "no type character after colon\n", + key, (unsigned long long)tmp_id.id, endptr)); + ret = false; + goto done; + default: + DEBUG(0, ("FAILED to parse value for key [%s] " + "(id=[%llu], endptr=[%s]): " + "illegal type character '%c'\n", + key, (unsigned long long)tmp_id.id, endptr, + endptr[1])); + ret = false; + goto done; + } + if (endptr[2] != '\0') { + DEBUG(0, ("FAILED to parse value for key [%s] " + "(id=[%llu], endptr=[%s]): " + "more than 1 type character after colon\n", + key, (unsigned long long)tmp_id.id, endptr)); + ret = false; + goto done; + } + + *id = tmp_id; + *expired = (timeout <= time(NULL)); + } else { + DEBUG(0, ("FAILED to parse value for key [%s] (value=[%s]): " + "colon missing after id=[%llu]\n", + key, value, (unsigned long long)tmp_id.id)); + } + +done: + TALLOC_FREE(key); + TALLOC_FREE(value); + return ret; +} + +/** + * Find a sid2uid mapping + * @param[in] sid the sid to map + * @param[out] puid where to put the result + * @param[out] expired is the cache entry expired? + * @retval Was anything in the cache at all? + * + * If *puid == -1 this was a negative mapping. + */ + +bool idmap_cache_find_sid2uid(const struct dom_sid *sid, uid_t *puid, + bool *expired) +{ + bool ret; + struct unixid id; + ret = idmap_cache_find_sid2unixid(sid, &id, expired); + if (!ret) { + return false; + } + + if (id.type == ID_TYPE_BOTH || id.type == ID_TYPE_UID) { + *puid = id.id; + } else { + *puid = -1; + } + return true; +} + +/** + * Find a sid2gid mapping + * @param[in] sid the sid to map + * @param[out] pgid where to put the result + * @param[out] expired is the cache entry expired? + * @retval Was anything in the cache at all? + * + * If *pgid == -1 this was a negative mapping. + */ + +bool idmap_cache_find_sid2gid(const struct dom_sid *sid, gid_t *pgid, + bool *expired) +{ + bool ret; + struct unixid id; + ret = idmap_cache_find_sid2unixid(sid, &id, expired); + if (!ret) { + return false; + } + + if (id.type == ID_TYPE_BOTH || id.type == ID_TYPE_GID) { + *pgid = id.id; + } else { + *pgid = -1; + } + return true; +} + +struct idmap_cache_xid2sid_state { + struct dom_sid *sid; + bool *expired; + bool ret; +}; + +static void idmap_cache_xid2sid_parser(const struct gencache_timeout *timeout, + DATA_BLOB blob, + void *private_data) +{ + struct idmap_cache_xid2sid_state *state = + (struct idmap_cache_xid2sid_state *)private_data; + char *value; + + if ((blob.length == 0) || (blob.data[blob.length-1] != 0)) { + /* + * Not a string, can't be a valid mapping + */ + state->ret = false; + return; + } + + value = (char *)blob.data; + + if ((value[0] == '-') && (value[1] == '\0')) { + /* + * Return NULL SID, see comment to uid2sid + */ + *state->sid = (struct dom_sid) {0}; + state->ret = true; + } else { + state->ret = string_to_sid(state->sid, value); + } + if (state->ret) { + *state->expired = gencache_timeout_expired(timeout); + } +} + +/** + * Find a xid2sid mapping + * @param[in] id the unix id to map + * @param[out] sid where to put the result + * @param[out] expired is the cache entry expired? + * @retval Was anything in the cache at all? + * + * If "is_null_sid(sid)", this was a negative mapping. + */ +bool idmap_cache_find_xid2sid( + const struct unixid *id, struct dom_sid *sid, bool *expired) +{ + struct idmap_cache_xid2sid_state state = { + .sid = sid, .expired = expired + }; + fstring key; + char c; + + switch (id->type) { + case ID_TYPE_UID: + c = 'U'; + break; + case ID_TYPE_GID: + c = 'G'; + break; + default: + return false; + } + + fstr_sprintf(key, "IDMAP/%cID2SID/%d", c, (int)id->id); + + gencache_parse(key, idmap_cache_xid2sid_parser, &state); + return state.ret; +} + + +/** + * Store a mapping in the idmap cache + * @param[in] sid the sid to map + * @param[in] unix_id the unix_id to map + * + * If both parameters are valid values, then a positive mapping in both + * directions is stored. If "is_null_sid(sid)" is true, then this will be a + * negative mapping of xid, we want to cache that for this xid we could not + * find anything. Likewise if "xid==-1", then we want to cache that we did not + * find a mapping for the sid passed here. + */ + +void idmap_cache_set_sid2unixid(const struct dom_sid *sid, struct unixid *unix_id) +{ + time_t now = time(NULL); + time_t timeout; + fstring key, value; + + if (!is_null_sid(sid)) { + struct dom_sid_buf sidstr; + fstr_sprintf(key, "IDMAP/SID2XID/%s", + dom_sid_str_buf(sid, &sidstr)); + switch (unix_id->type) { + case ID_TYPE_UID: + fstr_sprintf(value, "%d:U", (int)unix_id->id); + break; + case ID_TYPE_GID: + fstr_sprintf(value, "%d:G", (int)unix_id->id); + break; + case ID_TYPE_BOTH: + fstr_sprintf(value, "%d:B", (int)unix_id->id); + break; + case ID_TYPE_NOT_SPECIFIED: + fstr_sprintf(value, "%d:N", (int)unix_id->id); + break; + default: + return; + } + timeout = (unix_id->id == -1) + ? lp_idmap_negative_cache_time() + : lp_idmap_cache_time(); + gencache_set(key, value, now + timeout); + } + if (unix_id->id != -1) { + if (is_null_sid(sid)) { + /* negative xid mapping */ + fstrcpy(value, "-"); + timeout = lp_idmap_negative_cache_time(); + } + else { + sid_to_fstring(value, sid); + timeout = lp_idmap_cache_time(); + } + switch (unix_id->type) { + case ID_TYPE_BOTH: + fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id); + gencache_set(key, value, now + timeout); + fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id); + gencache_set(key, value, now + timeout); + return; + + case ID_TYPE_UID: + fstr_sprintf(key, "IDMAP/UID2SID/%d", (int)unix_id->id); + break; + + case ID_TYPE_GID: + fstr_sprintf(key, "IDMAP/GID2SID/%d", (int)unix_id->id); + break; + + default: + return; + } + gencache_set(key, value, now + timeout); + } +} + +static char* key_xid2sid_str(TALLOC_CTX* mem_ctx, char t, const char* id) { + return talloc_asprintf(mem_ctx, "IDMAP/%cID2SID/%s", t, id); +} + +static char* key_xid2sid(TALLOC_CTX* mem_ctx, char t, int id) { + char str[32]; + snprintf(str, sizeof(str), "%d", id); + return key_xid2sid_str(mem_ctx, t, str); +} + +static char* key_sid2xid_str(TALLOC_CTX* mem_ctx, const char* id) { + return talloc_asprintf(mem_ctx, "IDMAP/SID2XID/%s", id); +} + +static bool idmap_cache_del_xid(char t, int xid) +{ + TALLOC_CTX* mem_ctx = talloc_stackframe(); + const char* key = key_xid2sid(mem_ctx, t, xid); + char* sid_str = NULL; + time_t timeout; + bool ret = true; + + if (!gencache_get(key, mem_ctx, &sid_str, &timeout)) { + DEBUG(3, ("no entry: %s\n", key)); + ret = false; + goto done; + } + + if (sid_str[0] != '-') { + const char* sid_key = key_sid2xid_str(mem_ctx, sid_str); + if (!gencache_del(sid_key)) { + DEBUG(2, ("failed to delete: %s\n", sid_key)); + ret = false; + } else { + DEBUG(5, ("delete: %s\n", sid_key)); + } + + } + + if (!gencache_del(key)) { + DEBUG(1, ("failed to delete: %s\n", key)); + ret = false; + } else { + DEBUG(5, ("delete: %s\n", key)); + } + +done: + talloc_free(mem_ctx); + return ret; +} + +bool idmap_cache_del_uid(uid_t uid) { + return idmap_cache_del_xid('U', uid); +} + +bool idmap_cache_del_gid(gid_t gid) { + return idmap_cache_del_xid('G', gid); +} + +bool idmap_cache_del_sid(const struct dom_sid *sid) +{ + TALLOC_CTX* mem_ctx = talloc_stackframe(); + bool ret = true; + bool expired; + struct unixid id; + struct dom_sid_buf sidbuf; + const char *sid_key; + + if (!idmap_cache_find_sid2unixid(sid, &id, &expired)) { + ret = false; + goto done; + } + + if (id.id != -1) { + switch (id.type) { + case ID_TYPE_BOTH: + idmap_cache_del_xid('U', id.id); + idmap_cache_del_xid('G', id.id); + break; + case ID_TYPE_UID: + idmap_cache_del_xid('U', id.id); + break; + case ID_TYPE_GID: + idmap_cache_del_xid('G', id.id); + break; + default: + break; + } + } + + sid_key = key_sid2xid_str(mem_ctx, dom_sid_str_buf(sid, &sidbuf)); + if (sid_key == NULL) { + return false; + } + /* If the mapping was symmetric, then this should fail */ + gencache_del(sid_key); +done: + talloc_free(mem_ctx); + return ret; +} diff --git a/source3/lib/idmap_cache.h b/source3/lib/idmap_cache.h new file mode 100644 index 0000000..5a90902 --- /dev/null +++ b/source3/lib/idmap_cache.h @@ -0,0 +1,40 @@ +/* + * Unix SMB/CIFS implementation. + * ID Mapping Cache + * + * Copyright (C) Volker Lendecke 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _LIB_IDMAP_CACHE_H_ +#define _LIB_IDMAP_CACHE_H_ + +/* The following definitions come from lib/idmap_cache.c */ +struct unixid; +bool idmap_cache_find_sid2unixid(const struct dom_sid *sid, struct unixid *id, + bool *expired); +bool idmap_cache_find_sid2uid(const struct dom_sid *sid, uid_t *puid, + bool *expired); +bool idmap_cache_find_sid2gid(const struct dom_sid *sid, gid_t *pgid, + bool *expired); +bool idmap_cache_find_xid2sid( + const struct unixid *id, struct dom_sid *sid, bool *expired); +void idmap_cache_set_sid2unixid(const struct dom_sid *sid, struct unixid *unix_id); + +bool idmap_cache_del_uid(uid_t uid); +bool idmap_cache_del_gid(gid_t gid); +bool idmap_cache_del_sid(const struct dom_sid *sid); + +#endif /* _LIB_IDMAP_CACHE_H_ */ diff --git a/source3/lib/interface.c b/source3/lib/interface.c new file mode 100644 index 0000000..beb1003 --- /dev/null +++ b/source3/lib/interface.c @@ -0,0 +1,721 @@ +/* + Unix SMB/CIFS implementation. + multiple interface handling + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/socket/interfaces.h" +#include "librpc/gen_ndr/ioctl.h" +#include "lib/util/smb_strtox.h" + +static struct iface_struct *probed_ifaces; +static int total_probed; + +static struct interface *local_interfaces; + +/**************************************************************************** + Check if an IP is one of mine. +**************************************************************************/ + +bool ismyaddr(const struct sockaddr *ip) +{ + struct interface *i; + for (i=local_interfaces;i;i=i->next) { + if (sockaddr_equal((struct sockaddr *)&i->ip,ip)) { + return true; + } + } + return false; +} + +bool ismyip_v4(struct in_addr ip) +{ + struct sockaddr_storage ss; + in_addr_to_sockaddr_storage(&ss, ip); + return ismyaddr((struct sockaddr *)&ss); +} + +/**************************************************************************** + Try and find an interface that matches an ip. If we cannot, return NULL. +**************************************************************************/ + +static struct interface *iface_find(const struct sockaddr *ip, + bool check_mask) +{ + struct interface *i; + + if (is_address_any(ip)) { + return local_interfaces; + } + + for (i=local_interfaces;i;i=i->next) { + if (check_mask) { + if (same_net(ip, (struct sockaddr *)&i->ip, (struct sockaddr *)&i->netmask)) { + return i; + } + } else if (sockaddr_equal((struct sockaddr *)&i->ip, ip)) { + return i; + } + } + + return NULL; +} + +/**************************************************************************** + Check if a packet is from a local (known) net. +**************************************************************************/ + +bool is_local_net(const struct sockaddr *from) +{ + struct interface *i; + for (i=local_interfaces;i;i=i->next) { + if (same_net(from, (struct sockaddr *)&i->ip, (struct sockaddr *)&i->netmask)) { + return true; + } + } + return false; +} + +#if defined(HAVE_IPV6) +void setup_linklocal_scope_id(struct sockaddr *pss) +{ + struct interface *i; + for (i=local_interfaces;i;i=i->next) { + if (sockaddr_equal((struct sockaddr *)&i->ip,pss)) { + struct sockaddr_in6 *psa6 = + (struct sockaddr_in6 *)pss; + psa6->sin6_scope_id = if_nametoindex(i->name); + return; + } + } +} +#endif + +/**************************************************************************** + Check if a packet is from a local (known) net. +**************************************************************************/ + +bool is_local_net_v4(struct in_addr from) +{ + struct sockaddr_storage ss; + + in_addr_to_sockaddr_storage(&ss, from); + return is_local_net((struct sockaddr *)&ss); +} + +/**************************************************************************** + How many interfaces do we have ? +**************************************************************************/ + +int iface_count(void) +{ + int ret = 0; + struct interface *i; + + for (i=local_interfaces;i;i=i->next) { + ret++; + } + return ret; +} + +/**************************************************************************** + How many non-loopback IPv4 interfaces do we have ? +**************************************************************************/ + +int iface_count_v4_nl(void) +{ + int ret = 0; + struct interface *i; + + for (i=local_interfaces;i;i=i->next) { + if (is_loopback_addr((struct sockaddr *)&i->ip)) { + continue; + } + if (i->ip.ss_family == AF_INET) { + ret++; + } + } + return ret; +} + +/**************************************************************************** + Return a pointer to the in_addr of the first IPv4 interface that's + not 0.0.0.0. +**************************************************************************/ + +const struct in_addr *first_ipv4_iface(void) +{ + struct interface *i; + + for (i=local_interfaces;i ;i=i->next) { + if ((i->ip.ss_family == AF_INET) && + (!is_zero_ip_v4(((struct sockaddr_in *)&i->ip)->sin_addr))) + { + break; + } + } + + if (!i) { + return NULL; + } + return &((const struct sockaddr_in *)&i->ip)->sin_addr; +} + +/**************************************************************************** + Return the Nth interface. +**************************************************************************/ + +struct interface *get_interface(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) { + n--; + } + + if (i) { + return i; + } + return NULL; +} + +/**************************************************************************** + Return IP sockaddr_storage of the Nth interface. +**************************************************************************/ + +const struct sockaddr_storage *iface_n_sockaddr_storage(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) { + n--; + } + + if (i) { + return &i->ip; + } + return NULL; +} + +/**************************************************************************** + Return IPv4 of the Nth interface (if a v4 address). NULL otherwise. +**************************************************************************/ + +const struct in_addr *iface_n_ip_v4(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) { + n--; + } + + if (i && i->ip.ss_family == AF_INET) { + return &((const struct sockaddr_in *)&i->ip)->sin_addr; + } + return NULL; +} + +/**************************************************************************** + Return IPv4 bcast of the Nth interface (if a v4 address). NULL otherwise. +**************************************************************************/ + +const struct in_addr *iface_n_bcast_v4(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) { + n--; + } + + if (i && i->ip.ss_family == AF_INET) { + return &((const struct sockaddr_in *)&i->bcast)->sin_addr; + } + return NULL; +} + +/**************************************************************************** + Return bcast of the Nth interface. +**************************************************************************/ + +const struct sockaddr_storage *iface_n_bcast(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) { + n--; + } + + if (i) { + return &i->bcast; + } + return NULL; +} + +/* these 3 functions return the ip/bcast/nmask for the interface + most appropriate for the given ip address. If they can't find + an appropriate interface they return the requested field of the + first known interface. */ + +const struct sockaddr_storage *iface_ip(const struct sockaddr *ip) +{ + struct interface *i = iface_find(ip, true); + if (i) { + return &i->ip; + } + + /* Search for the first interface with + * matching address family. */ + + for (i=local_interfaces;i;i=i->next) { + if (i->ip.ss_family == ip->sa_family) { + return &i->ip; + } + } + return NULL; +} + +/* + return True if a IP is directly reachable on one of our interfaces +*/ + +bool iface_local(const struct sockaddr *ip) +{ + return iface_find(ip, true) ? true : false; +} + +/**************************************************************************** + Add an interface to the linked list of interfaces. +****************************************************************************/ + +static void add_interface(const struct iface_struct *ifs) +{ + char addr[INET6_ADDRSTRLEN]; + struct interface *iface; + + if (iface_find((const struct sockaddr *)&ifs->ip, False)) { + DEBUG(3,("add_interface: not adding duplicate interface %s\n", + print_sockaddr(addr, sizeof(addr), &ifs->ip) )); + return; + } + + if (!(ifs->flags & (IFF_BROADCAST|IFF_LOOPBACK))) { + DEBUG(3,("not adding non-broadcast interface %s\n", + ifs->name )); + return; + } + + iface = SMB_MALLOC_P(struct interface); + if (!iface) { + return; + } + + ZERO_STRUCTPN(iface); + + iface->name = SMB_STRDUP(ifs->name); + if (!iface->name) { + SAFE_FREE(iface); + return; + } + iface->flags = ifs->flags; + iface->ip = ifs->ip; + iface->netmask = ifs->netmask; + iface->bcast = ifs->bcast; + iface->linkspeed = ifs->linkspeed; + iface->capability = ifs->capability; + iface->if_index = ifs->if_index; + + DLIST_ADD(local_interfaces, iface); + + DEBUG(2,("added interface %s ip=%s ", + iface->name, + print_sockaddr(addr, sizeof(addr), &iface->ip) )); + DEBUG(2,("bcast=%s ", + print_sockaddr(addr, sizeof(addr), + &iface->bcast) )); + DEBUG(2,("netmask=%s\n", + print_sockaddr(addr, sizeof(addr), + &iface->netmask) )); +} + + +static void parse_extra_info(char *key, uint64_t *speed, uint32_t *cap, + uint32_t *if_index) +{ + while (key != NULL && *key != '\0') { + char *next_key; + char *val; + int error = 0; + + next_key = strchr_m(key, ','); + if (next_key != NULL) { + *next_key++ = 0; + } + + val = strchr_m(key, '='); + if (val != NULL) { + *val++ = 0; + + if (strequal_m(key, "speed")) { + *speed = (uint64_t)smb_strtoull(val, + NULL, + 0, + &error, + SMB_STR_STANDARD); + if (error != 0) { + DBG_DEBUG("Invalid speed value (%s)\n", val); + } + } else if (strequal_m(key, "capability")) { + if (strequal_m(val, "RSS")) { + *cap |= FSCTL_NET_IFACE_RSS_CAPABLE; + } else if (strequal(val, "RDMA")) { + *cap |= FSCTL_NET_IFACE_RDMA_CAPABLE; + } else { + DBG_WARNING("Capability unknown: " + "'%s'\n", val); + } + } else if (strequal_m(key, "if_index")) { + *if_index = (uint32_t)smb_strtoul(val, + NULL, + 0, + &error, + SMB_STR_STANDARD); + if (error != 0) { + DBG_DEBUG("Invalid key value (%s)\n", val); + } + } else { + DBG_DEBUG("Key unknown: '%s'\n", key); + } + } + + key = next_key; + } +} + +/**************************************************************************** + Interpret a single element from a interfaces= config line. + + This handles the following different forms: + + 1) wildcard interface name + 2) DNS name + 3) IP/masklen + 4) ip/mask + 5) bcast/mask + + Additional information for an interface can be specified with + this extended syntax: + + "interface[;key1=value1[,key2=value2[...]]]" + + Note: The double quoting is important for the + smb.conf parser! Otherwise the ';' and ',' separates + two interfaces. + + where + - keys known: 'speed', 'capability', 'if_index' + - speed is in bits per second + - capabilities known: 'RSS', 'RDMA' + - if_index should be used with care, because + these indexes should not conicide with indexes + the kernel sets... + + Note: The specified values overwrite the autodetected values! + +****************************************************************************/ + +static void interpret_interface(char *token) +{ + struct sockaddr_storage ss; + struct sockaddr_storage ss_mask; + struct sockaddr_storage ss_net; + struct sockaddr_storage ss_bcast; + struct iface_struct ifs; + char *p; + int i; + bool added=false; + bool goodaddr = false; + uint64_t speed = 0; + uint32_t cap = FSCTL_NET_IFACE_NONE_CAPABLE; + uint32_t if_index = 0; + bool speed_set = false; + bool cap_set = false; + bool if_index_set = false; + + /* + * extract speed / capability information if present + */ + p = strchr_m(token, ';'); + if (p != NULL) { + *p++ = 0; + parse_extra_info(p, &speed, &cap, &if_index); + if (speed != 0) { + speed_set = true; + } + if (cap != FSCTL_NET_IFACE_NONE_CAPABLE) { + cap_set = true; + } + if (if_index != 0) { + if_index_set = true; + } + } + + /* first check if it is an interface name */ + for (i=0;i<total_probed;i++) { + if (gen_fnmatch(token, probed_ifaces[i].name) == 0) { + if (speed_set) { + probed_ifaces[i].linkspeed = speed; + } + if (cap_set) { + probed_ifaces[i].capability = cap; + } + if (if_index_set) { + probed_ifaces[i].if_index = if_index; + } + add_interface(&probed_ifaces[i]); + added = true; + } + } + if (added) { + return; + } + + p = strchr_m(token,'/'); + if (p == NULL) { + if (!interpret_string_addr(&ss, token, 0)) { + DEBUG(2, ("interpret_interface: Can't find address " + "for %s\n", token)); + return; + } + + for (i=0;i<total_probed;i++) { + if (sockaddr_equal((struct sockaddr *)&ss, + (struct sockaddr *)&probed_ifaces[i].ip)) + { + if (speed_set) { + probed_ifaces[i].linkspeed = speed; + } + if (cap_set) { + probed_ifaces[i].capability = cap; + } + if (if_index_set) { + probed_ifaces[i].if_index = if_index; + } + add_interface(&probed_ifaces[i]); + return; + } + } + DEBUG(2,("interpret_interface: " + "can't determine interface for %s\n", + token)); + return; + } + + /* parse it into an IP address/netmasklength pair */ + *p = 0; + goodaddr = interpret_string_addr(&ss, token, 0); + *p++ = '/'; + + if (!goodaddr) { + DEBUG(2,("interpret_interface: " + "can't determine interface for %s\n", + token)); + return; + } + + if (strlen(p) > 2) { + goodaddr = interpret_string_addr(&ss_mask, p, 0); + if (!goodaddr) { + DEBUG(2,("interpret_interface: " + "can't determine netmask from %s\n", + p)); + return; + } + } else { + int error = 0; + unsigned long val; + + val = smb_strtoul(p, NULL, 0, &error, SMB_STR_FULL_STR_CONV); + if (error != 0) { + DEBUG(2,("interpret_interface: " + "can't determine netmask value from %s\n", + p)); + return; + } + if (!make_netmask(&ss_mask, &ss, val)) { + DEBUG(2,("interpret_interface: " + "can't apply netmask value %lu from %s\n", + val, + p)); + return; + } + } + + make_bcast(&ss_bcast, &ss, &ss_mask); + make_net(&ss_net, &ss, &ss_mask); + + /* Maybe the first component was a broadcast address. */ + if (sockaddr_equal((struct sockaddr *)&ss_bcast, (struct sockaddr *)&ss) || + sockaddr_equal((struct sockaddr *)&ss_net, (struct sockaddr *)&ss)) { + for (i=0;i<total_probed;i++) { + if (same_net((struct sockaddr *)&ss, + (struct sockaddr *)&probed_ifaces[i].ip, + (struct sockaddr *)&ss_mask)) { + /* Temporarily replace netmask on + * the detected interface - user knows + * best.... */ + struct sockaddr_storage saved_mask = + probed_ifaces[i].netmask; + probed_ifaces[i].netmask = ss_mask; + DEBUG(2,("interpret_interface: " + "using netmask value %s from " + "config file on interface %s\n", + p, + probed_ifaces[i].name)); + if (speed_set) { + probed_ifaces[i].linkspeed = speed; + } + if (cap_set) { + probed_ifaces[i].capability = cap; + } + if (if_index_set) { + probed_ifaces[i].if_index = if_index; + } + add_interface(&probed_ifaces[i]); + probed_ifaces[i].netmask = saved_mask; + return; + } + } + DEBUG(2,("interpret_interface: Can't determine ip for " + "broadcast address %s\n", + token)); + return; + } + + /* Just fake up the interface definition. User knows best. */ + + DEBUG(2,("interpret_interface: Adding interface %s\n", + token)); + + ZERO_STRUCT(ifs); + (void)strlcpy(ifs.name, token, sizeof(ifs.name)); + ifs.flags = IFF_BROADCAST; + ifs.ip = ss; + ifs.netmask = ss_mask; + ifs.bcast = ss_bcast; + if (if_index_set) { + ifs.if_index = if_index; + } + if (speed_set) { + ifs.linkspeed = speed; + } else { + ifs.linkspeed = 1000 * 1000 * 1000; + } + ifs.capability = cap; + add_interface(&ifs); +} + +/**************************************************************************** + Load the list of network interfaces. +****************************************************************************/ + +void load_interfaces(void) +{ + struct iface_struct *ifaces = NULL; + const char **ptr = lp_interfaces(); + int i; + + gfree_interfaces(); + + /* Probe the kernel for interfaces */ + total_probed = get_interfaces(talloc_tos(), &ifaces); + + if (total_probed > 0) { + probed_ifaces = (struct iface_struct *)smb_memdup(ifaces, + sizeof(ifaces[0])*total_probed); + if (!probed_ifaces) { + DEBUG(0,("ERROR: smb_memdup failed\n")); + exit(1); + } + } + TALLOC_FREE(ifaces); + + /* if we don't have a interfaces line then use all broadcast capable + interfaces except loopback */ + if (!ptr || !*ptr || !**ptr) { + if (total_probed <= 0) { + DEBUG(0,("ERROR: Could not determine network " + "interfaces, you must use a interfaces config line\n")); + exit(1); + } + for (i=0;i<total_probed;i++) { + if (probed_ifaces[i].flags & IFF_BROADCAST) { + add_interface(&probed_ifaces[i]); + } + } + return; + } + + if (ptr) { + while (*ptr) { + char *ptr_cpy = SMB_STRDUP(*ptr); + if (ptr_cpy) { + interpret_interface(ptr_cpy); + free(ptr_cpy); + } + ptr++; + } + } + + if (!local_interfaces) { + DEBUG(0,("WARNING: no network interfaces found\n")); + } +} + + +void gfree_interfaces(void) +{ + while (local_interfaces) { + struct interface *iface = local_interfaces; + DLIST_REMOVE(local_interfaces, local_interfaces); + SAFE_FREE(iface->name); + SAFE_FREE(iface); + } + + SAFE_FREE(probed_ifaces); +} + +/**************************************************************************** + Return True if the list of probed interfaces has changed. +****************************************************************************/ + +bool interfaces_changed(void) +{ + bool ret = false; + int n; + struct iface_struct *ifaces = NULL; + + n = get_interfaces(talloc_tos(), &ifaces); + + if ((n > 0 )&& (n != total_probed || + memcmp(ifaces, probed_ifaces, sizeof(ifaces[0])*n))) { + ret = true; + } + + TALLOC_FREE(ifaces); + return ret; +} diff --git a/source3/lib/interface.h b/source3/lib/interface.h new file mode 100644 index 0000000..f45435b --- /dev/null +++ b/source3/lib/interface.h @@ -0,0 +1,46 @@ +/* + * + * Unix SMB/CIFS implementation. + * + * Type definitions for interfaces + * + * Copyright (c) 2020 Andreas Schneider <asn@samba.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _INTERFACE_H +#define _INTERFACE_H + +#include <system/network.h> + +bool ismyaddr(const struct sockaddr *ip); +bool ismyip_v4(struct in_addr ip); +bool is_local_net(const struct sockaddr *from); +void setup_linklocal_scope_id(struct sockaddr *pss); +bool is_local_net_v4(struct in_addr from); +int iface_count(void); +int iface_count_v4_nl(void); +const struct in_addr *first_ipv4_iface(void); +struct interface *get_interface(int n); +const struct sockaddr_storage *iface_n_sockaddr_storage(int n); +const struct in_addr *iface_n_ip_v4(int n); +const struct in_addr *iface_n_bcast_v4(int n); +const struct sockaddr_storage *iface_n_bcast(int n); +const struct sockaddr_storage *iface_ip(const struct sockaddr *ip); +bool iface_local(const struct sockaddr *ip); +void load_interfaces(void); +void gfree_interfaces(void); +bool interfaces_changed(void); + +#endif /* _INTERFACE_H */ diff --git a/source3/lib/ldap_debug_handler.c b/source3/lib/ldap_debug_handler.c new file mode 100644 index 0000000..27d9a20 --- /dev/null +++ b/source3/lib/ldap_debug_handler.c @@ -0,0 +1,51 @@ +/* + * Unix SMB/CIFS implementation. + * Intercept libldap debug output. + * Copyright (C) Michael Adam 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smb_ldap.h" + +#if defined(HAVE_LDAP) && defined(HAVE_LBER_LOG_PRINT_FN) +static void samba_ldap_log_print_fn(LDAP_CONST char *data) +{ + DEBUG(lp_ldap_debug_threshold(), ("[LDAP] %s", data)); +} +#endif + +void init_ldap_debugging(void) +{ +#if defined(HAVE_LDAP) && defined(HAVE_LBER_LOG_PRINT_FN) + int ret; + int ldap_debug_level = lp_ldap_debug_level(); + + ret = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ldap_debug_level); + if (ret != LDAP_OPT_SUCCESS) { + DEBUG(10, ("Error setting LDAP debug level.\n")); + } + + if (ldap_debug_level == 0) { + return; + } + + ret = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, + (void *)samba_ldap_log_print_fn); + if (ret != LBER_OPT_SUCCESS) { + DEBUG(10, ("Error setting LBER log print function.\n")); + } +#endif /* HAVE_LDAP && HAVE_LBER_LOG_PRINT_FN */ +} diff --git a/source3/lib/ldap_escape.c b/source3/lib/ldap_escape.c new file mode 100644 index 0000000..b44662c --- /dev/null +++ b/source3/lib/ldap_escape.c @@ -0,0 +1,137 @@ +/* + Unix SMB/CIFS implementation. + ldap filter argument escaping + + Copyright (C) 1998, 1999, 2000 Luke Howard <lukeh@padl.com>, + Copyright (C) 2003 Andrew Bartlett <abartlet@samba.org> + + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/** + * Escape a parameter to an LDAP filter string, so they cannot contain + * embedded ( ) * or \ chars which may cause it not to parse correctly. + * + * @param s The input string + * + * @return A string allocated with talloc(), containing the escaped string, + * and to be talloc_free()ed by the caller. + **/ + +char *escape_ldap_string(TALLOC_CTX *mem_ctx, const char *s) +{ + size_t len = strlen(s)+1; + char *output = talloc_array(mem_ctx, char, len); + const char *sub; + int i = 0; + char *p = output; + + if (output == NULL) { + return NULL; + } + + while (*s) + { + switch (*s) + { + case '*': + sub = "\\2a"; + break; + case '(': + sub = "\\28"; + break; + case ')': + sub = "\\29"; + break; + case '\\': + sub = "\\5c"; + break; + default: + sub = NULL; + break; + } + + if (sub) { + char *tmp; + len = len + 3; + tmp = talloc_realloc(mem_ctx, output, char, len); + if (tmp == NULL) { + TALLOC_FREE(output); + return NULL; + } + output = tmp; + + p = &output[i]; + memcpy(p, sub, 3); + p += 3; + i += 3; + + } else { + *p = *s; + p++; + i++; + } + s++; + } + + *p = '\0'; + return output; +} + +char *escape_rdn_val_string_alloc(const char *s) +{ + char *output, *p; + + /* The maximum size of the escaped string can be twice the actual size */ + output = (char *)SMB_MALLOC(2*strlen(s) + 1); + + if (output == NULL) { + return NULL; + } + + p = output; + + while (*s) + { + switch (*s) + { + case ',': + case '=': + case '+': + case '<': + case '>': + case '#': + case ';': + case '\\': + case '\"': + *p++ = '\\'; + *p++ = *s; + break; + default: + *p = *s; + p++; + } + + s++; + } + + *p = '\0'; + + /* resize the string to the actual final size */ + output = (char *)SMB_REALLOC(output, strlen(output) + 1); + return output; +} diff --git a/source3/lib/lsa.c b/source3/lib/lsa.c new file mode 100644 index 0000000..1c24a42 --- /dev/null +++ b/source3/lib/lsa.c @@ -0,0 +1,73 @@ +/* + * Helper functions related to the LSA server + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/*************************************************************************** + init_lsa_ref_domain_list - adds a domain if it's not already in, returns index. +***************************************************************************/ + +#include "replace.h" +#include "lib/util/data_blob.h" +#include "lib/util/time.h" +#include "libcli/security/dom_sid.h" +#include "librpc/gen_ndr/lsa.h" +#include "lsa.h" + +int init_lsa_ref_domain_list(TALLOC_CTX *mem_ctx, + struct lsa_RefDomainList *ref, + const char *dom_name, + struct dom_sid *dom_sid) +{ + int num = 0; + + if (dom_name != NULL) { + for (num = 0; num < ref->count; num++) { + if (dom_sid_equal(dom_sid, ref->domains[num].sid)) { + return num; + } + } + } else { + num = ref->count; + } + + if (num >= LSA_REF_DOMAIN_LIST_MULTIPLIER) { + /* index not found, already at maximum domain limit */ + return -1; + } + + ref->count = num + 1; + ref->max_size = LSA_REF_DOMAIN_LIST_MULTIPLIER; + + ref->domains = talloc_realloc(mem_ctx, ref->domains, + struct lsa_DomainInfo, ref->count); + if (!ref->domains) { + return -1; + } + + ZERO_STRUCT(ref->domains[num]); + + ref->domains[num].name.string = talloc_strdup(mem_ctx, dom_name); + if (!ref->domains[num].name.string) { + return -1; + } + + ref->domains[num].sid = dom_sid_dup(mem_ctx, dom_sid); + if (!ref->domains[num].sid) { + return -1; + } + + return num; +} diff --git a/source3/lib/messages.c b/source3/lib/messages.c new file mode 100644 index 0000000..7d3d469 --- /dev/null +++ b/source3/lib/messages.c @@ -0,0 +1,1480 @@ +/* + Unix SMB/CIFS implementation. + Samba internal messaging functions + Copyright (C) Andrew Tridgell 2000 + Copyright (C) 2001 by Martin Pool + Copyright (C) 2002 by Jeremy Allison + Copyright (C) 2007 by Volker Lendecke + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + @defgroup messages Internal messaging framework + @{ + @file messages.c + + @brief Module for internal messaging between Samba daemons. + + The idea is that if a part of Samba wants to do communication with + another Samba process then it will do a message_register() of a + dispatch function, and use message_send_pid() to send messages to + that process. + + The dispatch function is given the pid of the sender, and it can + use that to reply by message_send_pid(). See ping_message() for a + simple example. + + @caution Dispatch functions must be able to cope with incoming + messages on an *odd* byte boundary. + + This system doesn't have any inherent size limitations but is not + very efficient for large messages or when messages are sent in very + quick succession. + +*/ + +#include "includes.h" +#include "lib/util/server_id.h" +#include "dbwrap/dbwrap.h" +#include "serverid.h" +#include "messages.h" +#include "lib/util/tevent_unix.h" +#include "lib/background.h" +#include "lib/messaging/messages_dgm.h" +#include "lib/util/iov_buf.h" +#include "lib/util/server_id_db.h" +#include "lib/messaging/messages_dgm_ref.h" +#include "lib/messages_ctdb.h" +#include "lib/messages_ctdb_ref.h" +#include "lib/messages_util.h" +#include "cluster_support.h" +#include "ctdbd_conn.h" +#include "ctdb_srvids.h" + +#ifdef CLUSTER_SUPPORT +#include "ctdb_protocol.h" +#endif + +struct messaging_callback { + struct messaging_callback *prev, *next; + uint32_t msg_type; + void (*fn)(struct messaging_context *msg, void *private_data, + uint32_t msg_type, + struct server_id server_id, DATA_BLOB *data); + void *private_data; +}; + +struct messaging_registered_ev { + struct tevent_context *ev; + struct tevent_immediate *im; + size_t refcount; +}; + +struct messaging_context { + struct server_id id; + struct tevent_context *event_ctx; + struct messaging_callback *callbacks; + + struct messaging_rec *posted_msgs; + + struct messaging_registered_ev *event_contexts; + + struct tevent_req **new_waiters; + size_t num_new_waiters; + + struct tevent_req **waiters; + size_t num_waiters; + + struct server_id_db *names_db; + + TALLOC_CTX *per_process_talloc_ctx; +}; + +static struct messaging_rec *messaging_rec_dup(TALLOC_CTX *mem_ctx, + struct messaging_rec *rec); +static bool messaging_dispatch_classic(struct messaging_context *msg_ctx, + struct messaging_rec *rec); +static bool messaging_dispatch_waiters(struct messaging_context *msg_ctx, + struct tevent_context *ev, + struct messaging_rec *rec); +static void messaging_dispatch_rec(struct messaging_context *msg_ctx, + struct tevent_context *ev, + struct messaging_rec *rec); + +/**************************************************************************** + A useful function for testing the message system. +****************************************************************************/ + +static void ping_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) +{ + struct server_id_buf idbuf; + + DEBUG(1, ("INFO: Received PING message from PID %s [%.*s]\n", + server_id_str_buf(src, &idbuf), (int)data->length, + data->data ? (char *)data->data : "")); + + messaging_send(msg_ctx, src, MSG_PONG, data); +} + +struct messaging_rec *messaging_rec_create( + TALLOC_CTX *mem_ctx, struct server_id src, struct server_id dst, + uint32_t msg_type, const struct iovec *iov, int iovlen, + const int *fds, size_t num_fds) +{ + ssize_t buflen; + uint8_t *buf; + struct messaging_rec *result; + + if (num_fds > INT8_MAX) { + return NULL; + } + + buflen = iov_buflen(iov, iovlen); + if (buflen == -1) { + return NULL; + } + buf = talloc_array(mem_ctx, uint8_t, buflen); + if (buf == NULL) { + return NULL; + } + iov_buf(iov, iovlen, buf, buflen); + + { + struct messaging_rec rec; + int64_t fds64[MAX(1, num_fds)]; + size_t i; + + for (i=0; i<num_fds; i++) { + fds64[i] = fds[i]; + } + + rec = (struct messaging_rec) { + .msg_version = MESSAGE_VERSION, .msg_type = msg_type, + .src = src, .dest = dst, + .buf.data = buf, .buf.length = buflen, + .num_fds = num_fds, .fds = fds64, + }; + + result = messaging_rec_dup(mem_ctx, &rec); + } + + TALLOC_FREE(buf); + + return result; +} + +static bool messaging_register_event_context(struct messaging_context *ctx, + struct tevent_context *ev) +{ + size_t i, num_event_contexts; + struct messaging_registered_ev *free_reg = NULL; + struct messaging_registered_ev *tmp; + + num_event_contexts = talloc_array_length(ctx->event_contexts); + + for (i=0; i<num_event_contexts; i++) { + struct messaging_registered_ev *reg = &ctx->event_contexts[i]; + + if (reg->refcount == 0) { + if (reg->ev != NULL) { + abort(); + } + free_reg = reg; + /* + * We continue here and may find another + * free_req, but the important thing is + * that we continue to search for an + * existing registration in the loop. + */ + continue; + } + + if (reg->ev == ev) { + reg->refcount += 1; + return true; + } + } + + if (free_reg == NULL) { + struct tevent_immediate *im = NULL; + + im = tevent_create_immediate(ctx); + if (im == NULL) { + return false; + } + + tmp = talloc_realloc(ctx, ctx->event_contexts, + struct messaging_registered_ev, + num_event_contexts+1); + if (tmp == NULL) { + return false; + } + ctx->event_contexts = tmp; + + free_reg = &ctx->event_contexts[num_event_contexts]; + free_reg->im = talloc_move(ctx->event_contexts, &im); + } + + /* + * free_reg->im might be cached + */ + free_reg->ev = ev; + free_reg->refcount = 1; + + return true; +} + +static bool messaging_deregister_event_context(struct messaging_context *ctx, + struct tevent_context *ev) +{ + size_t i, num_event_contexts; + + num_event_contexts = talloc_array_length(ctx->event_contexts); + + for (i=0; i<num_event_contexts; i++) { + struct messaging_registered_ev *reg = &ctx->event_contexts[i]; + + if (reg->refcount == 0) { + continue; + } + + if (reg->ev == ev) { + reg->refcount -= 1; + + if (reg->refcount == 0) { + /* + * The primary event context + * is never unregistered using + * messaging_deregister_event_context() + * it's only registered using + * messaging_register_event_context(). + */ + SMB_ASSERT(ev != ctx->event_ctx); + SMB_ASSERT(reg->ev != ctx->event_ctx); + + /* + * Not strictly necessary, just + * paranoia + */ + reg->ev = NULL; + + /* + * Do not talloc_free(reg->im), + * recycle immediates events. + * + * We just invalidate it using + * the primary event context, + * which is never unregistered. + */ + tevent_schedule_immediate(reg->im, + ctx->event_ctx, + NULL, NULL); + } + return true; + } + } + return false; +} + +static void messaging_post_main_event_context(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct messaging_context *ctx = talloc_get_type_abort( + private_data, struct messaging_context); + + while (ctx->posted_msgs != NULL) { + struct messaging_rec *rec = ctx->posted_msgs; + bool consumed; + + DLIST_REMOVE(ctx->posted_msgs, rec); + + consumed = messaging_dispatch_classic(ctx, rec); + if (!consumed) { + consumed = messaging_dispatch_waiters( + ctx, ctx->event_ctx, rec); + } + + if (!consumed) { + uint8_t i; + + for (i=0; i<rec->num_fds; i++) { + close(rec->fds[i]); + } + } + + TALLOC_FREE(rec); + } +} + +static void messaging_post_sub_event_context(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct messaging_context *ctx = talloc_get_type_abort( + private_data, struct messaging_context); + struct messaging_rec *rec, *next; + + for (rec = ctx->posted_msgs; rec != NULL; rec = next) { + bool consumed; + + next = rec->next; + + consumed = messaging_dispatch_waiters(ctx, ev, rec); + if (consumed) { + DLIST_REMOVE(ctx->posted_msgs, rec); + TALLOC_FREE(rec); + } + } +} + +static bool messaging_alert_event_contexts(struct messaging_context *ctx) +{ + size_t i, num_event_contexts; + + num_event_contexts = talloc_array_length(ctx->event_contexts); + + for (i=0; i<num_event_contexts; i++) { + struct messaging_registered_ev *reg = &ctx->event_contexts[i]; + + if (reg->refcount == 0) { + continue; + } + + /* + * We depend on schedule_immediate to work + * multiple times. Might be a bit inefficient, + * but this needs to be proven in tests. The + * alternatively would be to track whether the + * immediate has already been scheduled. For + * now, avoid that complexity here. + */ + + if (reg->ev == ctx->event_ctx) { + tevent_schedule_immediate( + reg->im, reg->ev, + messaging_post_main_event_context, + ctx); + } else { + tevent_schedule_immediate( + reg->im, reg->ev, + messaging_post_sub_event_context, + ctx); + } + + } + return true; +} + +static void messaging_recv_cb(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data) +{ + struct messaging_context *msg_ctx = talloc_get_type_abort( + private_data, struct messaging_context); + struct server_id_buf idbuf; + struct messaging_rec rec; + int64_t fds64[MAX(1, MIN(num_fds, INT8_MAX))]; + size_t i; + + if (msg_len < MESSAGE_HDR_LENGTH) { + DBG_WARNING("message too short: %zu\n", msg_len); + return; + } + + if (num_fds > INT8_MAX) { + DBG_WARNING("too many fds: %zu\n", num_fds); + return; + } + + for (i=0; i < num_fds; i++) { + fds64[i] = fds[i]; + } + + rec = (struct messaging_rec) { + .msg_version = MESSAGE_VERSION, + .buf.data = discard_const_p(uint8_t, msg) + MESSAGE_HDR_LENGTH, + .buf.length = msg_len - MESSAGE_HDR_LENGTH, + .num_fds = num_fds, + .fds = fds64, + }; + + message_hdr_get(&rec.msg_type, &rec.src, &rec.dest, msg); + + DBG_DEBUG("Received message 0x%x len %zu (num_fds:%zu) from %s\n", + (unsigned)rec.msg_type, rec.buf.length, num_fds, + server_id_str_buf(rec.src, &idbuf)); + + if (server_id_same_process(&rec.src, &msg_ctx->id)) { + DBG_DEBUG("Ignoring self-send\n"); + return; + } + + messaging_dispatch_rec(msg_ctx, ev, &rec); + + for (i=0; i<num_fds; i++) { + fds[i] = fds64[i]; + } +} + +static int messaging_context_destructor(struct messaging_context *ctx) +{ + size_t i; + + for (i=0; i<ctx->num_new_waiters; i++) { + if (ctx->new_waiters[i] != NULL) { + tevent_req_set_cleanup_fn(ctx->new_waiters[i], NULL); + ctx->new_waiters[i] = NULL; + } + } + for (i=0; i<ctx->num_waiters; i++) { + if (ctx->waiters[i] != NULL) { + tevent_req_set_cleanup_fn(ctx->waiters[i], NULL); + ctx->waiters[i] = NULL; + } + } + + /* + * The immediates from messaging_alert_event_contexts + * reference "ctx". Don't let them outlive the + * messaging_context we're destroying here. + */ + TALLOC_FREE(ctx->event_contexts); + + return 0; +} + +static const char *private_path(const char *name) +{ + return talloc_asprintf(talloc_tos(), "%s/%s", lp_private_dir(), name); +} + +static NTSTATUS messaging_init_internal(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context **pmsg_ctx) +{ + TALLOC_CTX *frame; + struct messaging_context *ctx; + NTSTATUS status; + int ret; + const char *lck_path; + const char *priv_path; + void *ref; + bool ok; + + /* + * sec_init() *must* be called before any other + * functions that use sec_XXX(). e.g. sec_initial_uid(). + */ + + sec_init(); + + lck_path = lock_path(talloc_tos(), "msg.lock"); + if (lck_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ok = directory_create_or_exist_strict(lck_path, + sec_initial_uid(), + 0755); + if (!ok) { + DBG_DEBUG("Could not create lock directory: %s\n", + strerror(errno)); + return NT_STATUS_ACCESS_DENIED; + } + + priv_path = private_path("msg.sock"); + if (priv_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ok = directory_create_or_exist_strict(priv_path, sec_initial_uid(), + 0700); + if (!ok) { + DBG_DEBUG("Could not create msg directory: %s\n", + strerror(errno)); + return NT_STATUS_ACCESS_DENIED; + } + + frame = talloc_stackframe(); + if (frame == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ctx = talloc_zero(frame, struct messaging_context); + if (ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ctx->id = (struct server_id) { + .pid = tevent_cached_getpid(), .vnn = NONCLUSTER_VNN + }; + + ctx->event_ctx = ev; + + ctx->per_process_talloc_ctx = talloc_new(ctx); + if (ctx->per_process_talloc_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ok = messaging_register_event_context(ctx, ev); + if (!ok) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ref = messaging_dgm_ref( + ctx->per_process_talloc_ctx, + ctx->event_ctx, + &ctx->id.unique_id, + priv_path, + lck_path, + messaging_recv_cb, + ctx, + &ret); + if (ref == NULL) { + DEBUG(2, ("messaging_dgm_ref failed: %s\n", strerror(ret))); + status = map_nt_error_from_unix(ret); + goto done; + } + talloc_set_destructor(ctx, messaging_context_destructor); + +#ifdef CLUSTER_SUPPORT + if (lp_clustering()) { + ref = messaging_ctdb_ref( + ctx->per_process_talloc_ctx, + ctx->event_ctx, + lp_ctdbd_socket(), + lp_ctdb_timeout(), + ctx->id.unique_id, + messaging_recv_cb, + ctx, + &ret); + if (ref == NULL) { + DBG_NOTICE("messaging_ctdb_ref failed: %s\n", + strerror(ret)); + status = map_nt_error_from_unix(ret); + goto done; + } + } +#endif + + ctx->id.vnn = get_my_vnn(); + + ctx->names_db = server_id_db_init(ctx, + ctx->id, + lp_lock_directory(), + 0, + TDB_INCOMPATIBLE_HASH|TDB_CLEAR_IF_FIRST); + if (ctx->names_db == NULL) { + DBG_DEBUG("server_id_db_init failed\n"); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + messaging_register(ctx, NULL, MSG_PING, ping_message); + + /* Register some debugging related messages */ + + register_msg_pool_usage(ctx->per_process_talloc_ctx, ctx); + register_dmalloc_msgs(ctx); + debug_register_msgs(ctx); + + { + struct server_id_buf tmp; + DBG_DEBUG("my id: %s\n", server_id_str_buf(ctx->id, &tmp)); + } + + *pmsg_ctx = talloc_steal(mem_ctx, ctx); + + status = NT_STATUS_OK; +done: + TALLOC_FREE(frame); + + return status; +} + +struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev) +{ + struct messaging_context *ctx = NULL; + NTSTATUS status; + + status = messaging_init_internal(mem_ctx, + ev, + &ctx); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return ctx; +} + +struct server_id messaging_server_id(const struct messaging_context *msg_ctx) +{ + return msg_ctx->id; +} + +/* + * re-init after a fork + */ +NTSTATUS messaging_reinit(struct messaging_context *msg_ctx) +{ + int ret; + char *lck_path; + void *ref; + + TALLOC_FREE(msg_ctx->per_process_talloc_ctx); + + msg_ctx->per_process_talloc_ctx = talloc_new(msg_ctx); + if (msg_ctx->per_process_talloc_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + msg_ctx->id = (struct server_id) { + .pid = tevent_cached_getpid(), .vnn = msg_ctx->id.vnn + }; + + lck_path = lock_path(talloc_tos(), "msg.lock"); + if (lck_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ref = messaging_dgm_ref( + msg_ctx->per_process_talloc_ctx, + msg_ctx->event_ctx, + &msg_ctx->id.unique_id, + private_path("msg.sock"), + lck_path, + messaging_recv_cb, + msg_ctx, + &ret); + + if (ref == NULL) { + DEBUG(2, ("messaging_dgm_ref failed: %s\n", strerror(ret))); + return map_nt_error_from_unix(ret); + } + + if (lp_clustering()) { + ref = messaging_ctdb_ref( + msg_ctx->per_process_talloc_ctx, + msg_ctx->event_ctx, + lp_ctdbd_socket(), + lp_ctdb_timeout(), + msg_ctx->id.unique_id, + messaging_recv_cb, + msg_ctx, + &ret); + if (ref == NULL) { + DBG_NOTICE("messaging_ctdb_ref failed: %s\n", + strerror(ret)); + return map_nt_error_from_unix(ret); + } + } + + server_id_db_reinit(msg_ctx->names_db, msg_ctx->id); + register_msg_pool_usage(msg_ctx->per_process_talloc_ctx, msg_ctx); + + return NT_STATUS_OK; +} + + +/* + * Register a dispatch function for a particular message type. Allow multiple + * registrants +*/ +NTSTATUS messaging_register(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + void (*fn)(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data)) +{ + struct messaging_callback *cb; + + DEBUG(5, ("Registering messaging pointer for type %u - " + "private_data=%p\n", + (unsigned)msg_type, private_data)); + + /* + * Only one callback per type + */ + + for (cb = msg_ctx->callbacks; cb != NULL; cb = cb->next) { + /* we allow a second registration of the same message + type if it has a different private pointer. This is + needed in, for example, the internal notify code, + which creates a new notify context for each tree + connect, and expects to receive messages to each of + them. */ + if (cb->msg_type == msg_type && private_data == cb->private_data) { + DEBUG(5,("Overriding messaging pointer for type %u - private_data=%p\n", + (unsigned)msg_type, private_data)); + cb->fn = fn; + cb->private_data = private_data; + return NT_STATUS_OK; + } + } + + if (!(cb = talloc(msg_ctx, struct messaging_callback))) { + return NT_STATUS_NO_MEMORY; + } + + cb->msg_type = msg_type; + cb->fn = fn; + cb->private_data = private_data; + + DLIST_ADD(msg_ctx->callbacks, cb); + return NT_STATUS_OK; +} + +/* + De-register the function for a particular message type. +*/ +void messaging_deregister(struct messaging_context *ctx, uint32_t msg_type, + void *private_data) +{ + struct messaging_callback *cb, *next; + + for (cb = ctx->callbacks; cb; cb = next) { + next = cb->next; + if ((cb->msg_type == msg_type) + && (cb->private_data == private_data)) { + DEBUG(5,("Deregistering messaging pointer for type %u - private_data=%p\n", + (unsigned)msg_type, private_data)); + DLIST_REMOVE(ctx->callbacks, cb); + TALLOC_FREE(cb); + } + } +} + +/* + Send a message to a particular server +*/ +NTSTATUS messaging_send(struct messaging_context *msg_ctx, + struct server_id server, uint32_t msg_type, + const DATA_BLOB *data) +{ + struct iovec iov = {0}; + + if (data != NULL) { + iov.iov_base = data->data; + iov.iov_len = data->length; + }; + + return messaging_send_iov(msg_ctx, server, msg_type, &iov, 1, NULL, 0); +} + +NTSTATUS messaging_send_buf(struct messaging_context *msg_ctx, + struct server_id server, uint32_t msg_type, + const uint8_t *buf, size_t len) +{ + DATA_BLOB blob = data_blob_const(buf, len); + return messaging_send(msg_ctx, server, msg_type, &blob); +} + +static int messaging_post_self(struct messaging_context *msg_ctx, + struct server_id src, struct server_id dst, + uint32_t msg_type, + const struct iovec *iov, int iovlen, + const int *fds, size_t num_fds) +{ + struct messaging_rec *rec; + bool ok; + + rec = messaging_rec_create( + msg_ctx, src, dst, msg_type, iov, iovlen, fds, num_fds); + if (rec == NULL) { + return ENOMEM; + } + + ok = messaging_alert_event_contexts(msg_ctx); + if (!ok) { + TALLOC_FREE(rec); + return ENOMEM; + } + + DLIST_ADD_END(msg_ctx->posted_msgs, rec); + + return 0; +} + +int messaging_send_iov_from(struct messaging_context *msg_ctx, + struct server_id src, struct server_id dst, + uint32_t msg_type, + const struct iovec *iov, int iovlen, + const int *fds, size_t num_fds) +{ + int ret; + uint8_t hdr[MESSAGE_HDR_LENGTH]; + struct iovec iov2[iovlen+1]; + + if (server_id_is_disconnected(&dst)) { + return EINVAL; + } + + if (num_fds > INT8_MAX) { + return EINVAL; + } + + if (server_id_equal(&dst, &msg_ctx->id)) { + ret = messaging_post_self(msg_ctx, src, dst, msg_type, + iov, iovlen, fds, num_fds); + return ret; + } + + message_hdr_put(hdr, msg_type, src, dst); + iov2[0] = (struct iovec){ .iov_base = hdr, .iov_len = sizeof(hdr) }; + memcpy(&iov2[1], iov, iovlen * sizeof(*iov)); + + if (dst.vnn != msg_ctx->id.vnn) { + if (num_fds > 0) { + return ENOSYS; + } + + ret = messaging_ctdb_send(dst.vnn, dst.pid, iov2, iovlen+1); + return ret; + } + + ret = messaging_dgm_send(dst.pid, iov2, iovlen+1, fds, num_fds); + + if (ret == EACCES) { + become_root(); + ret = messaging_dgm_send(dst.pid, iov2, iovlen+1, + fds, num_fds); + unbecome_root(); + } + + if (ret == ECONNREFUSED) { + /* + * Linux returns this when a socket exists in the file + * system without a listening process. This is not + * documented in susv4 or the linux manpages, but it's + * easily testable. For the higher levels this is the + * same as "destination does not exist" + */ + ret = ENOENT; + } + + return ret; +} + +NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx, + struct server_id dst, uint32_t msg_type, + const struct iovec *iov, int iovlen, + const int *fds, size_t num_fds) +{ + int ret; + + ret = messaging_send_iov_from(msg_ctx, msg_ctx->id, dst, msg_type, + iov, iovlen, fds, num_fds); + if (ret != 0) { + return map_nt_error_from_unix(ret); + } + return NT_STATUS_OK; +} + +struct send_all_state { + struct messaging_context *msg_ctx; + int msg_type; + const void *buf; + size_t len; +}; + +static int send_all_fn(pid_t pid, void *private_data) +{ + struct send_all_state *state = private_data; + NTSTATUS status; + + if (pid == tevent_cached_getpid()) { + DBG_DEBUG("Skip ourselves in messaging_send_all\n"); + return 0; + } + + status = messaging_send_buf(state->msg_ctx, pid_to_procid(pid), + state->msg_type, state->buf, state->len); + if (!NT_STATUS_IS_OK(status)) { + DBG_NOTICE("messaging_send_buf to %ju failed: %s\n", + (uintmax_t)pid, nt_errstr(status)); + } + + return 0; +} + +void messaging_send_all(struct messaging_context *msg_ctx, + int msg_type, const void *buf, size_t len) +{ + struct send_all_state state = { + .msg_ctx = msg_ctx, .msg_type = msg_type, + .buf = buf, .len = len + }; + int ret; + +#ifdef CLUSTER_SUPPORT + if (lp_clustering()) { + struct ctdbd_connection *conn = messaging_ctdb_connection(); + uint8_t msghdr[MESSAGE_HDR_LENGTH]; + struct iovec iov[] = { + { .iov_base = msghdr, + .iov_len = sizeof(msghdr) }, + { .iov_base = discard_const_p(void, buf), + .iov_len = len } + }; + + message_hdr_put(msghdr, msg_type, messaging_server_id(msg_ctx), + (struct server_id) {0}); + + ret = ctdbd_messaging_send_iov( + conn, CTDB_BROADCAST_CONNECTED, + CTDB_SRVID_SAMBA_PROCESS, + iov, ARRAY_SIZE(iov)); + if (ret != 0) { + DBG_WARNING("ctdbd_messaging_send_iov failed: %s\n", + strerror(ret)); + } + + return; + } +#endif + + ret = messaging_dgm_forall(send_all_fn, &state); + if (ret != 0) { + DBG_WARNING("messaging_dgm_forall failed: %s\n", + strerror(ret)); + } +} + +static struct messaging_rec *messaging_rec_dup(TALLOC_CTX *mem_ctx, + struct messaging_rec *rec) +{ + struct messaging_rec *result; + size_t fds_size = sizeof(int64_t) * rec->num_fds; + size_t payload_len; + + payload_len = rec->buf.length + fds_size; + if (payload_len < rec->buf.length) { + /* overflow */ + return NULL; + } + + result = talloc_pooled_object(mem_ctx, struct messaging_rec, 2, + payload_len); + if (result == NULL) { + return NULL; + } + *result = *rec; + + /* Doesn't fail, see talloc_pooled_object */ + + result->buf.data = talloc_memdup(result, rec->buf.data, + rec->buf.length); + + result->fds = NULL; + if (result->num_fds > 0) { + size_t i; + + result->fds = talloc_memdup(result, rec->fds, fds_size); + + for (i=0; i<rec->num_fds; i++) { + /* + * fd's can only exist once + */ + rec->fds[i] = -1; + } + } + + return result; +} + +struct messaging_filtered_read_state { + struct tevent_context *ev; + struct messaging_context *msg_ctx; + struct messaging_dgm_fde *fde; + struct messaging_ctdb_fde *cluster_fde; + + bool (*filter)(struct messaging_rec *rec, void *private_data); + void *private_data; + + struct messaging_rec *rec; +}; + +static void messaging_filtered_read_cleanup(struct tevent_req *req, + enum tevent_req_state req_state); + +struct tevent_req *messaging_filtered_read_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct messaging_context *msg_ctx, + bool (*filter)(struct messaging_rec *rec, void *private_data), + void *private_data) +{ + struct tevent_req *req; + struct messaging_filtered_read_state *state; + size_t new_waiters_len; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct messaging_filtered_read_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->msg_ctx = msg_ctx; + state->filter = filter; + state->private_data = private_data; + + /* + * We have to defer the callback here, as we might be called from + * within a different tevent_context than state->ev + */ + tevent_req_defer_callback(req, state->ev); + + state->fde = messaging_dgm_register_tevent_context(state, ev); + if (tevent_req_nomem(state->fde, req)) { + return tevent_req_post(req, ev); + } + + if (lp_clustering()) { + state->cluster_fde = + messaging_ctdb_register_tevent_context(state, ev); + if (tevent_req_nomem(state->cluster_fde, req)) { + return tevent_req_post(req, ev); + } + } + + /* + * We add ourselves to the "new_waiters" array, not the "waiters" + * array. If we are called from within messaging_read_done, + * messaging_dispatch_rec will be in an active for-loop on + * "waiters". We must be careful not to mess with this array, because + * it could mean that a single event is being delivered twice. + */ + + new_waiters_len = talloc_array_length(msg_ctx->new_waiters); + + if (new_waiters_len == msg_ctx->num_new_waiters) { + struct tevent_req **tmp; + + tmp = talloc_realloc(msg_ctx, msg_ctx->new_waiters, + struct tevent_req *, new_waiters_len+1); + if (tevent_req_nomem(tmp, req)) { + return tevent_req_post(req, ev); + } + msg_ctx->new_waiters = tmp; + } + + msg_ctx->new_waiters[msg_ctx->num_new_waiters] = req; + msg_ctx->num_new_waiters += 1; + tevent_req_set_cleanup_fn(req, messaging_filtered_read_cleanup); + + ok = messaging_register_event_context(msg_ctx, ev); + if (!ok) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + + return req; +} + +static void messaging_filtered_read_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct messaging_filtered_read_state *state = tevent_req_data( + req, struct messaging_filtered_read_state); + struct messaging_context *msg_ctx = state->msg_ctx; + size_t i; + bool ok; + + tevent_req_set_cleanup_fn(req, NULL); + + TALLOC_FREE(state->fde); + TALLOC_FREE(state->cluster_fde); + + ok = messaging_deregister_event_context(msg_ctx, state->ev); + if (!ok) { + abort(); + } + + /* + * Just set the [new_]waiters entry to NULL, be careful not to mess + * with the other "waiters" array contents. We are often called from + * within "messaging_dispatch_rec", which loops over + * "waiters". Messing with the "waiters" array will mess up that + * for-loop. + */ + + for (i=0; i<msg_ctx->num_waiters; i++) { + if (msg_ctx->waiters[i] == req) { + msg_ctx->waiters[i] = NULL; + return; + } + } + + for (i=0; i<msg_ctx->num_new_waiters; i++) { + if (msg_ctx->new_waiters[i] == req) { + msg_ctx->new_waiters[i] = NULL; + return; + } + } +} + +static void messaging_filtered_read_done(struct tevent_req *req, + struct messaging_rec *rec) +{ + struct messaging_filtered_read_state *state = tevent_req_data( + req, struct messaging_filtered_read_state); + + state->rec = messaging_rec_dup(state, rec); + if (tevent_req_nomem(state->rec, req)) { + return; + } + tevent_req_done(req); +} + +int messaging_filtered_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct messaging_rec **presult) +{ + struct messaging_filtered_read_state *state = tevent_req_data( + req, struct messaging_filtered_read_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + tevent_req_received(req); + return err; + } + if (presult != NULL) { + *presult = talloc_move(mem_ctx, &state->rec); + } + tevent_req_received(req); + return 0; +} + +struct messaging_read_state { + uint32_t msg_type; + struct messaging_rec *rec; +}; + +static bool messaging_read_filter(struct messaging_rec *rec, + void *private_data); +static void messaging_read_done(struct tevent_req *subreq); + +struct tevent_req *messaging_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context *msg, + uint32_t msg_type) +{ + struct tevent_req *req, *subreq; + struct messaging_read_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct messaging_read_state); + if (req == NULL) { + return NULL; + } + state->msg_type = msg_type; + + subreq = messaging_filtered_read_send(state, ev, msg, + messaging_read_filter, state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, messaging_read_done, req); + return req; +} + +static bool messaging_read_filter(struct messaging_rec *rec, + void *private_data) +{ + struct messaging_read_state *state = talloc_get_type_abort( + private_data, struct messaging_read_state); + + if (rec->num_fds != 0) { + return false; + } + + return rec->msg_type == state->msg_type; +} + +static void messaging_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct messaging_read_state *state = tevent_req_data( + req, struct messaging_read_state); + int ret; + + ret = messaging_filtered_read_recv(subreq, state, &state->rec); + TALLOC_FREE(subreq); + if (tevent_req_error(req, ret)) { + return; + } + tevent_req_done(req); +} + +int messaging_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct messaging_rec **presult) +{ + struct messaging_read_state *state = tevent_req_data( + req, struct messaging_read_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + return err; + } + if (presult != NULL) { + *presult = talloc_move(mem_ctx, &state->rec); + } + return 0; +} + +static bool messaging_append_new_waiters(struct messaging_context *msg_ctx) +{ + if (msg_ctx->num_new_waiters == 0) { + return true; + } + + if (talloc_array_length(msg_ctx->waiters) < + (msg_ctx->num_waiters + msg_ctx->num_new_waiters)) { + struct tevent_req **tmp; + tmp = talloc_realloc( + msg_ctx, msg_ctx->waiters, struct tevent_req *, + msg_ctx->num_waiters + msg_ctx->num_new_waiters); + if (tmp == NULL) { + DEBUG(1, ("%s: talloc failed\n", __func__)); + return false; + } + msg_ctx->waiters = tmp; + } + + memcpy(&msg_ctx->waiters[msg_ctx->num_waiters], msg_ctx->new_waiters, + sizeof(struct tevent_req *) * msg_ctx->num_new_waiters); + + msg_ctx->num_waiters += msg_ctx->num_new_waiters; + msg_ctx->num_new_waiters = 0; + + return true; +} + +static bool messaging_dispatch_classic(struct messaging_context *msg_ctx, + struct messaging_rec *rec) +{ + struct messaging_callback *cb, *next; + + for (cb = msg_ctx->callbacks; cb != NULL; cb = next) { + size_t j; + + next = cb->next; + if (cb->msg_type != rec->msg_type) { + continue; + } + + /* + * the old style callbacks don't support fd passing + */ + for (j=0; j < rec->num_fds; j++) { + int fd = rec->fds[j]; + close(fd); + } + rec->num_fds = 0; + rec->fds = NULL; + + cb->fn(msg_ctx, cb->private_data, rec->msg_type, + rec->src, &rec->buf); + + return true; + } + + return false; +} + +static bool messaging_dispatch_waiters(struct messaging_context *msg_ctx, + struct tevent_context *ev, + struct messaging_rec *rec) +{ + size_t i; + + if (!messaging_append_new_waiters(msg_ctx)) { + return false; + } + + i = 0; + while (i < msg_ctx->num_waiters) { + struct tevent_req *req; + struct messaging_filtered_read_state *state; + + req = msg_ctx->waiters[i]; + if (req == NULL) { + /* + * This got cleaned up. In the meantime, + * move everything down one. We need + * to keep the order of waiters, as + * other code may depend on this. + */ + ARRAY_DEL_ELEMENT( + msg_ctx->waiters, i, msg_ctx->num_waiters); + msg_ctx->num_waiters -= 1; + continue; + } + + state = tevent_req_data( + req, struct messaging_filtered_read_state); + if ((ev == state->ev) && + state->filter(rec, state->private_data)) { + messaging_filtered_read_done(req, rec); + return true; + } + + i += 1; + } + + return false; +} + +/* + Dispatch one messaging_rec +*/ +static void messaging_dispatch_rec(struct messaging_context *msg_ctx, + struct tevent_context *ev, + struct messaging_rec *rec) +{ + bool consumed; + size_t i; + + if (ev == msg_ctx->event_ctx) { + consumed = messaging_dispatch_classic(msg_ctx, rec); + if (consumed) { + return; + } + } + + consumed = messaging_dispatch_waiters(msg_ctx, ev, rec); + if (consumed) { + return; + } + + if (ev != msg_ctx->event_ctx) { + struct iovec iov; + int fds[MAX(1, rec->num_fds)]; + int ret; + + /* + * We've been listening on a nested event + * context. Messages need to be handled in the main + * event context, so post to ourselves + */ + + iov.iov_base = rec->buf.data; + iov.iov_len = rec->buf.length; + + for (i=0; i<rec->num_fds; i++) { + fds[i] = rec->fds[i]; + } + + ret = messaging_post_self( + msg_ctx, rec->src, rec->dest, rec->msg_type, + &iov, 1, fds, rec->num_fds); + if (ret == 0) { + return; + } + } +} + +static int mess_parent_dgm_cleanup(void *private_data); +static void mess_parent_dgm_cleanup_done(struct tevent_req *req); + +bool messaging_parent_dgm_cleanup_init(struct messaging_context *msg) +{ + struct tevent_req *req; + + req = background_job_send( + msg, msg->event_ctx, msg, NULL, 0, + lp_parm_int(-1, "messaging", "messaging dgm cleanup interval", + 60*15), + mess_parent_dgm_cleanup, msg); + if (req == NULL) { + DBG_WARNING("background_job_send failed\n"); + return false; + } + tevent_req_set_callback(req, mess_parent_dgm_cleanup_done, msg); + return true; +} + +static int mess_parent_dgm_cleanup(void *private_data) +{ + int ret; + + ret = messaging_dgm_wipe(); + DEBUG(10, ("messaging_dgm_wipe returned %s\n", + ret ? strerror(ret) : "ok")); + return lp_parm_int(-1, "messaging", "messaging dgm cleanup interval", + 60*15); +} + +static void mess_parent_dgm_cleanup_done(struct tevent_req *req) +{ + struct messaging_context *msg = tevent_req_callback_data( + req, struct messaging_context); + NTSTATUS status; + + status = background_job_recv(req); + TALLOC_FREE(req); + DEBUG(1, ("messaging dgm cleanup job ended with %s\n", + nt_errstr(status))); + + req = background_job_send( + msg, msg->event_ctx, msg, NULL, 0, + lp_parm_int(-1, "messaging", "messaging dgm cleanup interval", + 60*15), + mess_parent_dgm_cleanup, msg); + if (req == NULL) { + DEBUG(1, ("background_job_send failed\n")); + return; + } + tevent_req_set_callback(req, mess_parent_dgm_cleanup_done, msg); +} + +int messaging_cleanup(struct messaging_context *msg_ctx, pid_t pid) +{ + int ret; + + if (pid == 0) { + ret = messaging_dgm_wipe(); + } else { + ret = messaging_dgm_cleanup(pid); + } + + return ret; +} + +struct tevent_context *messaging_tevent_context( + struct messaging_context *msg_ctx) +{ + return msg_ctx->event_ctx; +} + +struct server_id_db *messaging_names_db(struct messaging_context *msg_ctx) +{ + return msg_ctx->names_db; +} + +/** @} **/ diff --git a/source3/lib/messages_ctdb.c b/source3/lib/messages_ctdb.c new file mode 100644 index 0000000..d55b53b --- /dev/null +++ b/source3/lib/messages_ctdb.c @@ -0,0 +1,306 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2017 by Volker Lendecke + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/messages_ctdb.h" +#include "lib/util/server_id.h" +#include "messages.h" +#include "util_tdb.h" +#include "lib/util/iov_buf.h" +#include "lib/messages_util.h" +#include "ctdbd_conn.h" +#include "lib/cluster_support.h" +#include "ctdb_srvids.h" + +struct messaging_ctdb_context; + +/* + * We can only have one tevent_fd per ctdb_context and per + * tevent_context. Maintain a list of registered tevent_contexts per + * ctdb_context. + */ +struct messaging_ctdb_fde_ev { + struct messaging_ctdb_fde_ev *prev, *next; + + /* + * Backreference to enable DLIST_REMOVE from our + * destructor. Also, set to NULL when the ctdb_context dies + * before the messaging_ctdb_fde_ev. + */ + struct messaging_ctdb_context *ctx; + + struct tevent_context *ev; + struct tevent_fd *fde; +}; + +struct messaging_ctdb_context { + struct ctdbd_connection *conn; + + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data); + void *recv_cb_private_data; + + struct messaging_ctdb_fde_ev *fde_evs; +}; + +static int messaging_ctdb_recv( + struct tevent_context *ev, + uint32_t src_vnn, uint32_t dst_vnn, uint64_t dst_srvid, + const uint8_t *msg, size_t msg_len, void *private_data) +{ + struct messaging_ctdb_context *state = talloc_get_type_abort( + private_data, struct messaging_ctdb_context); + + state->recv_cb(ev, msg, msg_len, NULL, 0, state->recv_cb_private_data); + + return 0; +} + +struct messaging_ctdb_context *global_ctdb_context; + +static int global_ctdb_ctx_destructor(struct messaging_ctdb_context *ctx) +{ + if (ctx != NULL) { + struct messaging_ctdb_fde_ev *fde_ev = NULL; + for (fde_ev = ctx->fde_evs; + fde_ev != NULL; + fde_ev = fde_ev->next) { + if (fde_ev->ctx == ctx) { + fde_ev->ctx = NULL; + } + } + } + return 0; +} + +int messaging_ctdb_init(const char *sockname, int timeout, uint64_t unique_id, + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data), + void *private_data) +{ + struct messaging_ctdb_context *ctx; + int ret; + + if (global_ctdb_context != NULL) { + return EBUSY; + } + + ctx = talloc_zero(NULL, struct messaging_ctdb_context); + if (ctx == NULL) { + return ENOMEM; + } + + talloc_set_destructor(ctx, + global_ctdb_ctx_destructor); + + ctx->recv_cb = recv_cb; + ctx->recv_cb_private_data = private_data; + + ret = ctdbd_init_connection(ctx, sockname, timeout, &ctx->conn); + if (ret != 0) { + DBG_DEBUG("ctdbd_init_connection returned %s\n", + strerror(ret)); + goto fail; + } + + ret = register_with_ctdbd(ctx->conn, tevent_cached_getpid(), messaging_ctdb_recv, + ctx); + if (ret != 0) { + DBG_DEBUG("register_with_ctdbd returned %s (%d)\n", + strerror(ret), ret); + goto fail; + } + + ret = register_with_ctdbd(ctx->conn, CTDB_SRVID_SAMBA_PROCESS, + messaging_ctdb_recv, ctx); + if (ret != 0) { + DBG_DEBUG("register_with_ctdbd returned %s (%d)\n", + strerror(ret), ret); + goto fail; + } + + ret = register_with_ctdbd(ctx->conn, unique_id, NULL, NULL); + if (ret != 0) { + DBG_DEBUG("register_with_ctdbd returned %s (%d)\n", + strerror(ret), ret); + goto fail; + } + + set_my_vnn(ctdbd_vnn(ctx->conn)); + + global_ctdb_context = ctx; + return 0; +fail: + TALLOC_FREE(ctx); + return ret; +} + +void messaging_ctdb_destroy(void) +{ + TALLOC_FREE(global_ctdb_context); +} + +int messaging_ctdb_send(uint32_t dst_vnn, uint64_t dst_srvid, + const struct iovec *iov, int iovlen) +{ + struct messaging_ctdb_context *ctx = global_ctdb_context; + int ret; + + if (ctx == NULL) { + return ENOTCONN; + } + + ret = ctdbd_messaging_send_iov(ctx->conn, dst_vnn, dst_srvid, + iov, iovlen); + return ret; +} + +static void messaging_ctdb_read_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct messaging_ctdb_context *ctx = talloc_get_type_abort( + private_data, struct messaging_ctdb_context); + + if ((flags & TEVENT_FD_READ) == 0) { + return; + } + ctdbd_socket_readable(ev, ctx->conn); +} + +struct messaging_ctdb_fde { + struct tevent_fd *fde; +}; + +static int messaging_ctdb_fde_ev_destructor( + struct messaging_ctdb_fde_ev *fde_ev) +{ + if (fde_ev->ctx != NULL) { + DLIST_REMOVE(fde_ev->ctx->fde_evs, fde_ev); + fde_ev->ctx = NULL; + } + return 0; +} + +/* + * Reference counter for a struct tevent_fd messaging read event + * (with callback function) on a struct tevent_context registered + * on a messaging context. + * + * If we've already registered this struct tevent_context before + * (so already have a read event), just increase the reference count. + * + * Otherwise create a new struct tevent_fd messaging read event on the + * previously unseen struct tevent_context - this is what drives + * the message receive processing. + * + */ + +struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context( + TALLOC_CTX *mem_ctx, struct tevent_context *ev) +{ + struct messaging_ctdb_context *ctx = global_ctdb_context; + struct messaging_ctdb_fde_ev *fde_ev; + struct messaging_ctdb_fde *fde; + + if (ctx == NULL) { + return NULL; + } + + fde = talloc(mem_ctx, struct messaging_ctdb_fde); + if (fde == NULL) { + return NULL; + } + + for (fde_ev = ctx->fde_evs; fde_ev != NULL; fde_ev = fde_ev->next) { + if (tevent_fd_get_flags(fde_ev->fde) == 0) { + /* + * If the event context got deleted, + * tevent_fd_get_flags() will return 0 + * for the stale fde. + * + * In that case we should not + * use fde_ev->ev anymore. + */ + continue; + } + if (fde_ev->ev == ev) { + break; + } + } + + if (fde_ev == NULL) { + int sock = ctdbd_conn_get_fd(ctx->conn); + + fde_ev = talloc(fde, struct messaging_ctdb_fde_ev); + if (fde_ev == NULL) { + return NULL; + } + fde_ev->fde = tevent_add_fd( + ev, fde_ev, sock, TEVENT_FD_READ, + messaging_ctdb_read_handler, ctx); + if (fde_ev->fde == NULL) { + TALLOC_FREE(fde); + return NULL; + } + fde_ev->ev = ev; + fde_ev->ctx = ctx; + DLIST_ADD(ctx->fde_evs, fde_ev); + talloc_set_destructor( + fde_ev, messaging_ctdb_fde_ev_destructor); + } else { + /* + * Same trick as with tdb_wrap: The caller will never + * see the talloc_referenced object, the + * messaging_ctdb_fde_ev, so problems with + * talloc_unlink will not happen. + */ + if (talloc_reference(fde, fde_ev) == NULL) { + TALLOC_FREE(fde); + return NULL; + } + } + + fde->fde = fde_ev->fde; + return fde; +} + +bool messaging_ctdb_fde_active(struct messaging_ctdb_fde *fde) +{ + uint16_t flags; + + if (fde == NULL) { + return false; + } + flags = tevent_fd_get_flags(fde->fde); + return (flags != 0); +} + +struct ctdbd_connection *messaging_ctdb_connection(void) +{ + if (global_ctdb_context == NULL) { + smb_panic("messaging not initialized\n"); + } + return global_ctdb_context->conn; +} diff --git a/source3/lib/messages_ctdb.h b/source3/lib/messages_ctdb.h new file mode 100644 index 0000000..9d56343 --- /dev/null +++ b/source3/lib/messages_ctdb.h @@ -0,0 +1,44 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2017 by Volker Lendecke + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __MESSAGES_CTDB_H__ +#define __MESSAGES_CTDB_H__ + +#include "replace.h" +#include "system/filesys.h" +#include <tevent.h> + +int messaging_ctdb_init(const char *sockname, int timeout, uint64_t unique_id, + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data), + void *private_data); +void messaging_ctdb_destroy(void); +int messaging_ctdb_send(uint32_t dst_vnn, uint64_t dst_srvid, + const struct iovec *iov, int iovlen); + +struct messaging_ctdb_fde; +struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context( + TALLOC_CTX *mem_ctx, struct tevent_context *ev); +bool messaging_ctdb_fde_active(struct messaging_ctdb_fde *fde); + +struct ctdbd_connection *messaging_ctdb_connection(void); + +#endif diff --git a/source3/lib/messages_ctdb_ref.c b/source3/lib/messages_ctdb_ref.c new file mode 100644 index 0000000..ed6285f --- /dev/null +++ b/source3/lib/messages_ctdb_ref.c @@ -0,0 +1,145 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2017 by Volker Lendecke + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include <talloc.h> +#include "messages_ctdb.h" +#include "messages_ctdb_ref.h" +#include "lib/util/debug.h" +#include "lib/util/dlinklist.h" + +struct msg_ctdb_ref { + struct msg_ctdb_ref *prev, *next; + struct messaging_ctdb_fde *fde; + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, void *private_data); + void *recv_cb_private_data; +}; + +static pid_t ctdb_pid = 0; +static struct msg_ctdb_ref *refs = NULL; + +static int msg_ctdb_ref_destructor(struct msg_ctdb_ref *r); +static void msg_ctdb_ref_recv(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, void *private_data); + +void *messaging_ctdb_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *sockname, int timeout, uint64_t unique_id, + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data), + void *recv_cb_private_data, + int *err) +{ + struct msg_ctdb_ref *result, *tmp_refs; + + result = talloc(mem_ctx, struct msg_ctdb_ref); + if (result == NULL) { + *err = ENOMEM; + return NULL; + } + result->fde = NULL; + + tmp_refs = refs; + + if ((refs != NULL) && (ctdb_pid != tevent_cached_getpid())) { + /* + * Have to reinit after fork + */ + messaging_ctdb_destroy(); + refs = NULL; + } + + if (refs == NULL) { + int ret; + + ret = messaging_ctdb_init(sockname, timeout, unique_id, + msg_ctdb_ref_recv, NULL); + DBG_DEBUG("messaging_ctdb_init returned %s\n", strerror(ret)); + if (ret != 0) { + DEBUG(10, ("messaging_ctdb_init failed: %s\n", + strerror(ret))); + TALLOC_FREE(result); + *err = ret; + return NULL; + } + ctdb_pid = tevent_cached_getpid(); + } + + result->fde = messaging_ctdb_register_tevent_context(result, ev); + if (result->fde == NULL) { + TALLOC_FREE(result); + *err = ENOMEM; + return NULL; + } + + refs = tmp_refs; + + result->recv_cb = recv_cb; + result->recv_cb_private_data = recv_cb_private_data; + DLIST_ADD(refs, result); + talloc_set_destructor(result, msg_ctdb_ref_destructor); + + return result; +} + +static void msg_ctdb_ref_recv(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, void *private_data) +{ + struct msg_ctdb_ref *r, *next; + + for (r = refs; r != NULL; r = next) { + bool active; + + next = r->next; + + active = messaging_ctdb_fde_active(r->fde); + if (!active) { + /* + * r's tevent_context has died. + */ + continue; + } + + r->recv_cb(ev, msg, msg_len, fds, num_fds, + r->recv_cb_private_data); + break; + } +} + +static int msg_ctdb_ref_destructor(struct msg_ctdb_ref *r) +{ + if (refs == NULL) { + abort(); + } + DLIST_REMOVE(refs, r); + + TALLOC_FREE(r->fde); + + DBG_DEBUG("refs=%p\n", refs); + + if (refs == NULL) { + messaging_ctdb_destroy(); + } + return 0; +} diff --git a/source3/lib/messages_ctdb_ref.h b/source3/lib/messages_ctdb_ref.h new file mode 100644 index 0000000..44541d8 --- /dev/null +++ b/source3/lib/messages_ctdb_ref.h @@ -0,0 +1,35 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2017 by Volker Lendecke + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __MESSAGES_CTDB_REF_H__ +#define __MESSAGES_CTDB_REF_H__ + +#include "replace.h" +#include <tevent.h> + +void *messaging_ctdb_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *sockname, int timeout, uint64_t unique_id, + void (*recv_cb)(struct tevent_context *ev, + const uint8_t *msg, size_t msg_len, + int *fds, size_t num_fds, + void *private_data), + void *private_data, + int *err); + +#endif diff --git a/source3/lib/messages_util.c b/source3/lib/messages_util.c new file mode 100644 index 0000000..d712dfe --- /dev/null +++ b/source3/lib/messages_util.c @@ -0,0 +1,42 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2013 by Volker Lendecke + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "lib/util/server_id.h" +#include "lib/util/samba_util.h" +#include "librpc/gen_ndr/server_id.h" +#include "lib/util/byteorder.h" +#include "messages_util.h" + +void message_hdr_put(uint8_t buf[MESSAGE_HDR_LENGTH], uint32_t msg_type, + struct server_id src, struct server_id dst) +{ + server_id_put(buf, dst); + server_id_put(buf + SERVER_ID_BUF_LENGTH, src); + SIVAL(buf, 2 * SERVER_ID_BUF_LENGTH, msg_type); +} + +void message_hdr_get(uint32_t *msg_type, struct server_id *src, + struct server_id *dst, + const uint8_t buf[MESSAGE_HDR_LENGTH]) +{ + server_id_get(dst, buf); + server_id_get(src, buf + SERVER_ID_BUF_LENGTH); + *msg_type = IVAL(buf, 2 * SERVER_ID_BUF_LENGTH); +} diff --git a/source3/lib/messages_util.h b/source3/lib/messages_util.h new file mode 100644 index 0000000..5b22f5e --- /dev/null +++ b/source3/lib/messages_util.h @@ -0,0 +1,33 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2013 by Volker Lendecke + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _MESSAGES_UTIL_H_ +#define _MESSAGES_UTIL_H_ + +struct message_hdr; + +#define MESSAGE_HDR_LENGTH 52 + +void message_hdr_put(uint8_t buf[MESSAGE_HDR_LENGTH], uint32_t msg_type, + struct server_id src, struct server_id dst); +void message_hdr_get(uint32_t *msg_type, struct server_id *src, + struct server_id *dst, + const uint8_t buf[MESSAGE_HDR_LENGTH]); + +#endif diff --git a/source3/lib/ms_fnmatch.c b/source3/lib/ms_fnmatch.c new file mode 100644 index 0000000..4fbed58 --- /dev/null +++ b/source3/lib/ms_fnmatch.c @@ -0,0 +1,232 @@ +/* + Unix SMB/CIFS implementation. + filename matching routine + Copyright (C) Andrew Tridgell 1992-2004 + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + This module was originally based on fnmatch.c copyright by the Free + Software Foundation. It bears little (if any) resemblance to that + code now +*/ + + +#include "includes.h" + +static int null_match(const smb_ucs2_t *p) +{ + for (;*p;p++) { + if (*p != UCS2_CHAR('*') && + *p != UCS2_CHAR('<') && + *p != UCS2_CHAR('"') && + *p != UCS2_CHAR('>')) return -1; + } + return 0; +} + +/* + the max_n structure is purely for efficiency, it doesn't contribute + to the matching algorithm except by ensuring that the algorithm does + not grow exponentially +*/ +struct max_n { + const smb_ucs2_t *predot; + const smb_ucs2_t *postdot; +}; + + +/* + p and n are the pattern and string being matched. The max_n array is + an optimisation only. The ldot pointer is NULL if the string does + not contain a '.', otherwise it points at the last dot in 'n'. +*/ +static int ms_fnmatch_core(const smb_ucs2_t *p, const smb_ucs2_t *n, + struct max_n *max_n, const smb_ucs2_t *ldot, + bool is_case_sensitive) +{ + smb_ucs2_t c; + int i; + + while ((c = *p++)) { + switch (c) { + /* a '*' matches zero or more characters of any type */ + case UCS2_CHAR('*'): + if (max_n->predot && max_n->predot <= n) { + return null_match(p); + } + for (i=0; n[i]; i++) { + if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) { + return 0; + } + } + if (!max_n->predot || max_n->predot > n) max_n->predot = n; + return null_match(p); + + /* a '<' matches zero or more characters of + any type, but stops matching at the last + '.' in the string. */ + case UCS2_CHAR('<'): + if (max_n->predot && max_n->predot <= n) { + return null_match(p); + } + if (max_n->postdot && max_n->postdot <= n && n <= ldot) { + return -1; + } + for (i=0; n[i]; i++) { + if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0; + if (n+i == ldot) { + if (ms_fnmatch_core(p, n+i+1, max_n+1, ldot, is_case_sensitive) == 0) return 0; + if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n; + return -1; + } + } + if (!max_n->predot || max_n->predot > n) max_n->predot = n; + return null_match(p); + + /* a '?' matches any single character */ + case UCS2_CHAR('?'): + if (! *n) { + return -1; + } + n++; + break; + + /* a '?' matches any single character */ + case UCS2_CHAR('>'): + if (n[0] == UCS2_CHAR('.')) { + if (! n[1] && null_match(p) == 0) { + return 0; + } + break; + } + if (! *n) return null_match(p); + n++; + break; + + case UCS2_CHAR('"'): + if (*n == 0 && null_match(p) == 0) { + return 0; + } + if (*n != UCS2_CHAR('.')) return -1; + n++; + break; + + default: + if (c != *n) { + if (is_case_sensitive) { + return -1; + } + if (toupper_w(c) != toupper_w(*n)) { + return -1; + } + } + n++; + break; + } + } + + if (! *n) { + return 0; + } + + return -1; +} + +int ms_fnmatch(const char *pattern, const char *string, bool translate_pattern, + bool is_case_sensitive) +{ + smb_ucs2_t *p = NULL; + smb_ucs2_t *s = NULL; + int ret; + size_t count, i; + struct max_n *max_n = NULL; + struct max_n *max_n_free = NULL; + struct max_n one_max_n; + size_t converted_size; + + if (ISDOTDOT(string)) { + string = "."; + } + + if (strpbrk(pattern, "<>*?\"") == NULL) { + /* this is not just an optimisation - it is essential + for LANMAN1 correctness */ + if (is_case_sensitive) { + return strcmp(pattern, string); + } else { + return strcasecmp_m(pattern, string); + } + } + + if (!push_ucs2_talloc(talloc_tos(), &p, pattern, &converted_size)) { + return -1; + } + + if (!push_ucs2_talloc(talloc_tos(), &s, string, &converted_size)) { + TALLOC_FREE(p); + return -1; + } + + if (translate_pattern) { + /* + for older negotiated protocols it is possible to + translate the pattern to produce a "new style" + pattern that exactly matches w2k behaviour + */ + for (i=0;p[i];i++) { + if (p[i] == UCS2_CHAR('?')) { + p[i] = UCS2_CHAR('>'); + } else if (p[i] == UCS2_CHAR('.') && + (p[i+1] == UCS2_CHAR('?') || + p[i+1] == UCS2_CHAR('*') || + p[i+1] == 0)) { + p[i] = UCS2_CHAR('"'); + } else if (p[i] == UCS2_CHAR('*') && p[i+1] == UCS2_CHAR('.')) { + p[i] = UCS2_CHAR('<'); + } + } + } + + for (count=i=0;p[i];i++) { + if (p[i] == UCS2_CHAR('*') || p[i] == UCS2_CHAR('<')) count++; + } + + if (count != 0) { + if (count == 1) { + /* + * We're doing this a LOT, so save the effort to allocate + */ + ZERO_STRUCT(one_max_n); + max_n = &one_max_n; + } + else { + max_n = SMB_CALLOC_ARRAY(struct max_n, count); + if (!max_n) { + TALLOC_FREE(p); + TALLOC_FREE(s); + return -1; + } + max_n_free = max_n; + } + } + + ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.')), is_case_sensitive); + + SAFE_FREE(max_n_free); + TALLOC_FREE(p); + TALLOC_FREE(s); + return ret; +} diff --git a/source3/lib/namearray.c b/source3/lib/namearray.c new file mode 100644 index 0000000..e5c3bd9 --- /dev/null +++ b/source3/lib/namearray.c @@ -0,0 +1,39 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2007 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + Copyright (C) James Peach 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +/**************************************************************************** + Routine to free a namearray. +****************************************************************************/ + +void free_namearray(name_compare_entry *name_array) +{ + int i; + + if(name_array == NULL) + return; + + for(i=0; name_array[i].name!=NULL; i++) + SAFE_FREE(name_array[i].name); + SAFE_FREE(name_array); +} diff --git a/source3/lib/namemap_cache.c b/source3/lib/namemap_cache.c new file mode 100644 index 0000000..2e8d298 --- /dev/null +++ b/source3/lib/namemap_cache.c @@ -0,0 +1,333 @@ +/* + * Unix SMB/CIFS implementation. + * Utils for caching sid2name and name2sid + * Copyright (C) Volker Lendecke 2017 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "namemap_cache.h" +#include "source3/lib/gencache.h" +#include "lib/util/debug.h" +#include "lib/util/strv.h" +#include "lib/util/util.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/charset/charset.h" +#include "libcli/security/dom_sid.h" +#include "lib/util/smb_strtox.h" + +bool namemap_cache_set_sid2name(const struct dom_sid *sid, + const char *domain, const char *name, + enum lsa_SidType type, time_t timeout) +{ + char typebuf[16]; + struct dom_sid_buf sidbuf; + char keybuf[sizeof(sidbuf.buf)+10]; + char *val = NULL; + DATA_BLOB data; + int ret; + bool ok = false; + + if ((sid == NULL) || is_null_sid(sid)) { + return true; + } + if (domain == NULL) { + domain = ""; + } + if (name == NULL) { + name = ""; + } + if (type == SID_NAME_UNKNOWN) { + domain = ""; + name = ""; + } + + snprintf(typebuf, sizeof(typebuf), "%d", (int)type); + + ret = strv_add(talloc_tos(), &val, domain); + if (ret != 0) { + DBG_DEBUG("strv_add failed: %s\n", strerror(ret)); + goto fail; + } + ret = strv_add(NULL, &val, name); + if (ret != 0) { + DBG_DEBUG("strv_add failed: %s\n", strerror(ret)); + goto fail; + } + ret = strv_add(NULL, &val, typebuf); + if (ret != 0) { + DBG_DEBUG("strv_add failed: %s\n", strerror(ret)); + goto fail; + } + + dom_sid_str_buf(sid, &sidbuf); + snprintf(keybuf, sizeof(keybuf), "SID2NAME/%s", sidbuf.buf); + + data = data_blob_const(val, talloc_get_size(val)); + + ok = gencache_set_data_blob(keybuf, data, timeout); + if (!ok) { + DBG_DEBUG("gencache_set_data_blob failed\n"); + } +fail: + TALLOC_FREE(val); + return ok; +} + +struct namemap_cache_find_sid_state { + void (*fn)(const char *domain, + const char *name, + enum lsa_SidType type, + bool expired, + void *private_data); + void *private_data; + bool ok; +}; + +static void namemap_cache_find_sid_parser( + const struct gencache_timeout *timeout, + DATA_BLOB blob, + void *private_data) +{ + struct namemap_cache_find_sid_state *state = private_data; + const char *strv = (const char *)blob.data; + size_t strv_len = blob.length; + const char *domain; + const char *name; + const char *typebuf; + int error = 0; + unsigned long type; + + state->ok = false; + + domain = strv_len_next(strv, strv_len, NULL); + if (domain == NULL) { + return; + } + name = strv_len_next(strv, strv_len, domain); + if (name == NULL) { + return; + } + typebuf = strv_len_next(strv, strv_len, name); + if (typebuf == NULL) { + return; + } + + type = smb_strtoul(typebuf, NULL, 10, &error, SMB_STR_FULL_STR_CONV); + if (error != 0) { + return; + } + + state->fn(domain, + name, + (enum lsa_SidType)type, + gencache_timeout_expired(timeout), + state->private_data); + + state->ok = true; +} + +bool namemap_cache_find_sid(const struct dom_sid *sid, + void (*fn)(const char *domain, + const char *name, + enum lsa_SidType type, + bool expired, + void *private_data), + void *private_data) +{ + struct namemap_cache_find_sid_state state = { + .fn = fn, .private_data = private_data + }; + struct dom_sid_buf sidbuf; + char keybuf[sizeof(sidbuf.buf)+10]; + bool ok; + + dom_sid_str_buf(sid, &sidbuf); + snprintf(keybuf, sizeof(keybuf), "SID2NAME/%s", sidbuf.buf); + + ok = gencache_parse(keybuf, namemap_cache_find_sid_parser, &state); + if (!ok) { + DBG_DEBUG("gencache_parse(%s) failed\n", keybuf); + return false; + } + + if (!state.ok) { + DBG_DEBUG("Could not parse %s, deleting\n", keybuf); + gencache_del(keybuf); + return false; + } + + return true; +} + +bool namemap_cache_set_name2sid(const char *domain, const char *name, + const struct dom_sid *sid, + enum lsa_SidType type, + time_t timeout) +{ + char typebuf[16]; + struct dom_sid_buf sidbuf = {{0}}; + char *key; + char *key_upper; + char *val = NULL; + DATA_BLOB data; + int ret; + bool ok = false; + + if (domain == NULL) { + domain = ""; + } + if (name == NULL) { + name = ""; + } + if (type != SID_NAME_UNKNOWN) { + dom_sid_str_buf(sid, &sidbuf); + } + + snprintf(typebuf, sizeof(typebuf), "%d", (int)type); + + key = talloc_asprintf(talloc_tos(), "NAME2SID/%s\\%s", domain, name); + if (key == NULL) { + DBG_DEBUG("talloc_asprintf failed\n"); + goto fail; + } + key_upper = strupper_talloc(key, key); + if (key_upper == NULL) { + DBG_DEBUG("strupper_talloc failed\n"); + goto fail; + } + + ret = strv_add(key, &val, sidbuf.buf); + if (ret != 0) { + DBG_DEBUG("strv_add failed: %s\n", strerror(ret)); + goto fail; + } + ret = strv_add(NULL, &val, typebuf); + if (ret != 0) { + DBG_DEBUG("strv_add failed: %s\n", strerror(ret)); + goto fail; + } + + data = data_blob_const(val, talloc_get_size(val)); + + ok = gencache_set_data_blob(key_upper, data, timeout); + if (!ok) { + DBG_DEBUG("gencache_set_data_blob failed\n"); + } +fail: + TALLOC_FREE(key); + return ok; +} + +struct namemap_cache_find_name_state { + void (*fn)(const struct dom_sid *sid, + enum lsa_SidType type, + bool expired, + void *private_data); + void *private_data; + bool ok; +}; + +static void namemap_cache_find_name_parser( + const struct gencache_timeout *timeout, + DATA_BLOB blob, + void *private_data) +{ + struct namemap_cache_find_name_state *state = private_data; + const char *strv = (const char *)blob.data; + size_t strv_len = blob.length; + const char *sidbuf; + const char *sid_endptr; + const char *typebuf; + int error = 0; + struct dom_sid sid; + unsigned long type; + bool ok; + + state->ok = false; + + sidbuf = strv_len_next(strv, strv_len, NULL); + if (sidbuf == NULL) { + return; + } + typebuf = strv_len_next(strv, strv_len, sidbuf); + if (typebuf == NULL) { + return; + } + + ok = dom_sid_parse_endp(sidbuf, &sid, &sid_endptr); + if (!ok) { + return; + } + if (*sid_endptr != '\0') { + return; + } + + type = smb_strtoul(typebuf, NULL, 10, &error, SMB_STR_FULL_STR_CONV); + if (error != 0) { + return; + } + + state->fn(&sid, + (enum lsa_SidType)type, + gencache_timeout_expired(timeout), + state->private_data); + + state->ok = true; +} + +bool namemap_cache_find_name(const char *domain, + const char *name, + void (*fn)(const struct dom_sid *sid, + enum lsa_SidType type, + bool expired, + void *private_data), + void *private_data) +{ + struct namemap_cache_find_name_state state = { + .fn = fn, .private_data = private_data + }; + char *key; + char *key_upper; + bool ret = false; + bool ok; + + key = talloc_asprintf(talloc_tos(), "NAME2SID/%s\\%s", domain, name); + if (key == NULL) { + DBG_DEBUG("talloc_asprintf failed\n"); + return false; + } + key_upper = strupper_talloc(key, key); + if (key_upper == NULL) { + DBG_DEBUG("strupper_talloc failed\n"); + goto fail; + } + + ok = gencache_parse(key_upper, namemap_cache_find_name_parser, &state); + if (!ok) { + DBG_DEBUG("gencache_parse(%s) failed\n", key_upper); + goto fail; + } + + if (!state.ok) { + DBG_DEBUG("Could not parse %s, deleting\n", key_upper); + goto fail; + } + + ret = true; +fail: + TALLOC_FREE(key); + return ret; +} diff --git a/source3/lib/namemap_cache.h b/source3/lib/namemap_cache.h new file mode 100644 index 0000000..5de8ce4 --- /dev/null +++ b/source3/lib/namemap_cache.h @@ -0,0 +1,49 @@ +/* + * Unix SMB/CIFS implementation. + * Utils for caching sid2name and name2sid + * Copyright (C) Volker Lendecke 2017 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_NAMEMAP_CACHE_H__ +#define __LIB_NAMEMAP_CACHE_H__ + +#include "lib/util/time.h" +#include "lib/util/data_blob.h" +#include "librpc/gen_ndr/lsa.h" + +bool namemap_cache_set_sid2name(const struct dom_sid *sid, + const char *domain, const char *name, + enum lsa_SidType type, time_t timeout); +bool namemap_cache_set_name2sid(const char *domain, const char *name, + const struct dom_sid *sid, + enum lsa_SidType type, + time_t timeout); +bool namemap_cache_find_sid(const struct dom_sid *sid, + void (*fn)(const char *domain, + const char *name, + enum lsa_SidType type, + bool expired, + void *private_data), + void *private_data); +bool namemap_cache_find_name(const char *domain, + const char *name, + void (*fn)(const struct dom_sid *sid, + enum lsa_SidType type, + bool expired, + void *private_data), + void *private_data); + +#endif diff --git a/source3/lib/netapi/Doxyfile b/source3/lib/netapi/Doxyfile new file mode 100644 index 0000000..44bf78b --- /dev/null +++ b/source3/lib/netapi/Doxyfile @@ -0,0 +1,1362 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Samba + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 3.2.0pre3 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = dox + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = $(PWD)/ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = NO + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = netapi.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.c \ + *.h \ + *.idl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = include/includes.h \ + include/proto.h \ + libnetapi.c \ + libnetapi.h \ + netapi.c + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 1 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = . + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 3 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = NO + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/source3/lib/netapi/cm.c b/source3/lib/netapi/cm.c new file mode 100644 index 0000000..c54f955 --- /dev/null +++ b/source3/lib/netapi/cm.c @@ -0,0 +1,284 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "libsmb/libsmb.h" +#include "rpc_client/cli_pipe.h" +#include "../libcli/smb/smbXcli_base.h" + +/******************************************************************** +********************************************************************/ + +struct client_ipc_connection { + struct client_ipc_connection *prev, *next; + struct cli_state *cli; + struct client_pipe_connection *pipe_connections; +}; + +struct client_pipe_connection { + struct client_pipe_connection *prev, *next; + struct rpc_pipe_client *pipe; +}; + +/******************************************************************** +********************************************************************/ + +static struct client_ipc_connection *ipc_cm_find( + struct libnetapi_private_ctx *priv_ctx, const char *server_name) +{ + struct client_ipc_connection *p; + + for (p = priv_ctx->ipc_connections; p; p = p->next) { + const char *remote_name = smbXcli_conn_remote_name(p->cli->conn); + + if (strequal(remote_name, server_name)) { + return p; + } + } + + return NULL; +} + +/******************************************************************** +********************************************************************/ + +static WERROR libnetapi_open_ipc_connection(struct libnetapi_ctx *ctx, + const char *server_name, + struct client_ipc_connection **pp) +{ + struct libnetapi_private_ctx *priv_ctx; + struct cli_state *cli_ipc = NULL; + struct client_ipc_connection *p; + NTSTATUS status; + const char *username = NULL; + const char *password = NULL; + NET_API_STATUS rc; + enum credentials_use_kerberos krb5_state; + + if (!ctx || !pp || !server_name) { + return WERR_INVALID_PARAMETER; + } + + priv_ctx = (struct libnetapi_private_ctx *)ctx->private_data; + + p = ipc_cm_find(priv_ctx, server_name); + if (p) { + *pp = p; + return WERR_OK; + } + + rc = libnetapi_get_username(ctx, &username); + if (rc != 0) { + return WERR_INTERNAL_ERROR; + } + + rc = libnetapi_get_password(ctx, &password); + if (rc != 0) { + return WERR_INTERNAL_ERROR; + } + + if (password == NULL) { + cli_credentials_set_cmdline_callbacks(ctx->creds); + } + + krb5_state = cli_credentials_get_kerberos_state(ctx->creds); + + if (username != NULL && username[0] != '\0' && + password != NULL && password[0] != '\0' && + krb5_state == CRED_USE_KERBEROS_REQUIRED) { + cli_credentials_set_kerberos_state(ctx->creds, + CRED_USE_KERBEROS_DESIRED, + CRED_SPECIFIED); + } + + status = cli_cm_open(ctx, NULL, + server_name, "IPC$", + ctx->creds, + NULL, 0, 0x20, &cli_ipc); + if (!NT_STATUS_IS_OK(status)) { + cli_ipc = NULL; + } + + if (!cli_ipc) { + libnetapi_set_error_string(ctx, + "Failed to connect to IPC$ share on %s", server_name); + return WERR_CAN_NOT_COMPLETE; + } + + p = talloc_zero(ctx, struct client_ipc_connection); + if (p == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + p->cli = cli_ipc; + DLIST_ADD(priv_ctx->ipc_connections, p); + + *pp = p; + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +WERROR libnetapi_shutdown_cm(struct libnetapi_ctx *ctx) +{ + struct libnetapi_private_ctx *priv_ctx = + (struct libnetapi_private_ctx *)ctx->private_data; + struct client_ipc_connection *p; + + for (p = priv_ctx->ipc_connections; p; p = p->next) { + cli_shutdown(p->cli); + } + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS pipe_cm_find(struct client_ipc_connection *ipc, + const struct ndr_interface_table *table, + struct rpc_pipe_client **presult) +{ + struct client_pipe_connection *p; + + for (p = ipc->pipe_connections; p; p = p->next) { + const char *ipc_remote_name; + + if (!rpccli_is_connected(p->pipe)) { + return NT_STATUS_PIPE_EMPTY; + } + + ipc_remote_name = smbXcli_conn_remote_name(ipc->cli->conn); + + if (strequal(ipc_remote_name, p->pipe->desthost) + && ndr_syntax_id_equal(&p->pipe->abstract_syntax, + &table->syntax_id)) { + *presult = p->pipe; + return NT_STATUS_OK; + } + } + + return NT_STATUS_PIPE_NOT_AVAILABLE; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS pipe_cm_connect(TALLOC_CTX *mem_ctx, + struct client_ipc_connection *ipc, + const struct ndr_interface_table *table, + struct rpc_pipe_client **presult) +{ + struct client_pipe_connection *p; + NTSTATUS status; + + p = talloc_zero_array(mem_ctx, struct client_pipe_connection, 1); + if (!p) { + return NT_STATUS_NO_MEMORY; + } + + status = cli_rpc_pipe_open_noauth(ipc->cli, table, &p->pipe); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(p); + return status; + } + + DLIST_ADD(ipc->pipe_connections, p); + + *presult = p->pipe; + return NT_STATUS_OK; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS pipe_cm_open(TALLOC_CTX *ctx, + struct client_ipc_connection *ipc, + const struct ndr_interface_table *table, + struct rpc_pipe_client **presult) +{ + if (NT_STATUS_IS_OK(pipe_cm_find(ipc, table, presult))) { + return NT_STATUS_OK; + } + + return pipe_cm_connect(ctx, ipc, table, presult); +} + +/******************************************************************** +********************************************************************/ + +WERROR libnetapi_open_pipe(struct libnetapi_ctx *ctx, + const char *server_name, + const struct ndr_interface_table *table, + struct rpc_pipe_client **presult) +{ + struct rpc_pipe_client *result = NULL; + NTSTATUS status; + WERROR werr; + struct client_ipc_connection *ipc = NULL; + + if (!presult) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_open_ipc_connection(ctx, server_name, &ipc); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + status = pipe_cm_open(ctx, ipc, table, &result); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(ctx, "failed to open PIPE %s: %s", + table->name, + get_friendly_nt_error_msg(status)); + return WERR_NERR_DESTNOTFOUND; + } + + *presult = result; + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +WERROR libnetapi_get_binding_handle(struct libnetapi_ctx *ctx, + const char *server_name, + const struct ndr_interface_table *table, + struct dcerpc_binding_handle **binding_handle) +{ + struct rpc_pipe_client *pipe_cli; + WERROR result; + + *binding_handle = NULL; + + result = libnetapi_open_pipe(ctx, server_name, table, &pipe_cli); + if (!W_ERROR_IS_OK(result)) { + return result; + } + + *binding_handle = pipe_cli->binding_handle; + + return WERR_OK; +} diff --git a/source3/lib/netapi/examples/common.c b/source3/lib/netapi/examples/common.c new file mode 100644 index 0000000..72d7150 --- /dev/null +++ b/source3/lib/netapi/examples/common.c @@ -0,0 +1,249 @@ +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <inttypes.h> + +#include <popt.h> +#include <netapi.h> + +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include <iconv.h> + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#include "common.h" + +void popt_common_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + struct libnetapi_ctx *ctx = NULL; + + libnetapi_getctx(&ctx); + + if (reason == POPT_CALLBACK_REASON_PRE) { + } + + if (reason == POPT_CALLBACK_REASON_POST) { + } + + if (!opt) { + return; + } + switch (opt->val) { + case 'U': { + char *puser = strdup(arg); + char *p = NULL; + + if ((p = strchr(puser,'%'))) { + size_t len; + *p = 0; + libnetapi_set_username(ctx, puser); + libnetapi_set_password(ctx, p+1); + len = strlen(p+1); + memset(strchr(arg,'%')+1,'X',len); + } else { + libnetapi_set_username(ctx, puser); + } + free(puser); + break; + } + case 'd': + libnetapi_set_debuglevel(ctx, arg); + break; + case 'p': + libnetapi_set_password(ctx, arg); + break; + case 'k': + libnetapi_set_use_kerberos(ctx); + break; + } +} + +struct poptOption popt_common_netapi_examples[] = { + { + .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, + .arg = (void *)popt_common_callback, + }, + { + .longName = "user", + .shortName = 'U', + .argInfo = POPT_ARG_STRING, + .val = 'U', + .descrip = "Username used for connection", + .argDescrip = "USERNAME", + }, + { + .longName = "password", + .shortName = 'p', + .argInfo = POPT_ARG_STRING, + .val = 'p', + .descrip = "Password used for connection", + .argDescrip = "PASSWORD", + }, + { + .longName = "debuglevel", + .shortName = 'd', + .argInfo = POPT_ARG_STRING, + .val = 'd', + .descrip = "Debuglevel", + .argDescrip = "DEBUGLEVEL", + }, + { + .longName = "kerberos", + .shortName = 'k', + .argInfo = POPT_ARG_NONE, + .val = 'k', + .descrip = "Use Kerberos", + }, + POPT_TABLEEND +}; + +char *netapi_read_file(const char *filename, uint32_t *psize) +{ + int fd; + FILE *file = NULL; + char *p = NULL; + size_t size = 0; + size_t chunk = 1024; + size_t maxsize = SIZE_MAX; + int err; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + goto fail; + } + + file = fdopen(fd, "r"); + if (file == NULL) { + goto fail; + } + + while (size < maxsize) { + char *tmp = NULL; + size_t newbufsize; + size_t nread; + + chunk = MIN(chunk, (maxsize - size)); + + newbufsize = size + (chunk+1); /* chunk+1 can't overflow */ + if (newbufsize < size) { + goto fail; /* overflow */ + } + + tmp = realloc(p, sizeof(char) * newbufsize); + if (tmp == NULL) { + free(p); + p = NULL; + goto fail; + } + p = tmp; + + nread = fread(p+size, 1, chunk, file); + size += nread; + + if (nread != chunk) { + break; + } + } + + err = ferror(file); + if (err != 0) { + free(p); + goto fail; + } + + p[size] = '\0'; + + if (psize != NULL) { + *psize = size; + } + fail: + if (file != NULL) { + fclose(file); + } + if (fd >= 0) { + close(fd); + } + + return p; +} + +int netapi_save_file(const char *fname, void *ppacket, size_t length) +{ + int fd; + fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd == -1) { + perror(fname); + return -1; + } + if (write(fd, ppacket, length) != length) { + fprintf(stderr,"Failed to write %s\n", fname); + close(fd); + return -1; + } + close(fd); + return 0; +} + +int netapi_save_file_ucs2(const char *fname, const char *str) +{ + char *str_p = NULL; + char *ucs2_str = NULL; + size_t str_len = 0; + size_t ucs2_str_len = 0; + iconv_t cd; + int ret; + char *start; + size_t start_len; + char *p; + + str_len = strlen(str) + 1; + ucs2_str_len = 2 * str_len; /* room for ucs2 */ + ucs2_str_len += 2; + + ucs2_str = calloc(ucs2_str_len, sizeof(char)); + if (ucs2_str == NULL) { + return -1; + } + p = ucs2_str; /* store for free */ + + ucs2_str[0] = 0xff; + ucs2_str[1] = 0xfe; + + start = ucs2_str; + start_len = ucs2_str_len; + + ucs2_str += 2; + ucs2_str_len -= 2; + + cd = iconv_open("UTF-16LE", "ASCII"); + if (cd == (iconv_t)-1) { + free(p); + return -1; + } + + str_p = (void *)((uintptr_t)str); + + ret = iconv(cd, + &str_p, + &str_len, + &ucs2_str, + &ucs2_str_len); + if (ret == -1) { + free(p); + return -1; + } + iconv_close(cd); + + ret = netapi_save_file(fname, start, start_len); + free(p); + + return ret; +} diff --git a/source3/lib/netapi/examples/common.h b/source3/lib/netapi/examples/common.h new file mode 100644 index 0000000..df7f176 --- /dev/null +++ b/source3/lib/netapi/examples/common.h @@ -0,0 +1,18 @@ +#include <popt.h> + +void popt_common_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data); + +extern struct poptOption popt_common_netapi_examples[]; + +#ifndef POPT_TABLEEND +#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL } +#endif + +#define POPT_COMMON_LIBNETAPI_EXAMPLES { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_netapi_examples, 0, "Common samba netapi example options:", NULL }, + +char *netapi_read_file(const char *filename, uint32_t *psize); +int netapi_save_file(const char *fname, void *ppacket, size_t length); +int netapi_save_file_ucs2(const char *fname, const char *str); diff --git a/source3/lib/netapi/examples/dsgetdc/dsgetdc.c b/source3/lib/netapi/examples/dsgetdc/dsgetdc.c new file mode 100644 index 0000000..6265e66 --- /dev/null +++ b/source3/lib/netapi/examples/dsgetdc/dsgetdc.c @@ -0,0 +1,101 @@ +/* + * Unix SMB/CIFS implementation. + * DsGetDcName query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + + const char *hostname = NULL; + const char *domain = NULL; + uint32_t flags = 0; + struct DOMAIN_CONTROLLER_INFO *info = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + { "flags", 0, POPT_ARG_INT, NULL, 'f', "Query flags", "FLAGS" }, + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("dsgetdc", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname domainname"); + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'f': + sscanf(poptGetOptArg(pc), "%x", &flags); + } + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + domain = poptGetArg(pc); + + /* DsGetDcName */ + + status = DsGetDcName(hostname, domain, NULL, NULL, flags, &info); + if (status != 0) { + printf("DsGetDcName failed with: %s\n", + libnetapi_errstr(status)); + return status; + } + + printf("DC Name:\t\t%s\n", info->domain_controller_name); + printf("DC Address:\t\t%s\n", info->domain_controller_address); + printf("DC Address Type:\t%d\n", info->domain_controller_address_type); + printf("Domain Name:\t\t%s\n", info->domain_name); + printf("DNS Forest Name:\t%s\n", info->dns_forest_name); + printf("DC flags:\t\t0x%08x\n", info->flags); + printf("DC Sitename:\t\t%s\n", info->dc_site_name); + printf("Client Sitename:\t%s\n", info->client_site_name); + + out: + NetApiBufferFree(info); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/file/file_close.c b/source3/lib/netapi/examples/file/file_close.c new file mode 100644 index 0000000..759173a --- /dev/null +++ b/source3/lib/netapi/examples/file/file_close.c @@ -0,0 +1,83 @@ +/* + * Unix SMB/CIFS implementation. + * NetFileClose query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t fileid = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("file_close", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname fileid"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + fileid = atoi(poptGetArg(pc)); + + /* NetFileClose */ + + status = NetFileClose(hostname, fileid); + if (status != 0) { + printf("NetFileClose failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/file/file_enum.c b/source3/lib/netapi/examples/file/file_enum.c new file mode 100644 index 0000000..5fbb285 --- /dev/null +++ b/source3/lib/netapi/examples/file/file_enum.c @@ -0,0 +1,146 @@ +/* + * Unix SMB/CIFS implementation. + * NetFileEnum query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *basepath = NULL; + const char *username = NULL; + uint32_t level = 3; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int i; + + struct FILE_INFO_2 *i2 = NULL; + struct FILE_INFO_3 *i3 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("file_enum", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname basepath username level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + basepath = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetFileEnum */ + + do { + + status = NetFileEnum(hostname, + basepath, + username, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + printf("total entries: %d\n", total_entries); + switch (level) { + case 2: + i2 = (struct FILE_INFO_2 *)buffer; + break; + case 3: + i3 = (struct FILE_INFO_3 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 2: + printf("file_id: %d\n", i2->fi2_id); + i2++; + break; + case 3: + printf("file_id: %d\n", i3->fi3_id); + printf("permissions: %d\n", i3->fi3_permissions); + printf("num_locks: %d\n", i3->fi3_num_locks); + printf("pathname: %s\n", i3->fi3_pathname); + printf("username: %s\n", i3->fi3_username); + i3++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetFileEnum failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/file/file_getinfo.c b/source3/lib/netapi/examples/file/file_getinfo.c new file mode 100644 index 0000000..9ad8305 --- /dev/null +++ b/source3/lib/netapi/examples/file/file_getinfo.c @@ -0,0 +1,112 @@ +/* + * Unix SMB/CIFS implementation. + * NetFileGetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t fileid = 0; + uint32_t level = 3; + uint8_t *buffer = NULL; + + struct FILE_INFO_2 *i2 = NULL; + struct FILE_INFO_3 *i3 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("file_getinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname fileid"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + fileid = atoi(poptGetArg(pc)); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetFileGetInfo */ + + status = NetFileGetInfo(hostname, + fileid, + level, + &buffer); + if (status != 0) { + printf("NetFileGetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 2: + i2 = (struct FILE_INFO_2 *)buffer; + printf("file_id: %d\n", i2->fi2_id); + break; + case 3: + i3 = (struct FILE_INFO_3 *)buffer; + printf("file_id: %d\n", i3->fi3_id); + printf("permissions: %d\n", i3->fi3_permissions); + printf("num_locks: %d\n", i3->fi3_num_locks); + printf("pathname: %s\n", i3->fi3_pathname); + printf("username: %s\n", i3->fi3_username); + break; + default: + break; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/getdc/getdc.c b/source3/lib/netapi/examples/getdc/getdc.c new file mode 100644 index 0000000..98bb6a1 --- /dev/null +++ b/source3/lib/netapi/examples/getdc/getdc.c @@ -0,0 +1,86 @@ +/* + * Unix SMB/CIFS implementation. + * GetDCName query + * Copyright (C) Guenther Deschner 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + + const char *hostname = NULL; + const char *domain = NULL; + uint8_t *buffer = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("getdc", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname domainname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + domain = poptGetArg(pc); + + /* NetGetDCName */ + + status = NetGetDCName(hostname, domain, &buffer); + if (status != 0) { + printf("GetDcName failed with: %s\n", libnetapi_errstr(status)); + } else { + printf("%s\n", (char *)buffer); + } + + out: + NetApiBufferFree(buffer); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_add.c b/source3/lib/netapi/examples/group/group_add.c new file mode 100644 index 0000000..4da97c5 --- /dev/null +++ b/source3/lib/netapi/examples/group/group_add.c @@ -0,0 +1,90 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupAdd query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + struct GROUP_INFO_1 g1; + uint32_t parm_error = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_add", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + /* NetGroupAdd */ + + g1.grpi1_name = groupname; + g1.grpi1_comment = "Domain Group created using NetApi example code"; + + status = NetGroupAdd(hostname, + 1, + (uint8_t *)&g1, + &parm_error); + if (status != 0) { + printf("NetGroupAdd failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_adduser.c b/source3/lib/netapi/examples/group/group_adduser.c new file mode 100644 index 0000000..253b3c5 --- /dev/null +++ b/source3/lib/netapi/examples/group/group_adduser.c @@ -0,0 +1,91 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupAddUser query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + const char *username = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_adduser", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname username"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + /* NetGroupAddUser */ + + status = NetGroupAddUser(hostname, + groupname, + username); + if (status != 0) { + printf("NetGroupAddUser failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_del.c b/source3/lib/netapi/examples/group/group_del.c new file mode 100644 index 0000000..789e429 --- /dev/null +++ b/source3/lib/netapi/examples/group/group_del.c @@ -0,0 +1,82 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupDel query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_del", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + /* NetGroupDel */ + + status = NetGroupDel(hostname, groupname); + if (status != 0) { + printf("NetGroupDel failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_deluser.c b/source3/lib/netapi/examples/group/group_deluser.c new file mode 100644 index 0000000..751ab5c --- /dev/null +++ b/source3/lib/netapi/examples/group/group_deluser.c @@ -0,0 +1,91 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupDelUser query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + const char *username = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_deluser", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname username"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + /* NetGroupDelUser */ + + status = NetGroupDelUser(hostname, + groupname, + username); + if (status != 0) { + printf("NetGroupDelUser failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_enum.c b/source3/lib/netapi/examples/group/group_enum.c new file mode 100644 index 0000000..fe2aee1 --- /dev/null +++ b/source3/lib/netapi/examples/group/group_enum.c @@ -0,0 +1,153 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupEnum query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int i; + char *sid_str = NULL; + + struct GROUP_INFO_0 *info0 = NULL; + struct GROUP_INFO_1 *info1 = NULL; + struct GROUP_INFO_2 *info2 = NULL; + struct GROUP_INFO_3 *info3 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_enum", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetGroupEnum */ + + do { + status = NetGroupEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + printf("total entries: %d\n", total_entries); + switch (level) { + case 0: + info0 = (struct GROUP_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_INFO_1 *)buffer; + break; + case 2: + info2 = (struct GROUP_INFO_2 *)buffer; + break; + case 3: + info3 = (struct GROUP_INFO_3 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d group: %s\n", i, info0->grpi0_name); + info0++; + break; + case 1: + printf("#%d group: %s\n", i, info1->grpi1_name); + printf("#%d comment: %s\n", i, info1->grpi1_comment); + info1++; + break; + case 2: + printf("#%d group: %s\n", i, info2->grpi2_name); + printf("#%d comment: %s\n", i, info2->grpi2_comment); + printf("#%d rid: %d\n", i, info2->grpi2_group_id); + printf("#%d attributes: 0x%08x\n", i, info2->grpi2_attributes); + info2++; + break; + case 3: + printf("#%d group: %s\n", i, info3->grpi3_name); + printf("#%d comment: %s\n", i, info3->grpi3_comment); + if (ConvertSidToStringSid(info3->grpi3_group_sid, + &sid_str)) { + printf("#%d group_sid: %s\n", i, sid_str); + free(sid_str); + } + printf("#%d attributes: 0x%08x\n", i, info3->grpi3_attributes); + info3++; + break; + + + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetGroupEnum failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_getinfo.c b/source3/lib/netapi/examples/group/group_getinfo.c new file mode 100644 index 0000000..2e5b793 --- /dev/null +++ b/source3/lib/netapi/examples/group/group_getinfo.c @@ -0,0 +1,127 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupGetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + struct GROUP_INFO_0 *g0; + struct GROUP_INFO_1 *g1; + struct GROUP_INFO_2 *g2; + struct GROUP_INFO_3 *g3; + char *sid_str = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_getinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetGroupGetInfo */ + + status = NetGroupGetInfo(hostname, + groupname, + level, + &buffer); + if (status != 0) { + printf("NetGroupGetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 0: + g0 = (struct GROUP_INFO_0 *)buffer; + printf("name: %s\n", g0->grpi0_name); + break; + case 1: + g1 = (struct GROUP_INFO_1 *)buffer; + printf("name: %s\n", g1->grpi1_name); + printf("comment: %s\n", g1->grpi1_comment); + break; + case 2: + g2 = (struct GROUP_INFO_2 *)buffer; + printf("name: %s\n", g2->grpi2_name); + printf("comment: %s\n", g2->grpi2_comment); + printf("group_id: %d\n", g2->grpi2_group_id); + printf("attributes: %d\n", g2->grpi2_attributes); + break; + case 3: + g3 = (struct GROUP_INFO_3 *)buffer; + printf("name: %s\n", g3->grpi3_name); + printf("comment: %s\n", g3->grpi3_comment); + if (ConvertSidToStringSid(g3->grpi3_group_sid, + &sid_str)) { + printf("group_sid: %s\n", sid_str); + free(sid_str); + } + printf("attributes: %d\n", g3->grpi3_attributes); + break; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_getusers.c b/source3/lib/netapi/examples/group/group_getusers.c new file mode 100644 index 0000000..72e79ec --- /dev/null +++ b/source3/lib/netapi/examples/group/group_getusers.c @@ -0,0 +1,132 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupGetUsers query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int i; + + struct GROUP_USERS_INFO_0 *info0 = NULL; + struct GROUP_USERS_INFO_1 *info1 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_getusers", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetGroupGetUsers */ + + do { + status = NetGroupGetUsers(hostname, + groupname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + printf("total entries: %d\n", total_entries); + switch (level) { + case 0: + info0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d member: %s\n", i, info0->grui0_name); + info0++; + break; + case 1: + printf("#%d member: %s\n", i, info1->grui1_name); + printf("#%d attributes: %d\n", i, info1->grui1_attributes); + info1++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetGroupGetUsers failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_setinfo.c b/source3/lib/netapi/examples/group/group_setinfo.c new file mode 100644 index 0000000..cd30d8b --- /dev/null +++ b/source3/lib/netapi/examples/group/group_setinfo.c @@ -0,0 +1,142 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupSetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + const char *option = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + uint32_t parm_err = 0; + struct GROUP_INFO_0 g0; + struct GROUP_INFO_1 g1; + struct GROUP_INFO_2 g2; + struct GROUP_INFO_3 g3; + struct GROUP_INFO_1002 g1002; + struct GROUP_INFO_1005 g1005; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_setinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level option"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + level = atoi(poptGetArg(pc)); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + option = poptGetArg(pc); + + /* NetGroupSetInfo */ + + switch (level) { + case 0: + g0.grpi0_name = option; + buffer = (uint8_t *)&g0; + break; + case 1: + g1.grpi1_name = option; /* this one will be ignored */ + g1.grpi1_comment = option; + buffer = (uint8_t *)&g1; + break; + case 2: + g2.grpi2_name = option; /* this one will be ignored */ + g2.grpi2_comment = option; + g2.grpi2_group_id = 4711; /* this one will be ignored */ + g2.grpi2_attributes = 7; + buffer = (uint8_t *)&g2; + break; + case 3: + g3.grpi3_name = option; /* this one will be ignored */ + g3.grpi3_comment = option; + g2.grpi2_attributes = 7; + buffer = (uint8_t *)&g3; + break; + case 1002: + g1002.grpi1002_comment = option; + buffer = (uint8_t *)&g1002; + break; + case 1005: + g1005.grpi1005_attributes = atoi(option); + buffer = (uint8_t *)&g1005; + break; + } + + status = NetGroupSetInfo(hostname, + groupname, + level, + buffer, + &parm_err); + if (status != 0) { + printf("NetGroupSetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/group/group_setusers.c b/source3/lib/netapi/examples/group/group_setusers.c new file mode 100644 index 0000000..70cf105 --- /dev/null +++ b/source3/lib/netapi/examples/group/group_setusers.c @@ -0,0 +1,142 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroupSetUsers query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t num_entries = 0; + const char **names = NULL; + int i = 0; + size_t buf_size = 0; + + struct GROUP_USERS_INFO_0 *g0 = NULL; + struct GROUP_USERS_INFO_1 *g1 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("group_setusers", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + + names = poptGetArgs(pc); + for (i=0; names[i] != NULL; i++) { + num_entries++; + } + + switch (level) { + case 0: + buf_size = sizeof(struct GROUP_USERS_INFO_0) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g0); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<num_entries; i++) { + g0[i].grui0_name = names[i]; + } + + buffer = (uint8_t *)g0; + break; + case 1: + buf_size = sizeof(struct GROUP_USERS_INFO_1) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g1); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<num_entries; i++) { + g1[i].grui1_name = names[i]; + } + + buffer = (uint8_t *)g1; + break; + default: + break; + } + + /* NetGroupSetUsers */ + + status = NetGroupSetUsers(hostname, + groupname, + level, + buffer, + num_entries); + if (status != 0) { + printf("NetGroupSetUsers failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/djoin.c b/source3/lib/netapi/examples/join/djoin.c new file mode 100644 index 0000000..737f330 --- /dev/null +++ b/source3/lib/netapi/examples/join/djoin.c @@ -0,0 +1,166 @@ +/* + * Unix SMB/CIFS implementation. + * Offline Domain Join utility + * Copyright (C) Guenther Deschner 2021 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *domain = NULL; + const char *machine_name = NULL; + const char *windows_path = NULL; + const char *dcname = NULL; + const char *loadfile = NULL; + const char *savefile = NULL; + const char *machine_account_ou = NULL; + uint32_t options = 0; + uint8_t *provision_bin_data = NULL; + uint32_t provision_bin_data_size = 0; + const char *provision_text_data = NULL; + int provision = 0; + int requestodj = 0; + int default_password = 0; + int print_blob = 0; + int localos = 0; + int reuse = 0; + + struct libnetapi_ctx *ctx = NULL; + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "provision", 0, POPT_ARG_NONE, &provision, 'D', "Create computer account in AD", NULL }, + { "dcname", 0, POPT_ARG_STRING, &dcname, 'D', "Domain Controller Name", "DCNAME" }, + { "machine_account_ou", 0, POPT_ARG_STRING, &machine_account_ou, 'D', "LDAP DN for Machine Account OU", "MACHINE_ACCOUNT_OU" }, + { "domain", 0, POPT_ARG_STRING, &domain, 'D', "Domain name", "DOMAIN" }, + { "machine_name", 0, POPT_ARG_STRING, &machine_name, 'D', "Computer Account Name", "MACHINENAME" }, + { "defpwd", 0, POPT_ARG_NONE, &default_password, 'D', "Use default password for machine account (not recommended)", "" }, + { "printblob", 0, POPT_ARG_NONE, &print_blob, 'D', "Print base64 encoded ODJ blob (for Windows answer files)", "" }, + { "savefile", 0, POPT_ARG_STRING, &savefile, 'D', "Save ODJ blob to file (for Windows answer files)", "FILENAME" }, + { "reuse", 0, POPT_ARG_NONE, &reuse, 'D', "Reuse machine account", "" }, + { "requestodj", 0, POPT_ARG_NONE, &requestodj, 'D', "Load offline join data", NULL }, + { "loadfile", 0, POPT_ARG_STRING, &loadfile, 'D', "Load file from previous provision", "FILENAME" }, + { "localos", 0, POPT_ARG_NONE, &localos, 'D', "Request local OS to load offline join information", "" }, + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("djoin", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "[provision|requestodj]"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (provision) { + + if (domain == NULL) { + printf("domain must be defined\n"); + goto out; + } + + if (machine_name == NULL) { + printf("machine_name must be defined\n"); + goto out; + } + + if (default_password) { + options |= NETSETUP_PROVISION_USE_DEFAULT_PASSWORD; + } + + if (reuse) { + options |= NETSETUP_PROVISION_REUSE_ACCOUNT; + } + + status = NetProvisionComputerAccount(domain, + machine_name, + machine_account_ou, + dcname, + options, + NULL, + 0, + &provision_text_data); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + if (print_blob) { + printf("Provision Text Data: %s\n", provision_text_data); + } + + if (savefile != NULL) { + status = netapi_save_file_ucs2(savefile, provision_text_data); + if (status != 0) { + goto out; + } + } + } + + if (requestodj) { + + if (loadfile == NULL) { + printf("--loadfile <FILENAME> is required\n"); + goto out; + } + provision_bin_data = (uint8_t *)netapi_read_file(loadfile, + &provision_bin_data_size); + if (provision_bin_data == NULL) { + printf("failed to read loadfile: %s\n", loadfile); + goto out; + } + + if (localos) { + options |= NETSETUP_PROVISION_ONLINE_CALLER; + } + + status = NetRequestOfflineDomainJoin(provision_bin_data, + provision_bin_data_size, + options, + windows_path); + free(provision_bin_data); + + if (status != 0 && status != 0x00000a99) { + /* NERR_JoinPerformedMustRestart */ + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/getjoinableous.c b/source3/lib/netapi/examples/join/getjoinableous.c new file mode 100644 index 0000000..c0fba57 --- /dev/null +++ b/source3/lib/netapi/examples/join/getjoinableous.c @@ -0,0 +1,110 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (cmdline + netapi) + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *host_name = NULL; + const char *domain_name = NULL; + const char **ous = NULL; + uint32_t num_ous = 0; + struct libnetapi_ctx *ctx = NULL; + int i; + const char *username = NULL; + const char *password = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "domain", 0, POPT_ARG_STRING, NULL, 'D', "Domain name", "DOMAIN" }, + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("getjoinableous", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname domainname"); + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'D': + domain_name = poptGetOptArg(pc); + break; + } + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + host_name = poptGetArg(pc); + + /* NetGetJoinableOUs */ + + status = libnetapi_get_username(ctx, &username); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + status = libnetapi_get_password(ctx, &password); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + status = NetGetJoinableOUs(host_name, + domain_name, + username, + password, + &num_ous, + &ous); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } else { + printf("Successfully queried joinable ous:\n"); + for (i=0; i<num_ous; i++) { + printf("ou: %s\n", ous[i]); + } + } + + out: + NetApiBufferFree(ous); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/getjoininformation.c b/source3/lib/netapi/examples/join/getjoininformation.c new file mode 100644 index 0000000..7dac456 --- /dev/null +++ b/source3/lib/netapi/examples/join/getjoininformation.c @@ -0,0 +1,105 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (cmdline + netapi) + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *host_name = NULL; + char *name_buffer = NULL; + const char *p = NULL; + uint16_t name_type = 0; + struct libnetapi_ctx *ctx = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("getjoininformation", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + host_name = poptGetArg(pc); + + /* NetGetJoinInformation */ + + status = NetGetJoinInformation(host_name, &p, &name_type); + name_buffer = discard_const_p(char, p); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } else { + printf("Successfully queried join information:\n"); + + switch (name_type) { + case NetSetupUnknownStatus: + printf("%s's join status unknown (name: %s)\n", + host_name, name_buffer); + break; + case NetSetupUnjoined: + printf("%s is not joined (name: %s)\n", + host_name, name_buffer); + break; + case NetSetupWorkgroupName: + printf("%s is joined to workgroup %s\n", + host_name, name_buffer); + break; + case NetSetupDomainName: + printf("%s is joined to domain %s\n", + host_name, name_buffer); + break; + default: + printf("%s is in unknown status %d (name: %s)\n", + host_name, name_type, name_buffer); + break; + } + } + + out: + NetApiBufferFree(name_buffer); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/netdomjoin.c b/source3/lib/netapi/examples/join/netdomjoin.c new file mode 100644 index 0000000..08ce71b --- /dev/null +++ b/source3/lib/netapi/examples/join/netdomjoin.c @@ -0,0 +1,104 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (cmdline + netapi) + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +enum { + OPT_OU = 1000 +}; + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *host_name = NULL; + const char *domain_name = NULL; + const char *account_ou = NULL; + const char *account = NULL; + const char *password = NULL; + uint32_t join_flags = NETSETUP_JOIN_DOMAIN | + NETSETUP_ACCT_CREATE | + NETSETUP_DOMAIN_JOIN_IF_JOINED; + struct libnetapi_ctx *ctx = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "ou", 0, POPT_ARG_STRING, &account_ou, 'U', "Account ou", "ACCOUNT_OU" }, + { "domain", 0, POPT_ARG_STRING, &domain_name, 'U', "Domain name (required)", "DOMAIN" }, + { "userd", 0, POPT_ARG_STRING, &account, 'U', "Domain admin account", "USERNAME" }, + { "passwordd", 0, POPT_ARG_STRING, &password, 'U', "Domain admin password", "PASSWORD" }, + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("netdomjoin", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + host_name = poptGetArg(pc); + + if (!domain_name) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + + /* NetJoinDomain */ + + status = NetJoinDomain(host_name, + domain_name, + account_ou, + account, + password, + join_flags); + if (status != 0) { + const char *errstr = NULL; + errstr = libnetapi_get_error_string(ctx, status); + printf("Join failed with: %s\n", errstr); + } else { + printf("Successfully joined\n"); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/provision_computer_account.c b/source3/lib/netapi/examples/join/provision_computer_account.c new file mode 100644 index 0000000..cc2dde5 --- /dev/null +++ b/source3/lib/netapi/examples/join/provision_computer_account.c @@ -0,0 +1,122 @@ +/* + * Unix SMB/CIFS implementation. + * NetProvisionComputerAccount query + * Copyright (C) Guenther Deschner 2021 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *domain = NULL; + const char *machine_name = NULL; + const char *machine_account_ou = NULL; + const char *dcname = NULL; + uint32_t options = 0; + const char *provision_text_data = NULL; + int default_password = 0; + int print_blob = 0; + const char *savefile = NULL; + int reuse = 0; + + struct libnetapi_ctx *ctx = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "dcname", 0, POPT_ARG_STRING, &dcname, 'D', "Domain Controller Name", "DCNAME" }, + { "machine_account_ou", 0, POPT_ARG_STRING, &machine_account_ou, 'D', "LDAP DN for Machine Account OU", "MACHINE_ACCOUNT_OU" }, + { "defpwd", 0, POPT_ARG_NONE, &default_password, 'D', "Use default password for machine account (not recommended)", "" }, + { "printblob", 0, POPT_ARG_NONE, &print_blob, 'D', "Print base64 encoded ODJ blob (for Windows answer files)", "" }, + { "savefile", 0, POPT_ARG_STRING, &savefile, 'D', "Save ODJ blob to file (for Windows answer files)", "FILENAME" }, + { "reuse", 0, POPT_ARG_NONE, &reuse, 'D', "Reuse machine account", "" }, + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("provision_computer_account", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "domain machine_name"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + domain = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + machine_name = poptGetArg(pc); + + if (default_password) { + options |= NETSETUP_PROVISION_USE_DEFAULT_PASSWORD; + } + if (reuse) { + options |= NETSETUP_PROVISION_REUSE_ACCOUNT; + } + + /* NetProvisionComputerAccount */ + + status = NetProvisionComputerAccount(domain, + machine_name, + machine_account_ou, + dcname, + options, + NULL, + 0, + &provision_text_data); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + if (print_blob) { + printf("Provision data: %s\n", provision_text_data); + } + + if (savefile != NULL) { + status = netapi_save_file_ucs2(savefile, provision_text_data); + if (status != 0) { + goto out; + } + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/rename_machine.c b/source3/lib/netapi/examples/join/rename_machine.c new file mode 100644 index 0000000..7be6dc2 --- /dev/null +++ b/source3/lib/netapi/examples/join/rename_machine.c @@ -0,0 +1,101 @@ +/* + * Unix SMB/CIFS implementation. + * NetRenameMachineInDomain query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *host_name = NULL; + const char *new_machine_name = NULL; + uint32_t rename_opt = 0; + struct libnetapi_ctx *ctx = NULL; + const char *username = NULL; + const char *password = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("rename_machine", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname newmachinename"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + host_name = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + new_machine_name = poptGetArg(pc); + + /* NetRenameMachineInDomain */ + + status = libnetapi_get_username(ctx, &username); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + status = libnetapi_get_password(ctx, &password); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + status = NetRenameMachineInDomain(host_name, + new_machine_name, + username, + password, + rename_opt); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/join/request_offline_domain_join.c b/source3/lib/netapi/examples/join/request_offline_domain_join.c new file mode 100644 index 0000000..96bbe0b --- /dev/null +++ b/source3/lib/netapi/examples/join/request_offline_domain_join.c @@ -0,0 +1,97 @@ +/* + * Unix SMB/CIFS implementation. + * NetRequestOfflineDomainJoin + * Copyright (C) Guenther Deschner 2021 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + const char *windows_path = NULL; + uint32_t options = 0; + uint8_t *provision_bin_data = NULL; + uint32_t provision_bin_data_size = 0; + const char *loadfile = NULL; + struct libnetapi_ctx *ctx = NULL; + int localos = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "loadfile", 0, POPT_ARG_STRING, &loadfile, 'D', "Load file from previous provision", "FILENAME" }, + { "localos", 0, POPT_ARG_NONE, &localos, 'D', "Request local OS to load offline join information", "" }, + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("request_offline_domain_join", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, ""); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (loadfile == NULL) { + printf("--loadfile <FILENAME> is required\n"); + goto out; + } + provision_bin_data = (uint8_t *)netapi_read_file(loadfile, + &provision_bin_data_size); + if (provision_bin_data == NULL) { + printf("failed to read loadfile: %s\n", loadfile); + goto out; + } + + if (localos) { + options |= NETSETUP_PROVISION_ONLINE_CALLER; + } + + /* NetRequestOfflineDomainJoin */ + + status = NetRequestOfflineDomainJoin(provision_bin_data, + provision_bin_data_size, + options, + windows_path); + free(provision_bin_data); + if (status != 0 && status != 0x00000a99) { + /* NERR_JoinPerformedMustRestart */ + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_add.c b/source3/lib/netapi/examples/localgroup/localgroup_add.c new file mode 100644 index 0000000..7f23c99 --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_add.c @@ -0,0 +1,106 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupAdd query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + const char *comment = NULL; + struct LOCALGROUP_INFO_0 g0; + struct LOCALGROUP_INFO_1 g1; + uint32_t parm_error = 0; + uint8_t *buf = NULL; + uint32_t level = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_add", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname comment"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + comment = poptGetArg(pc); + } + + /* NetLocalGroupAdd */ + + if (comment) { + level = 1; + g1.lgrpi1_name = groupname; + g1.lgrpi1_comment = comment; + buf = (uint8_t *)&g1; + } else { + level = 0; + g0.lgrpi0_name = groupname; + buf = (uint8_t *)&g0; + } + + status = NetLocalGroupAdd(hostname, + level, + buf, + &parm_error); + if (status != 0) { + printf("NetLocalGroupAdd failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_addmembers.c b/source3/lib/netapi/examples/localgroup/localgroup_addmembers.c new file mode 100644 index 0000000..aa4a9b5 --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_addmembers.c @@ -0,0 +1,141 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupAddMembers query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + struct LOCALGROUP_MEMBERS_INFO_0 *g0; + struct LOCALGROUP_MEMBERS_INFO_3 *g3; + uint32_t total_entries = 0; + uint8_t *buffer = NULL; + uint32_t level = 3; + const char **names = NULL; + int i = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_addmembers", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname member1 member2 ..."); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + + names = poptGetArgs(pc); + for (i=0; names[i] != NULL; i++) { + total_entries++; + } + + switch (level) { + case 0: + status = NetApiBufferAllocate(sizeof(struct LOCALGROUP_MEMBERS_INFO_0) * total_entries, + (void **)&g0); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<total_entries; i++) { + if (!ConvertStringSidToSid(names[i], &g0[i].lgrmi0_sid)) { + printf("could not convert sid\n"); + goto out; + } + } + + buffer = (uint8_t *)g0; + break; + case 3: + status = NetApiBufferAllocate(sizeof(struct LOCALGROUP_MEMBERS_INFO_3) * total_entries, + (void **)&g3); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<total_entries; i++) { + g3[i].lgrmi3_domainandname = names[i]; + } + + buffer = (uint8_t *)g3; + break; + default: + break; + } + + /* NetLocalGroupAddMembers */ + + status = NetLocalGroupAddMembers(hostname, + groupname, + level, + buffer, + total_entries); + if (status != 0) { + printf("NetLocalGroupAddMembers failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_del.c b/source3/lib/netapi/examples/localgroup/localgroup_del.c new file mode 100644 index 0000000..a2515df --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_del.c @@ -0,0 +1,83 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupDel query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_del", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + /* NetLocalGroupDel */ + + status = NetLocalGroupDel(hostname, + groupname); + if (status != 0) { + printf("NetLocalGroupDel failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_delmembers.c b/source3/lib/netapi/examples/localgroup/localgroup_delmembers.c new file mode 100644 index 0000000..7bd3ec0 --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_delmembers.c @@ -0,0 +1,141 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupDelMembers query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + struct LOCALGROUP_MEMBERS_INFO_0 *g0; + struct LOCALGROUP_MEMBERS_INFO_3 *g3; + uint32_t total_entries = 0; + uint8_t *buffer = NULL; + uint32_t level = 3; + const char **names = NULL; + int i = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_delmembers", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname member1 member2 ..."); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + + names = poptGetArgs(pc); + for (i=0; names[i] != NULL; i++) { + total_entries++; + } + + switch (level) { + case 0: + status = NetApiBufferAllocate(sizeof(struct LOCALGROUP_MEMBERS_INFO_0) * total_entries, + (void **)&g0); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<total_entries; i++) { + if (!ConvertStringSidToSid(names[i], &g0[i].lgrmi0_sid)) { + printf("could not convert sid\n"); + goto out; + } + } + + buffer = (uint8_t *)g0; + break; + case 3: + status = NetApiBufferAllocate(sizeof(struct LOCALGROUP_MEMBERS_INFO_3) * total_entries, + (void **)&g3); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<total_entries; i++) { + g3[i].lgrmi3_domainandname = names[i]; + } + + buffer = (uint8_t *)g3; + break; + default: + break; + } + + /* NetLocalGroupDelMembers */ + + status = NetLocalGroupDelMembers(hostname, + groupname, + level, + buffer, + total_entries); + if (status != 0) { + printf("NetLocalGroupDelMembers failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_enum.c b/source3/lib/netapi/examples/localgroup/localgroup_enum.c new file mode 100644 index 0000000..6fe0cf4 --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_enum.c @@ -0,0 +1,126 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupEnum query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int i; + + struct LOCALGROUP_INFO_0 *info0 = NULL; + struct LOCALGROUP_INFO_1 *info1 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_enum", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetLocalGroupEnum */ + + do { + status = NetLocalGroupEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + printf("total entries: %d\n", total_entries); + switch (level) { + case 0: + info0 = (struct LOCALGROUP_INFO_0 *)buffer; + break; + case 1: + info1 = (struct LOCALGROUP_INFO_1 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d group: %s\n", i, info0->lgrpi0_name); + info0++; + break; + case 1: + printf("#%d group: %s\n", i, info1->lgrpi1_name); + printf("#%d comment: %s\n", i, info1->lgrpi1_comment); + info1++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetLocalGroupEnum failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_getinfo.c b/source3/lib/netapi/examples/localgroup/localgroup_getinfo.c new file mode 100644 index 0000000..cd8fa8c --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_getinfo.c @@ -0,0 +1,112 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupGetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + struct LOCALGROUP_INFO_0 *g0; + struct LOCALGROUP_INFO_1 *g1; + struct LOCALGROUP_INFO_1002 *g1002; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_getinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetLocalGroupGetInfo */ + + status = NetLocalGroupGetInfo(hostname, + groupname, + level, + &buffer); + if (status != 0) { + printf("NetLocalGroupGetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 0: + g0 = (struct LOCALGROUP_INFO_0 *)buffer; + printf("name: %s\n", g0->lgrpi0_name); + break; + case 1: + g1 = (struct LOCALGROUP_INFO_1 *)buffer; + printf("name: %s\n", g1->lgrpi1_name); + printf("comment: %s\n", g1->lgrpi1_comment); + break; + case 1002: + g1002 = (struct LOCALGROUP_INFO_1002 *)buffer; + printf("comment: %s\n", g1002->lgrpi1002_comment); + break; + } + NetApiBufferFree(buffer); + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_getmembers.c b/source3/lib/netapi/examples/localgroup/localgroup_getmembers.c new file mode 100644 index 0000000..0589870 --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_getmembers.c @@ -0,0 +1,165 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupGetMembers query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + char *sid_str = NULL; + int i; + + struct LOCALGROUP_MEMBERS_INFO_0 *info0 = NULL; + struct LOCALGROUP_MEMBERS_INFO_1 *info1 = NULL; + struct LOCALGROUP_MEMBERS_INFO_2 *info2 = NULL; + struct LOCALGROUP_MEMBERS_INFO_3 *info3 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_getmembers", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetLocalGroupGetMembers */ + + do { + status = NetLocalGroupGetMembers(hostname, + groupname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + printf("total entries: %d\n", total_entries); + switch (level) { + case 0: + info0 = (struct LOCALGROUP_MEMBERS_INFO_0 *)buffer; + break; + case 1: + info1 = (struct LOCALGROUP_MEMBERS_INFO_1 *)buffer; + break; + case 2: + info2 = (struct LOCALGROUP_MEMBERS_INFO_2 *)buffer; + break; + case 3: + info3 = (struct LOCALGROUP_MEMBERS_INFO_3 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + if (ConvertSidToStringSid(info0->lgrmi0_sid, + &sid_str)) { + printf("#%d member sid: %s\n", i, sid_str); + free(sid_str); + } + info0++; + break; + case 1: + if (ConvertSidToStringSid(info1->lgrmi1_sid, + &sid_str)) { + printf("#%d member sid: %s\n", i, sid_str); + free(sid_str); + } + printf("#%d sid type: %d\n", i, info1->lgrmi1_sidusage); + printf("#%d name: %s\n", i, info1->lgrmi1_name); + info1++; + break; + case 2: + if (ConvertSidToStringSid(info2->lgrmi2_sid, + &sid_str)) { + printf("#%d member sid: %s\n", i, sid_str); + free(sid_str); + } + printf("#%d sid type: %d\n", i, info2->lgrmi2_sidusage); + printf("#%d full name: %s\n", i, info2->lgrmi2_domainandname); + info2++; + break; + case 3: + printf("#%d full name: %s\n", i, info3->lgrmi3_domainandname); + info3++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetLocalGroupGetMembers failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_setinfo.c b/source3/lib/netapi/examples/localgroup/localgroup_setinfo.c new file mode 100644 index 0000000..efcec76 --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_setinfo.c @@ -0,0 +1,128 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupSetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + struct LOCALGROUP_INFO_0 g0; + struct LOCALGROUP_INFO_1 g1; + struct LOCALGROUP_INFO_1002 g1002; + const char *newname = NULL; + const char *newcomment = NULL; + uint32_t parm_err = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + { "newname", 0, POPT_ARG_STRING, NULL, 'n', "New Local Group Name", "NEWNAME" }, + { "newcomment", 0, POPT_ARG_STRING, NULL, 'c', "New Local Group Comment", "NETCOMMENT" }, + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_setinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'n': + newname = poptGetOptArg(pc); + break; + case 'c': + newcomment = poptGetOptArg(pc); + break; + } + + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + if (newname && !newcomment) { + g0.lgrpi0_name = newname; + buffer = (uint8_t *)&g0; + level = 0; + } else if (newcomment && !newname) { + g1002.lgrpi1002_comment = newcomment; + buffer = (uint8_t *)&g1002; + level = 1002; + } else if (newname && newcomment) { + g1.lgrpi1_name = newname; + g1.lgrpi1_comment = newcomment; + buffer = (uint8_t *)&g1; + level = 1; + } else { + printf("not enough input\n"); + goto out; + } + + /* NetLocalGroupSetInfo */ + + status = NetLocalGroupSetInfo(hostname, + groupname, + level, + buffer, + &parm_err); + if (status != 0) { + printf("NetLocalGroupSetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/localgroup/localgroup_setmembers.c b/source3/lib/netapi/examples/localgroup/localgroup_setmembers.c new file mode 100644 index 0000000..c35f2bb --- /dev/null +++ b/source3/lib/netapi/examples/localgroup/localgroup_setmembers.c @@ -0,0 +1,146 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroupSetMembers query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *groupname = NULL; + struct LOCALGROUP_MEMBERS_INFO_0 *g0; + struct LOCALGROUP_MEMBERS_INFO_3 *g3; + uint32_t total_entries = 0; + uint8_t *buffer = NULL; + uint32_t level = 3; + const char **names = NULL; + int i = 0; + size_t buf_size = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("localgroup_setmembers", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname groupname member1 member2 ..."); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + groupname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + + names = poptGetArgs(pc); + for (i=0; names[i] != NULL; i++) { + total_entries++; + } + + switch (level) { + case 0: + buf_size = sizeof(struct LOCALGROUP_MEMBERS_INFO_0) * total_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g0); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<total_entries; i++) { + if (!ConvertStringSidToSid(names[i], &g0[i].lgrmi0_sid)) { + printf("could not convert sid\n"); + goto out; + } + } + + buffer = (uint8_t *)g0; + break; + case 3: + buf_size = sizeof(struct LOCALGROUP_MEMBERS_INFO_3) * total_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g3); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<total_entries; i++) { + g3[i].lgrmi3_domainandname = names[i]; + } + + buffer = (uint8_t *)g3; + break; + default: + break; + } + + /* NetLocalGroupSetMembers */ + + status = NetLocalGroupSetMembers(hostname, + groupname, + level, + buffer, + total_entries); + if (status != 0) { + printf("NetLocalGroupSetMembers failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + NetApiBufferFree(buffer); + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/netdomjoin-gui/logo-small.png b/source3/lib/netapi/examples/netdomjoin-gui/logo-small.png Binary files differnew file mode 100644 index 0000000..f041198 --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/logo-small.png diff --git a/source3/lib/netapi/examples/netdomjoin-gui/logo.png b/source3/lib/netapi/examples/netdomjoin-gui/logo.png Binary files differnew file mode 100644 index 0000000..6df4ace --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/logo.png diff --git a/source3/lib/netapi/examples/netdomjoin-gui/netdomjoin-gui.c b/source3/lib/netapi/examples/netdomjoin-gui/netdomjoin-gui.c new file mode 100644 index 0000000..6fd584a --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/netdomjoin-gui.c @@ -0,0 +1,1888 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (gtk + netapi) + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> + +#include <gtk/gtk.h> +#include <glib/gprintf.h> + +#include <netapi.h> + +#define MAX_CRED_LEN 256 +#define MAX_NETBIOS_NAME_LEN 15 + +#define SAMBA_ICON_PATH "/usr/share/pixmaps/samba/samba.ico" +#define SAMBA_IMAGE_PATH "/usr/share/pixmaps/samba/logo.png" +#define SAMBA_IMAGE_PATH_SMALL "/usr/share/pixmaps/samba/logo-small.png" + +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) + +static gboolean verbose = FALSE; + +typedef struct join_state { + struct libnetapi_ctx *ctx; + GtkWidget *window_main; + GtkWidget *window_parent; + GtkWidget *window_do_change; + GtkWidget *window_creds_prompt; + GtkWidget *entry_account; + GtkWidget *entry_password; + GtkWidget *entry_domain; + GtkWidget *entry_ou_list; + GtkWidget *entry_workgroup; + GtkWidget *button_ok; + GtkWidget *button_apply; + GtkWidget *button_ok_creds; + GtkWidget *button_get_ous; + GtkWidget *label_reboot; + GtkWidget *label_current_name_buffer; + GtkWidget *label_current_name_type; + GtkWidget *label_full_computer_name; + GtkWidget *label_winbind; + uint16_t name_type_initial; + uint16_t name_type_new; + char *name_buffer_initial; + char *name_buffer_new; + char *password; + char *account; + char *comment; + char *comment_new; + char *my_fqdn; + char *my_dnsdomain; + char *my_hostname; + uint16_t server_role; + gboolean settings_changed; + gboolean hostname_changed; + uint32_t stored_num_ous; + char *target_hostname; + uid_t uid; +} join_state; + +static void callback_creds_prompt(GtkWidget *widget, + gpointer data, + const char *label_string, + gpointer cont_fn); + + +static void debug(const char *format, ...) +{ + va_list args; + + if (!verbose) { + return; + } + + va_start(args, format); + g_vprintf(format, args); + va_end(args); +} + +static gboolean callback_delete_event(GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + gtk_main_quit(); + return FALSE; +} + +static void callback_do_close_data(GtkWidget *widget, + gpointer data) +{ + debug("callback_do_close_data called\n"); + + if (data) { + gtk_widget_destroy(GTK_WIDGET(data)); + } +} + +static void callback_do_close_widget(GtkWidget *widget, + gpointer data) +{ + debug("callback_do_close_widget called\n"); + + if (widget) { + gtk_widget_destroy(widget); + } +} + +static void callback_do_freeauth(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + debug("callback_do_freeauth called\n"); + + SAFE_FREE(state->account); + SAFE_FREE(state->password); + + if (state->window_creds_prompt) { + gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt)); + state->window_creds_prompt = NULL; + } +} + +static void callback_do_freeauth_and_close(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + debug("callback_do_freeauth_and_close called\n"); + + SAFE_FREE(state->account); + SAFE_FREE(state->password); + + if (state->window_creds_prompt) { + gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt)); + state->window_creds_prompt = NULL; + } + if (state->window_do_change) { + gtk_widget_destroy(GTK_WIDGET(state->window_do_change)); + state->window_do_change = NULL; + } +} + +static void free_join_state(struct join_state *s) +{ + SAFE_FREE(s->name_buffer_initial); + SAFE_FREE(s->name_buffer_new); + SAFE_FREE(s->password); + SAFE_FREE(s->account); + SAFE_FREE(s->comment); + SAFE_FREE(s->comment_new); + SAFE_FREE(s->my_fqdn); + SAFE_FREE(s->my_dnsdomain); + SAFE_FREE(s->my_hostname); +} + +static void do_cleanup(struct join_state *state) +{ + libnetapi_free(state->ctx); + free_join_state(state); +} + +static void callback_apply_description_change(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + NET_API_STATUS status = 0; + uint32_t parm_err = 0; + struct SERVER_INFO_1005 info1005; + GtkWidget *dialog; + + info1005.sv1005_comment = state->comment_new; + + status = NetServerSetInfo(state->target_hostname, + 1005, + (uint8_t *)&info1005, + &parm_err); + if (status) { + debug("NetServerSetInfo failed with: %s\n", + libnetapi_errstr(status)); + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Failed to change computer description: %s.", + libnetapi_get_error_string(state->ctx, status)); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_main)); + + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), FALSE); +} + +static void callback_do_exit(GtkWidget *widget, + gpointer data) +{ +#if 0 + GtkWidget *dialog; + gint result; +#endif + struct join_state *state = (struct join_state *)data; + + if (!state->settings_changed) { + callback_delete_event(NULL, NULL, NULL); + return; + } + +#if 0 + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "You must restart your computer before the new settings will take effect."); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + g_print("would reboot here\n"); + break; + case GTK_RESPONSE_NO: + default: + break; + } + if (dialog) { + gtk_widget_destroy(GTK_WIDGET(dialog)); + } +#endif + if (state->window_main) { + gtk_widget_destroy(GTK_WIDGET(state->window_main)); + state->window_main = NULL; + } + do_cleanup(state); + exit(0); +} + + +static void callback_do_reboot(GtkWidget *widget, + gpointer data, + gpointer data2) +{ + GtkWidget *dialog; + struct join_state *state = (struct join_state *)data2; + + debug("callback_do_reboot\n"); + + state->settings_changed = TRUE; + dialog = gtk_message_dialog_new(GTK_WINDOW(data), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "You must restart this computer for the changes to take effect."); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change)); +#if 0 + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + debug("showing dialog\n"); + gtk_widget_show(dialog); +#else + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(GTK_WIDGET(dialog)); +#endif + + gtk_label_set_text(GTK_LABEL(state->label_reboot), + "Changes will take effect after you restart this computer"); + + debug("destroying do_change window\n"); + gtk_widget_destroy(GTK_WIDGET(state->window_do_change)); + + { + uint32_t status; + const char *buffer; + uint16_t type; + + status = NetGetJoinInformation(state->target_hostname, + &buffer, + &type); + if (status != 0) { + g_print("failed to query status\n"); + return; + } + + debug("got new status: %s\n", buffer); + + SAFE_FREE(state->name_buffer_new); + state->name_buffer_new = strdup(buffer); + state->name_type_new = type; + state->name_buffer_initial = strdup(buffer); + state->name_type_initial = type; + NetApiBufferFree((void *)buffer); + + gtk_label_set_text(GTK_LABEL(state->label_current_name_buffer), + state->name_buffer_new); + if (state->name_type_new == NetSetupDomainName) { + gtk_label_set_text(GTK_LABEL(state->label_current_name_type), + "Domain:"); + } else { + gtk_label_set_text(GTK_LABEL(state->label_current_name_type), + "Workgroup:"); + } + } +} + +static void callback_return_username(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + debug("callback_return_username called\n"); + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + if (!entry_text) { + return; + } + debug("callback_return_username: %s\n", entry_text); + SAFE_FREE(state->account); + state->account = strdup(entry_text); +} + +static void callback_return_username_and_enter(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + if (!entry_text) { + return; + } + debug("callback_return_username_and_enter: %s\n", entry_text); + SAFE_FREE(state->account); + state->account = strdup(entry_text); + g_signal_emit_by_name(state->button_ok_creds, "clicked"); +} + +static void callback_return_password(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + debug("callback_return_password called\n"); + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + if (!entry_text) { + return; + } +#ifdef DEBUG_PASSWORD + debug("callback_return_password: %s\n", entry_text); +#else + debug("callback_return_password: (not printed)\n"); +#endif + SAFE_FREE(state->password); + state->password = strdup(entry_text); +} + +static void callback_return_password_and_enter(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + if (!entry_text) { + return; + } +#ifdef DEBUG_PASSWORD + debug("callback_return_password_and_enter: %s\n", entry_text); +#else + debug("callback_return_password_and_enter: (not printed)\n"); +#endif + SAFE_FREE(state->password); + state->password = strdup(entry_text); + g_signal_emit_by_name(state->button_ok_creds, "clicked"); +} + +static void callback_do_storeauth(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + debug("callback_do_storeauth called\n"); + + SAFE_FREE(state->account); + SAFE_FREE(state->password); + + callback_return_username(state->entry_account, (gpointer)state); + callback_return_password(state->entry_password, (gpointer)state); + + if (state->window_creds_prompt) { + gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt)); + state->window_creds_prompt = NULL; + } +} + +static void callback_continue(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + gtk_widget_grab_focus(GTK_WIDGET(state->button_ok)); + g_signal_emit_by_name(state->button_ok, "clicked"); +} + +static void callback_do_storeauth_and_continue(GtkWidget *widget, + gpointer data) +{ + callback_do_storeauth(widget, data); + callback_continue(NULL, data); +} + +static void callback_do_storeauth_and_scan(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + callback_do_storeauth(widget, data); + g_signal_emit_by_name(state->button_get_ous, "clicked"); +} + +static void callback_do_hostname_change(GtkWidget *widget, + gpointer data) +{ + GtkWidget *dialog; + const char *str = NULL; + + struct join_state *state = (struct join_state *)data; + + switch (state->name_type_initial) { + case NetSetupDomainName: { +#if 0 + NET_API_STATUS status; + const char *newname; + char *p = NULL; + + newname = strdup(gtk_label_get_text(GTK_LABEL(state->label_full_computer_name))); + if (!newname) { + return; + } + + p = strchr(newname, '.'); + if (p) { + *p = '\0'; + } + + if (!state->account || !state->password) { + debug("callback_do_hostname_change: no creds yet\n"); + callback_creds_prompt(NULL, state, + "Enter the name and password of an account with permission to change a computer name in the domain.", + callback_do_storeauth_and_continue); + } + + if (!state->account || !state->password) { + debug("callback_do_hostname_change: still no creds???\n"); + return; + } + + status = NetRenameMachineInDomain(state->target_hostname, + newname, + state->account, + state->password, + NETSETUP_ACCT_CREATE); + SAFE_FREE(newname); + /* we renamed the machine in the domain */ + if (status == 0) { + return; + } + str = libnetapi_get_error_string(state->ctx, status); +#else + str = "To be implemented: call NetRenameMachineInDomain\n"; +#endif + break; + } + case NetSetupWorkgroupName: + str = "To be implemented: call SetComputerNameEx\n"; + break; + default: + break; + } + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s",str); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_main)); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + gtk_widget_show(dialog); +} + +static void callback_creds_prompt(GtkWidget *widget, + gpointer data, + const char *label_string, + gpointer cont_fn) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *bbox; + GtkWidget *button; + GtkWidget *label; + + struct join_state *state = (struct join_state *)data; + + debug("callback_creds_prompt\n"); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + + gtk_window_set_title(GTK_WINDOW(window), "Computer Name Changes"); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_widget_set_size_request(GTK_WIDGET(window), 380, 280); + gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL); + gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(state->window_do_change)); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(callback_do_close_widget), NULL); + + state->window_creds_prompt = window; + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + box1 = gtk_vbox_new(FALSE, 0); + + gtk_container_add(GTK_CONTAINER(window), box1); + + label = gtk_label_new(label_string); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + gtk_box_pack_start(GTK_BOX(box1), label, FALSE, FALSE, 0); + + gtk_widget_show(label); + + /* USER NAME */ + label = gtk_label_new("User name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + state->entry_account = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(state->entry_account), MAX_CRED_LEN); + g_signal_connect(G_OBJECT(state->entry_account), "activate", + G_CALLBACK(callback_return_username_and_enter), + (gpointer)state); + gtk_editable_select_region(GTK_EDITABLE(state->entry_account), + 0, GTK_ENTRY(state->entry_account)->text_length); + gtk_box_pack_start(GTK_BOX(box1), state->entry_account, TRUE, TRUE, 0); + gtk_widget_show(state->entry_account); + + /* PASSWORD */ + label = gtk_label_new("Password:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + state->entry_password = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(state->entry_password), MAX_CRED_LEN); + gtk_entry_set_visibility(GTK_ENTRY(state->entry_password), FALSE); + g_signal_connect(G_OBJECT(state->entry_password), "activate", + G_CALLBACK(callback_return_password_and_enter), + (gpointer)state); + gtk_editable_set_editable(GTK_EDITABLE(state->entry_password), TRUE); + gtk_editable_select_region(GTK_EDITABLE(state->entry_password), + 0, GTK_ENTRY(state->entry_password)->text_length); + gtk_box_pack_start(GTK_BOX(box1), state->entry_password, TRUE, TRUE, 0); + gtk_widget_show(state->entry_password); + + /* BUTTONS */ + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_container_add(GTK_CONTAINER(box1), bbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox), 10); + + state->button_ok_creds = gtk_button_new_from_stock(GTK_STOCK_OK); + gtk_widget_grab_focus(GTK_WIDGET(state->button_ok_creds)); + gtk_container_add(GTK_CONTAINER(bbox), state->button_ok_creds); + g_signal_connect(G_OBJECT(state->button_ok_creds), "clicked", + G_CALLBACK(cont_fn), + (gpointer)state); + gtk_widget_show(state->button_ok_creds); + + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(bbox), button); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(callback_do_freeauth), + (gpointer)state); + gtk_widget_show_all(window); +} + +static void callback_do_join(GtkWidget *widget, + gpointer data) +{ + GtkWidget *dialog; + + NET_API_STATUS status; + const char *err_str = NULL; + uint32_t join_flags = 0; + uint32_t unjoin_flags = 0; + gboolean domain_join = FALSE; + gboolean try_unjoin = FALSE; + gboolean join_creds_required = TRUE; + gboolean unjoin_creds_required = TRUE; + const char *new_workgroup_type = NULL; + const char *initial_workgroup_type = NULL; + const char *account_ou = NULL; + + struct join_state *state = (struct join_state *)data; + + if (state->hostname_changed) { + callback_do_hostname_change(NULL, state); + return; + } + + switch (state->name_type_initial) { + case NetSetupWorkgroupName: + initial_workgroup_type = "workgroup"; + break; + case NetSetupDomainName: + initial_workgroup_type = "domain"; + break; + default: + break; + } + + switch (state->name_type_new) { + case NetSetupWorkgroupName: + new_workgroup_type = "workgroup"; + break; + case NetSetupDomainName: + new_workgroup_type = "domain"; + break; + default: + break; + } + + account_ou = gtk_combo_box_get_active_text(GTK_COMBO_BOX(state->entry_ou_list)); + if (account_ou && strlen(account_ou) == 0) { + account_ou = NULL; + } + + if ((state->name_type_initial != NetSetupDomainName) && + (state->name_type_new != NetSetupDomainName)) { + join_creds_required = FALSE; + unjoin_creds_required = FALSE; + } + + if (state->name_type_new == NetSetupDomainName) { + domain_join = TRUE; + join_creds_required = TRUE; + join_flags = NETSETUP_JOIN_DOMAIN | + NETSETUP_ACCT_CREATE | + NETSETUP_DOMAIN_JOIN_IF_JOINED; /* for testing */ + } + + if ((state->name_type_initial == NetSetupDomainName) && + (state->name_type_new == NetSetupWorkgroupName)) { + try_unjoin = TRUE; + unjoin_creds_required = TRUE; + join_creds_required = FALSE; + unjoin_flags = NETSETUP_JOIN_DOMAIN | + NETSETUP_ACCT_DELETE | + NETSETUP_IGNORE_UNSUPPORTED_FLAGS; + } + + if (try_unjoin) { + + debug("callback_do_join: Unjoining\n"); + + if (unjoin_creds_required) { + if (!state->account || !state->password) { + debug("callback_do_join: no creds yet\n"); + callback_creds_prompt(NULL, state, + "Enter the name and password of an account with permission to leave the domain.", + callback_do_storeauth_and_continue); + } + + if (!state->account || !state->password) { + debug("callback_do_join: still no creds???\n"); + return; + } + } + + status = NetUnjoinDomain(state->target_hostname, + state->account, + state->password, + unjoin_flags); + if (status != 0) { + callback_do_freeauth(NULL, state); + err_str = libnetapi_get_error_string(state->ctx, status); + g_print("callback_do_join: failed to unjoin (%s)\n", + err_str); +#if 0 + + /* in fact we shouldn't annoy the user with an error message here */ + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "The following error occurred attempting to unjoin the %s: \"%s\": %s", + initial_workgroup_type, + state->name_buffer_initial, + err_str); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +#endif + } + + } + + /* before prompting for creds, make sure we can find a dc */ + + if (domain_join) { + + struct DOMAIN_CONTROLLER_INFO *dc_info = NULL; + + status = DsGetDcName(NULL, + state->name_buffer_new, + NULL, + NULL, + 0, + &dc_info); + if (status != 0) { + err_str = libnetapi_get_error_string(state->ctx, status); + g_print("callback_do_join: failed find dc (%s)\n", err_str); + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to find a domain controller for domain: \"%s\": %s", + state->name_buffer_new, + err_str); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change)); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + + return; + } + } + + if (join_creds_required) { + if (!state->account || !state->password) { + debug("callback_do_join: no creds yet\n"); + callback_creds_prompt(NULL, state, + "Enter the name and password of an account with permission to join the domain.", + callback_do_storeauth_and_continue); + } + + if (!state->account || !state->password) { + debug("callback_do_join: still no creds???\n"); + return; + } + } + + debug("callback_do_join: Joining a %s named %s using join_flags 0x%08x ", + new_workgroup_type, + state->name_buffer_new, + join_flags); + if (domain_join) { + debug("as %s ", state->account); +#ifdef DEBUG_PASSWORD + debug("with %s ", state->password); +#endif + } + debug("\n"); + + status = NetJoinDomain(state->target_hostname, + state->name_buffer_new, + account_ou, + state->account, + state->password, + join_flags); + if (status != 0) { + callback_do_freeauth(NULL, state); + err_str = libnetapi_get_error_string(state->ctx, status); + g_print("callback_do_join: failed to join (%s)\n", err_str); + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "The following error occurred attempting to join the %s: \"%s\": %s", + new_workgroup_type, + state->name_buffer_new, + err_str); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change)); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + + return; + } + + debug("callback_do_join: Successfully joined %s\n", + new_workgroup_type); + + callback_do_freeauth(NULL, state); + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "Welcome to the %s %s.", + state->name_buffer_new, + new_workgroup_type); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change)); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + callback_do_reboot(NULL, state->window_parent, state); +} + +static void callback_enter_hostname_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + char *str = NULL; + struct join_state *state = (struct join_state *)data; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_hostname_and_unlock: %s\n", entry_text); + if (!entry_text || entry_text[0] == 0) { + state->hostname_changed = FALSE; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + gtk_label_set_text(GTK_LABEL(state->label_full_computer_name), ""); + return; + } + if (strcasecmp(state->my_hostname, entry_text) == 0) { + state->hostname_changed = FALSE; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + /* return; */ + } else { + state->hostname_changed = TRUE; + } + + if (state->name_type_initial == NetSetupDomainName) { + if (asprintf(&str, "%s.%s", entry_text, state->my_dnsdomain) == -1) { + return; + } + } else { + if (asprintf(&str, "%s.", entry_text) == -1) { + return; + } + } + gtk_label_set_text(GTK_LABEL(state->label_full_computer_name), str); + free(str); + + if (state->hostname_changed && entry_text && entry_text[0] != 0 && entry_text[0] != '.') { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE); + } +} + +static void callback_enter_computer_description_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + struct join_state *state = (struct join_state *)data; + int string_unchanged = FALSE; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_computer_description_and_unlock: %s\n", + entry_text); +#if 0 + if (!entry_text || entry_text[0] == 0) { + string_unchanged = 1; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), + FALSE); + return; + } +#endif + if (entry_text && state->comment && strcasecmp(state->comment, entry_text) == 0) { + string_unchanged = TRUE; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), + FALSE); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), TRUE); + SAFE_FREE(state->comment_new); + state->comment_new = strdup(entry_text); + +} + + +static void callback_enter_workgroup_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + struct join_state *state = (struct join_state *)data; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_workgroup_and_unlock: %s\n", entry_text); + if (!entry_text || entry_text[0] == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + if ((strcasecmp(state->name_buffer_initial, entry_text) == 0) && + (state->name_type_initial == NetSetupWorkgroupName)) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE); + SAFE_FREE(state->name_buffer_new); + state->name_buffer_new = strdup(entry_text); + state->name_type_new = NetSetupWorkgroupName; +} + +static void callback_enter_domain_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + struct join_state *state = (struct join_state *)data; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_domain_and_unlock: %s\n", entry_text); + if (!entry_text || entry_text[0] == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + if ((strcasecmp(state->name_buffer_initial, entry_text) == 0) && + (state->name_type_initial == NetSetupDomainName)) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_ou_list), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(state->button_get_ous), TRUE); + SAFE_FREE(state->name_buffer_new); + state->name_buffer_new = strdup(entry_text); + state->name_type_new = NetSetupDomainName; +} + +static void callback_apply_continue(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + gtk_widget_grab_focus(GTK_WIDGET(state->button_apply)); + g_signal_emit_by_name(state->button_apply, "clicked"); +} + +static void callback_do_join_workgroup(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + debug("callback_do_join_workgroup chosen\n"); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_workgroup), TRUE); + gtk_widget_grab_focus(GTK_WIDGET(state->entry_workgroup)); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_domain), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_ou_list), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(state->button_get_ous), FALSE); + callback_enter_workgroup_and_unlock(state->entry_workgroup, state); /* TEST */ +} + +static void callback_do_join_domain(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + debug("callback_do_join_domain chosen\n"); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_domain), TRUE); + gtk_widget_grab_focus(GTK_WIDGET(state->entry_domain)); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_workgroup), FALSE); + callback_enter_domain_and_unlock(state->entry_domain, state); /* TEST */ +} + +static void callback_do_getous(GtkWidget *widget, + gpointer data) +{ + NET_API_STATUS status; + uint32_t num_ous = 0; + const char **ous = NULL; + int i; + const char *domain = NULL; + struct DOMAIN_CONTROLLER_INFO *dc_info = NULL; + const char *err_str = NULL; + GtkWidget *dialog; + + struct join_state *state = (struct join_state *)data; + + debug("callback_do_getous called\n"); + + domain = state->name_buffer_new ? state->name_buffer_new : state->name_buffer_initial; + + status = DsGetDcName(NULL, + domain, + NULL, + NULL, + 0, + &dc_info); + if (status != 0) { + err_str = libnetapi_get_error_string(state->ctx, status); + g_print("callback_do_getous: failed find dc (%s)\n", err_str); + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Failed to find a domain controller for domain: \"%s\": %s", + domain, + err_str); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change)); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + + return; + } + + if (!state->account || !state->password) { + debug("callback_do_getous: no creds yet\n"); + callback_creds_prompt(NULL, state, + "Enter the name and password of an account with permission to join the domain.", + callback_do_storeauth_and_scan); + } + + if (!state->account || !state->password) { + debug("callback_do_getous: still no creds ???\n"); + return; + } + + status = NetGetJoinableOUs(state->target_hostname, + domain, + state->account, + state->password, + &num_ous, &ous); + if (status != NET_API_STATUS_SUCCESS) { + callback_do_freeauth(NULL, state); + debug("failed to call NetGetJoinableOUs: %s\n", + libnetapi_get_error_string(state->ctx, status)); + dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "Failed to query joinable OUs: %s", + libnetapi_get_error_string(state->ctx, status)); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change)); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + return; + } + + for (i=0; i<state->stored_num_ous; i++) { + gtk_combo_box_remove_text(GTK_COMBO_BOX(state->entry_ou_list), 0); + } + for (i=0; i<num_ous && ous[i] != NULL; i++) { + gtk_combo_box_append_text(GTK_COMBO_BOX(state->entry_ou_list), + ous[i]); + } + NetApiBufferFree(ous); + state->stored_num_ous = num_ous; + gtk_combo_box_set_active(GTK_COMBO_BOX(state->entry_ou_list), num_ous-1); +} + +static void callback_do_change(GtkWidget *widget, + gpointer data) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *bbox; + GtkWidget *button_workgroup; + GtkWidget *button_domain; + GtkWidget *button; + GtkWidget *label; + GtkWidget *frame_horz; + GtkWidget *vbox; + GtkWidget *entry; + GSList *group; + + struct join_state *state = (struct join_state *)data; + + debug("callback_do_change called\n"); + +#if 0 + /* FIXME: add proper warnings for Samba as a DC */ + if (state->server_role == 3) { + GtkWidget *dialog; + callback_do_freeauth(NULL, state); + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Domain controller cannot be moved from one domain to another, they must first be demoted. Renaming this domain controller may cause it to become temporarily unavailable to users and computers. For information on renaming domain controllers, including alternate renaming methods, see Help and Support. To continue renaming this domain controller, click OK."); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + return; + } +#endif + + state->button_ok = gtk_button_new_from_stock(GTK_STOCK_OK); + state->button_get_ous = gtk_button_new_with_label("Scan for joinable OUs"); + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + + gtk_window_set_title(GTK_WINDOW(window), "Computer Name Changes"); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_widget_set_size_request(GTK_WIDGET(window), 480, 650); + gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL); + gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(state->window_main)); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(callback_do_close_widget), NULL); + + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + box1 = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), box1); + + label = gtk_label_new("You can change the name and membership of this computer. Changes may affect access to network resources."); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + /* COMPUTER NAME */ + label = gtk_label_new("Computer name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + state->label_full_computer_name = gtk_label_new(NULL); + { + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_NETBIOS_NAME_LEN); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(callback_enter_hostname_and_unlock), + (gpointer)state); + gtk_entry_set_text(GTK_ENTRY(entry), state->my_hostname); + gtk_editable_select_region(GTK_EDITABLE(entry), + 0, GTK_ENTRY(entry)->text_length); + + gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); /* ! */ + gtk_box_pack_start(GTK_BOX(box1), entry, TRUE, TRUE, 0); + gtk_widget_show(entry); + } + + /* FULL COMPUTER NAME */ + label = gtk_label_new("Full computer name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + { + const gchar *entry_text; + char *str = NULL; + entry_text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (state->name_type_initial == NetSetupDomainName) { + if (asprintf(&str, "%s.%s", entry_text, + state->my_dnsdomain) == -1) { + return; + } + } else { + if (asprintf(&str, "%s.", entry_text) == -1) { + return; + } + } + gtk_label_set_text(GTK_LABEL(state->label_full_computer_name), + str); + free(str); + gtk_misc_set_alignment(GTK_MISC(state->label_full_computer_name), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), + state->label_full_computer_name, TRUE, TRUE, 0); + gtk_widget_show(state->label_full_computer_name); + } + + /* BOX */ + frame_horz = gtk_frame_new ("Member Of"); + gtk_box_pack_start(GTK_BOX(box1), frame_horz, TRUE, TRUE, 10); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + gtk_container_add(GTK_CONTAINER(frame_horz), vbox); + + /* TWO ENTRIES */ + state->entry_workgroup = gtk_entry_new(); + state->entry_domain = gtk_entry_new(); + + /* DOMAIN */ + button_domain = gtk_radio_button_new_with_label(NULL, "Domain"); + if (state->name_type_initial == NetSetupDomainName) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_domain), TRUE); + } + gtk_box_pack_start(GTK_BOX(vbox), button_domain, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(button_domain), "clicked", + G_CALLBACK(callback_do_join_domain), + (gpointer)state); + + { + gtk_entry_set_max_length(GTK_ENTRY(state->entry_domain), 50); + g_signal_connect(G_OBJECT(state->entry_domain), "changed", + G_CALLBACK(callback_enter_domain_and_unlock), + (gpointer)state); + g_signal_connect(G_OBJECT(state->entry_domain), "activate", + G_CALLBACK(callback_continue), + (gpointer)state); + if (state->name_type_initial == NetSetupDomainName) { + gtk_entry_set_text(GTK_ENTRY(state->entry_domain), + state->name_buffer_initial); + gtk_widget_set_sensitive(state->entry_workgroup, FALSE); + gtk_widget_set_sensitive(state->entry_domain, TRUE); + } + gtk_editable_set_editable(GTK_EDITABLE(state->entry_domain), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), state->entry_domain, TRUE, TRUE, 0); + gtk_widget_show(state->entry_domain); + } + gtk_widget_show(button_domain); + + /* WORKGROUP */ + group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button_domain)); + button_workgroup = gtk_radio_button_new_with_label(group, "Workgroup"); + if (state->name_type_initial == NetSetupWorkgroupName) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_workgroup), TRUE); + } + gtk_box_pack_start(GTK_BOX(vbox), button_workgroup, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(button_workgroup), "clicked", + G_CALLBACK(callback_do_join_workgroup), + (gpointer)state); + { + gtk_entry_set_max_length(GTK_ENTRY(state->entry_workgroup), + MAX_NETBIOS_NAME_LEN); + g_signal_connect(G_OBJECT(state->entry_workgroup), "changed", + G_CALLBACK(callback_enter_workgroup_and_unlock), + (gpointer)state); + g_signal_connect(G_OBJECT(state->entry_workgroup), "activate", + G_CALLBACK(callback_continue), + (gpointer)state); + + if (state->name_type_initial == NetSetupWorkgroupName) { + gtk_entry_set_text(GTK_ENTRY(state->entry_workgroup), + state->name_buffer_initial); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_domain), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_workgroup), TRUE); + } + gtk_box_pack_start(GTK_BOX(vbox), state->entry_workgroup, TRUE, TRUE, 0); + gtk_widget_show(state->entry_workgroup); + } + gtk_widget_show(button_workgroup); + + /* Advanced Options */ + frame_horz = gtk_frame_new("Advanced Options"); + gtk_box_pack_start(GTK_BOX(box1), frame_horz, TRUE, TRUE, 10); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + gtk_container_add(GTK_CONTAINER(frame_horz), vbox); + + /* OUs */ + gtk_container_add(GTK_CONTAINER(vbox), state->button_get_ous); + gtk_widget_set_sensitive(GTK_WIDGET(state->button_get_ous), FALSE); + g_signal_connect(G_OBJECT(state->button_get_ous), "clicked", + G_CALLBACK(callback_do_getous), + (gpointer)state); + + state->entry_ou_list = gtk_combo_box_entry_new_text(); + gtk_widget_set_sensitive(state->entry_ou_list, FALSE); + if (state->name_type_initial == NetSetupWorkgroupName) { + gtk_widget_set_sensitive(state->entry_ou_list, FALSE); + gtk_widget_set_sensitive(state->button_get_ous, FALSE); + } + gtk_box_pack_start(GTK_BOX(vbox), state->entry_ou_list, TRUE, TRUE, 0); + gtk_widget_show(state->entry_ou_list); + + { + state->label_winbind = gtk_check_button_new_with_label("Modify winbind configuration"); + gtk_box_pack_start(GTK_BOX(vbox), state->label_winbind, TRUE, TRUE, 0); + gtk_widget_set_sensitive(state->label_winbind, FALSE); + } + + + /* BUTTONS */ + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_container_add(GTK_CONTAINER(box1), bbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox), 10); + + state->window_do_change = window; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + gtk_container_add(GTK_CONTAINER(bbox), state->button_ok); + g_signal_connect(G_OBJECT(state->button_ok), "clicked", + G_CALLBACK(callback_do_join), + (gpointer)state); + + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(bbox), button); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(callback_do_freeauth_and_close), + (gpointer)state); + + gtk_widget_show_all(window); +} + +static void callback_do_about(GtkWidget *widget, + gpointer data) +{ + GdkPixbuf *logo; + GError *error = NULL; + GtkWidget *about; + + struct join_state *state = (struct join_state *)data; + + debug("callback_do_about called\n"); + + logo = gdk_pixbuf_new_from_file(SAMBA_IMAGE_PATH, + &error); + if (logo == NULL) { + g_print("failed to load logo from %s: %s\n", + SAMBA_IMAGE_PATH, error->message); + } + + about = gtk_about_dialog_new(); + gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "Samba"); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), "3.2.0pre3"); + gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), + "Copyright Andrew Tridgell and the Samba Team 1992-2008\n" + "Copyright Günther Deschner 2007-2008"); + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), "GPLv3"); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://www.samba.org"); + gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(about), "http://www.samba.org"); + if (logo) { + gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), logo); + } + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), "Samba gtk domain join utility"); + gtk_window_set_modal(GTK_WINDOW(about), TRUE); + gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(state->window_main)); + g_signal_connect_swapped(about, "response", + G_CALLBACK(gtk_widget_destroy), + about); + + gtk_widget_show(about); +} + +static int draw_main_window(struct join_state *state) +{ + GtkWidget *window; + GtkWidget *button; + GtkWidget *label; + GtkWidget *main_vbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *bbox; + GtkWidget *image; + GtkWidget *table; + GtkWidget *entry; + GdkPixbuf *icon; + GError *error = NULL; + + icon = gdk_pixbuf_new_from_file(SAMBA_ICON_PATH, + &error); + if (icon == NULL) { + g_print("failed to load icon from %s : %s\n", + SAMBA_ICON_PATH, error->message); + } + +#if 1 + image = gtk_image_new_from_file(SAMBA_IMAGE_PATH_SMALL); +#else + image = gtk_image_new_from_file("/usr/share/pixmaps/redhat-system_settings.png"); +#endif + if (image == NULL) { + g_print("failed to load logo from %s : %s\n", + SAMBA_IMAGE_PATH_SMALL, error->message); + } + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + state->window_main = window; + + gtk_window_set_title(GTK_WINDOW(window), "Samba - Join Domain dialogue"); + gtk_widget_set_size_request(GTK_WIDGET(window), 600, 600); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(callback_delete_event), NULL); + + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + main_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(window), main_vbox); + +#if 0 + gtk_box_pack_start(GTK_BOX(main_vbox), image, TRUE, TRUE, 10); + gtk_widget_show(image); +#endif + /* Hbox */ + hbox = gtk_hbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(main_vbox), hbox); + + { +/* gtk_box_pack_start(GTK_BOX(main_vbox), image, TRUE, TRUE, 10); */ +/* gtk_misc_set_alignment(GTK_MISC(image), 0, 0); */ + gtk_widget_set_size_request(GTK_WIDGET(image), 150, 40); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 10); + gtk_widget_show(image); + + /* Label */ + label = gtk_label_new("Samba uses the following information to identify your computer on the network."); +/* gtk_misc_set_alignment(GTK_MISC(label), 0, 0); */ + gtk_widget_set_size_request(GTK_WIDGET(label), 400, 40); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + } + + gtk_widget_show(hbox); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + gtk_container_add(GTK_CONTAINER(main_vbox), vbox); + + /* Table */ + table = gtk_table_new(6, 3, TRUE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_container_add(GTK_CONTAINER(vbox), table); + + { + /* Label */ + label = gtk_label_new("Computer description:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); + gtk_widget_show(label); + + state->button_apply = gtk_button_new_from_stock(GTK_STOCK_APPLY); + + /* Entry */ + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), 256); + + if (!state->target_hostname && state->uid != 0) { + gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE); + } + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(callback_enter_computer_description_and_unlock), + state); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(callback_apply_continue), + (gpointer)state); + + gtk_entry_set_text(GTK_ENTRY(entry), (char *)state->comment); + gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); /* ! */ + gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 3, 0, 1); + gtk_widget_show(entry); + } + + /* Label */ + label = gtk_label_new("For example: \"Samba \%v\"."); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 3, 1, 2); + gtk_widget_show(label); + + /* Label */ + label = gtk_label_new("Full computer name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); + gtk_widget_show(label); + + { + /* Label */ + char *str = NULL; + if (state->name_type_initial == NetSetupDomainName) { + if (asprintf(&str, "%s.%s", state->my_hostname, + state->my_dnsdomain) == -1) { + return -1; + } + } else { + if (asprintf(&str, "%s.", state->my_hostname) == -1) { + return -1; + } + } + + label = gtk_label_new(str); + SAFE_FREE(str); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 3, 2, 3); + gtk_widget_show(label); + } + + /* Label */ + if (state->name_type_initial == NetSetupDomainName) { + label = gtk_label_new("Domain:"); + } else { + label = gtk_label_new("Workgroup:"); + } + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); + gtk_widget_show(label); + state->label_current_name_type = label; + + /* Label */ + label = gtk_label_new(state->name_buffer_initial); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 3, 3, 4); + gtk_widget_show(label); + state->label_current_name_buffer = label; + + { + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(vbox), hbox); + label = gtk_label_new("To rename this computer or join a domain, click Change."); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + + } + + /* bbox */ + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_container_add(GTK_CONTAINER(hbox), bbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox), 10); + + button = gtk_button_new_with_mnemonic("Ch_ange"); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(callback_do_change), + (gpointer)state); + gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0); + if (!state->target_hostname && state->uid != 0) { + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + } + gtk_widget_show(button); + + /* Label (hidden) */ + state->label_reboot = gtk_label_new(NULL); + gtk_label_set_line_wrap(GTK_LABEL(state->label_reboot), TRUE); + gtk_misc_set_alignment(GTK_MISC(state->label_reboot), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), state->label_reboot, TRUE, TRUE, 0); + if (!state->target_hostname && state->uid != 0) { + gtk_label_set_text(GTK_LABEL(state->label_reboot), + "You cannot change computer description as you're not running with root permissions"); + } + + gtk_widget_show(state->label_reboot); + +#if 0 + gtk_box_pack_start(GTK_BOX(vbox), + create_bbox(window, TRUE, NULL, 10, 85, 20, GTK_BUTTONBOX_END), + TRUE, TRUE, 5); +#endif + { + + GtkWidget *frame; + GtkWidget *bbox2; + GtkWidget *button2; + + frame = gtk_frame_new(NULL); + bbox2 = gtk_hbutton_box_new(); + + gtk_container_set_border_width(GTK_CONTAINER(bbox2), 5); + gtk_container_add(GTK_CONTAINER(frame), bbox2); + + /* Set the appearance of the Button Box */ + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox2), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox2), 10); + /*gtk_button_box_set_child_size(GTK_BUTTON_BOX(bbox2), child_w, child_h);*/ + + button2 = gtk_button_new_from_stock(GTK_STOCK_OK); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(callback_do_exit), state); + + button2 = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", + G_CALLBACK(callback_delete_event), + window); + + gtk_container_add(GTK_CONTAINER(bbox2), state->button_apply); + g_signal_connect(G_OBJECT(state->button_apply), "clicked", + G_CALLBACK(callback_apply_description_change), + state); + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), FALSE); + + button2 = gtk_button_new_from_stock(GTK_STOCK_ABOUT); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", + G_CALLBACK(callback_do_about), + state); +#if 0 + button2 = gtk_button_new_from_stock(GTK_STOCK_HELP); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", + G_CALLBACK(callback_do_about), + window); +#endif + gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 5); + } + + gtk_widget_show_all(window); + + return 0; +} + +static int init_join_state(struct join_state **state) +{ + struct join_state *s; + + s = (struct join_state *)malloc(sizeof(struct join_state)); + if (!s) { + return -1; + } + + memset(s, '\0', sizeof(struct join_state)); + + *state = s; + + return 0; +} + +static NET_API_STATUS get_server_properties(struct join_state *state) +{ + struct SERVER_INFO_101 *info101 = NULL; + struct SERVER_INFO_1005 *info1005 = NULL; + NET_API_STATUS status; + + status = NetServerGetInfo(state->target_hostname, + 101, + (uint8_t **)&info101); + if (status == 0) { + state->comment = strdup(info101->sv101_comment); + if (!state->comment) { + return -1; + } + SAFE_FREE(state->my_hostname); + state->my_hostname = strdup(info101->sv101_name); + if (!state->my_hostname) { + return -1; + } + NetApiBufferFree(info101); + return NET_API_STATUS_SUCCESS; + } + + switch (status) { + case 124: /* WERR_INVALID_LEVEL */ + case 50: /* WERR_NOT_SUPPORTED */ + break; + default: + goto failed; + } + + status = NetServerGetInfo(state->target_hostname, + 1005, + (uint8_t **)&info1005); + if (status == 0) { + state->comment = strdup(info1005->sv1005_comment); + if (!state->comment) { + return -1; + } + NetApiBufferFree(info1005); + return NET_API_STATUS_SUCCESS; + } + + failed: + printf("NetServerGetInfo failed with: %s\n", + libnetapi_get_error_string(state->ctx, status)); + + return status; +} + +static int initialize_join_state(struct join_state *state, + const char *debug_level, + const char *target_hostname, + const char *target_username) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status = 0; + + status = libnetapi_init(&ctx); + if (status) { + return status; + } + + if (debug_level) { + libnetapi_set_debuglevel(ctx, debug_level); + } + + if (target_hostname) { + state->target_hostname = strdup(target_hostname); + if (!state->target_hostname) { + return -1; + } + } + + if (target_username) { + char *puser = strdup(target_username); + char *p = NULL; + + if ((p = strchr(puser,'%'))) { + size_t len; + *p = 0; + libnetapi_set_username(ctx, puser); + libnetapi_set_password(ctx, p+1); + len = strlen(p+1); + memset(strchr(target_username,'%')+1,'X',len); + } else { + libnetapi_set_username(ctx, puser); + } + free(puser); + } + + { + char my_hostname[HOST_NAME_MAX]; + const char *p = NULL; + struct hostent *hp = NULL; + + if (gethostname(my_hostname, sizeof(my_hostname)) == -1) { + return -1; + } + + p = strchr(my_hostname, '.'); + if (p) { + my_hostname[strlen(my_hostname)-strlen(p)] = '\0'; + } + state->my_hostname = strdup(my_hostname); + if (!state->my_hostname) { + return -1; + } + debug("state->my_hostname: %s\n", state->my_hostname); + + hp = gethostbyname(my_hostname); + if (!hp || !hp->h_name || !*hp->h_name) { + return -1; + } + + state->my_fqdn = strdup(hp->h_name); + if (!state->my_fqdn) { + return -1; + } + debug("state->my_fqdn: %s\n", state->my_fqdn); + + p = strchr(state->my_fqdn, '.'); + if (p) { + p++; + state->my_dnsdomain = strdup(p); + } else { + state->my_dnsdomain = strdup(""); + } + if (!state->my_dnsdomain) { + return -1; + } + debug("state->my_dnsdomain: %s\n", state->my_dnsdomain); + } + + { + const char *buffer = NULL; + uint16_t type = 0; + status = NetGetJoinInformation(state->target_hostname, + &buffer, + &type); + if (status != 0) { + printf("NetGetJoinInformation failed with: %s\n", + libnetapi_get_error_string(state->ctx, status)); + return status; + } + debug("NetGetJoinInformation gave: %s and %d\n", buffer, type); + state->name_buffer_initial = strdup(buffer); + if (!state->name_buffer_initial) { + return -1; + } + state->name_type_initial = type; + NetApiBufferFree((void *)buffer); + } + + status = get_server_properties(state); + if (status != 0) { + return -1; + } + + state->uid = geteuid(); + + state->ctx = ctx; + + return 0; +} + +int main(int argc, char **argv) +{ + GOptionContext *context = NULL; + static const char *debug_level = NULL; + static const char *target_hostname = NULL; + static const char *target_username = NULL; + struct join_state *state = NULL; + GError *error = NULL; + int ret = 0; + + static GOptionEntry entries[] = { + { "debug", 'd', 0, G_OPTION_ARG_STRING, &debug_level, "Debug level (for samba)", "N" }, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Verbose output", 0 }, + { "target", 'S', 0, G_OPTION_ARG_STRING, &target_hostname, "Target hostname", 0 }, + { "username", 'U', 0, G_OPTION_ARG_STRING, &target_username, "Target hostname", 0 }, + { NULL } + }; + + context = g_option_context_new("- Samba domain join utility"); + g_option_context_add_main_entries(context, entries, NULL); +/* g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE); */ + g_option_context_add_group(context, gtk_get_option_group(TRUE)); + g_option_context_parse(context, &argc, &argv, &error); + + gtk_init(&argc, &argv); + g_set_application_name("Samba"); + + ret = init_join_state(&state); + if (ret) { + return ret; + } + + ret = initialize_join_state(state, debug_level, + target_hostname, + target_username); + if (ret) { + return ret; + } + + draw_main_window(state); + + gtk_main(); + + do_cleanup(state); + + return 0; +} diff --git a/source3/lib/netapi/examples/netdomjoin-gui/samba.ico b/source3/lib/netapi/examples/netdomjoin-gui/samba.ico Binary files differnew file mode 100755 index 0000000..b70c959 --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/samba.ico diff --git a/source3/lib/netapi/examples/netlogon/netlogon_control.c b/source3/lib/netapi/examples/netlogon/netlogon_control.c new file mode 100644 index 0000000..34361cd --- /dev/null +++ b/source3/lib/netapi/examples/netlogon/netlogon_control.c @@ -0,0 +1,143 @@ +/* + * Unix SMB/CIFS implementation. + * I_NetLogonControl query + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t function_code = NETLOGON_CONTROL_QUERY; + uint32_t level = 1; + uint8_t *buffer = NULL; + struct NETLOGON_INFO_1 *i1 = NULL; + struct NETLOGON_INFO_2 *i2 = NULL; + struct NETLOGON_INFO_3 *i3 = NULL; + struct NETLOGON_INFO_4 *i4 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("netlogon_control", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + function_code = atoi(poptGetArg(pc)); + } + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* I_NetLogonControl */ + + status = I_NetLogonControl(hostname, + function_code, + level, + &buffer); + if (status != 0) { + printf("I_NetLogonControl failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + if (!buffer) { + goto out; + } + + switch (level) { + case 1: + i1 = (struct NETLOGON_INFO_1 *)buffer; + + printf("Flags: %x\n", i1->netlog1_flags); + printf("Connection Status Status = %d 0x%x %s\n", + i1->netlog1_pdc_connection_status, + i1->netlog1_pdc_connection_status, + libnetapi_errstr(i1->netlog1_pdc_connection_status)); + + break; + case 2: + i2 = (struct NETLOGON_INFO_2 *)buffer; + + printf("Flags: %x\n", i2->netlog2_flags); + printf("Trusted DC Name %s\n", i2->netlog2_trusted_dc_name); + printf("Trusted DC Connection Status Status = %d 0x%x %s\n", + i2->netlog2_tc_connection_status, + i2->netlog2_tc_connection_status, + libnetapi_errstr(i2->netlog2_tc_connection_status)); + printf("Trust Verification Status Status = %d 0x%x %s\n", + i2->netlog2_pdc_connection_status, + i2->netlog2_pdc_connection_status, + libnetapi_errstr(i2->netlog2_pdc_connection_status)); + + break; + case 3: + i3 = (struct NETLOGON_INFO_3 *)buffer; + + printf("Flags: %x\n", i3->netlog1_flags); + printf("Logon Attempts: %d\n", i3->netlog3_logon_attempts); + + break; + case 4: + i4 = (struct NETLOGON_INFO_4 *)buffer; + + printf("Trusted DC Name %s\n", i4->netlog4_trusted_dc_name); + printf("Trusted Domain Name %s\n", i4->netlog4_trusted_domain_name); + + break; + default: + break; + } + + out: + NetApiBufferFree(buffer); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/netlogon/netlogon_control2.c b/source3/lib/netapi/examples/netlogon/netlogon_control2.c new file mode 100644 index 0000000..72315c9 --- /dev/null +++ b/source3/lib/netapi/examples/netlogon/netlogon_control2.c @@ -0,0 +1,147 @@ +/* + * Unix SMB/CIFS implementation. + * I_NetLogonControl2 query + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t function_code = NETLOGON_CONTROL_QUERY; + uint32_t level = 1; + uint8_t *buffer = NULL; + struct NETLOGON_INFO_1 *i1 = NULL; + struct NETLOGON_INFO_2 *i2 = NULL; + struct NETLOGON_INFO_3 *i3 = NULL; + struct NETLOGON_INFO_4 *i4 = NULL; + char *domain = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("netlogon_control", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + function_code = atoi(poptGetArg(pc)); + } + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + domain = strdup("TEST"); + + /* I_NetLogonControl2 */ + + status = I_NetLogonControl2(hostname, + function_code, + level, + (uint8_t *)domain, + &buffer); + if (status != 0) { + printf("I_NetLogonControl2 failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + if (!buffer) { + goto out; + } + + switch (level) { + case 1: + i1 = (struct NETLOGON_INFO_1 *)buffer; + + printf("Flags: %x\n", i1->netlog1_flags); + printf("Connection Status Status = %d 0x%x %s\n", + i1->netlog1_pdc_connection_status, + i1->netlog1_pdc_connection_status, + libnetapi_errstr(i1->netlog1_pdc_connection_status)); + + break; + case 2: + i2 = (struct NETLOGON_INFO_2 *)buffer; + + printf("Flags: %x\n", i2->netlog2_flags); + printf("Trusted DC Name %s\n", i2->netlog2_trusted_dc_name); + printf("Trusted DC Connection Status Status = %d 0x%x %s\n", + i2->netlog2_tc_connection_status, + i2->netlog2_tc_connection_status, + libnetapi_errstr(i2->netlog2_tc_connection_status)); + printf("Trust Verification Status Status = %d 0x%x %s\n", + i2->netlog2_pdc_connection_status, + i2->netlog2_pdc_connection_status, + libnetapi_errstr(i2->netlog2_pdc_connection_status)); + + break; + case 3: + i3 = (struct NETLOGON_INFO_3 *)buffer; + + printf("Flags: %x\n", i3->netlog1_flags); + printf("Logon Attempts: %d\n", i3->netlog3_logon_attempts); + + break; + case 4: + i4 = (struct NETLOGON_INFO_4 *)buffer; + + printf("Trusted DC Name %s\n", i4->netlog4_trusted_dc_name); + printf("Trusted Domain Name %s\n", i4->netlog4_trusted_domain_name); + + break; + default: + break; + } + + out: + NetApiBufferFree(buffer); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/netlogon/nltest.c b/source3/lib/netapi/examples/netlogon/nltest.c new file mode 100644 index 0000000..2dd368f --- /dev/null +++ b/source3/lib/netapi/examples/netlogon/nltest.c @@ -0,0 +1,677 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Nltest netlogon testing tool + * + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +enum { + OPT_SERVER = 1, + OPT_DBFLAG, + OPT_SC_QUERY, + OPT_SC_RESET, + OPT_SC_VERIFY, + OPT_SC_CHANGE_PWD, + OPT_DSGETDC, + OPT_PDC, + OPT_DS, + OPT_DSP, + OPT_GC, + OPT_KDC, + OPT_TIMESERV, + OPT_GTIMESERV, + OPT_WS, + OPT_NETBIOS, + OPT_DNS, + OPT_IP, + OPT_FORCE, + OPT_WRITABLE, + OPT_AVOIDSELF, + OPT_LDAPONLY, + OPT_BACKG, + OPT_DS_6, + OPT_TRY_NEXT_CLOSEST_SITE, + OPT_SITE, + OPT_ACCOUNT, + OPT_RET_DNS, + OPT_RET_NETBIOS, + OPT_DSREGDNS +}; + +/**************************************************************** +****************************************************************/ + +static void print_netlogon_info_result(uint32_t level, + uint8_t *buffer) +{ + struct NETLOGON_INFO_1 *i1 = NULL; + struct NETLOGON_INFO_2 *i2 = NULL; + struct NETLOGON_INFO_3 *i3 = NULL; + struct NETLOGON_INFO_4 *i4 = NULL; + + if (!buffer) { + return; + } + + switch (level) { + case 1: + i1 = (struct NETLOGON_INFO_1 *)buffer; + + printf("Flags: %x\n", i1->netlog1_flags); + printf("Connection Status Status = %d 0x%x %s\n", + i1->netlog1_pdc_connection_status, + i1->netlog1_pdc_connection_status, + libnetapi_errstr(i1->netlog1_pdc_connection_status)); + + break; + case 2: + i2 = (struct NETLOGON_INFO_2 *)buffer; + + printf("Flags: %x\n", i2->netlog2_flags); + printf("Trusted DC Name %s\n", i2->netlog2_trusted_dc_name); + printf("Trusted DC Connection Status Status = %d 0x%x %s\n", + i2->netlog2_tc_connection_status, + i2->netlog2_tc_connection_status, + libnetapi_errstr(i2->netlog2_tc_connection_status)); + printf("Trust Verification Status Status = %d 0x%x %s\n", + i2->netlog2_pdc_connection_status, + i2->netlog2_pdc_connection_status, + libnetapi_errstr(i2->netlog2_pdc_connection_status)); + + break; + case 3: + i3 = (struct NETLOGON_INFO_3 *)buffer; + + printf("Flags: %x\n", i3->netlog1_flags); + printf("Logon Attempts: %d\n", i3->netlog3_logon_attempts); + + break; + case 4: + i4 = (struct NETLOGON_INFO_4 *)buffer; + + printf("Trusted DC Name %s\n", i4->netlog4_trusted_dc_name); + printf("Trusted Domain Name %s\n", i4->netlog4_trusted_domain_name); + + break; + default: + break; + } +} + +/**************************************************************** +****************************************************************/ + +static void print_dc_info_flags(uint32_t flags) +{ + if (flags & DS_PDC_FLAG) + printf("PDC "); + if (flags & DS_GC_FLAG) + printf("GC "); + if (flags & DS_DS_FLAG) + printf("DS "); + if (flags & DS_LDAP_FLAG) + printf("LDAP "); + if (flags & DS_KDC_FLAG) + printf("KDC "); + if (flags & DS_TIMESERV_FLAG) + printf("TIMESERV "); + if (flags & DS_GOOD_TIMESERV_FLAG) + printf("GTIMESERV "); + if (flags & DS_WRITABLE_FLAG) + printf("WRITABLE "); + if (flags & DS_DNS_FOREST_FLAG) + printf("DNS_FOREST "); + if (flags & DS_CLOSEST_FLAG) + printf("CLOSE_SITE "); + if (flags & DS_FULL_SECRET_DOMAIN_6_FLAG) + printf("FULL_SECRET "); + if (flags & DS_WS_FLAG) + printf("WS "); + if (flags & DS_DS_8_FLAG) + printf("DS_8 "); + printf("\n"); +} + +/**************************************************************** +****************************************************************/ + +static void print_dc_info(struct DOMAIN_CONTROLLER_INFO *dc_info) +{ + if (dc_info->flags) { + printf(" DC: %s\n", dc_info->domain_controller_name); + printf(" Address: %s\n", dc_info->domain_controller_address); +/* printf(" Dom Guid: %s\n", X(domain_guid)); */ + printf(" Dom Name: %s\n", dc_info->domain_name); + printf(" Forest Name: %s\n", dc_info->dns_forest_name); + printf(" Dc Site Name: %s\n", dc_info->dc_site_name); + printf("Our Site Name: %s\n", dc_info->client_site_name); + printf(" Flags: "); + print_dc_info_flags(dc_info->flags); + } else { + printf(" DC: %s\n", dc_info->domain_controller_name); + printf(" Address: %s\n", dc_info->domain_controller_address); + printf(" Dom Name: %s\n", dc_info->domain_name); + } +} + +/**************************************************************** +****************************************************************/ + +int main(int argc, const char **argv) +{ + int opt; + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + char *opt_server = NULL; + char *opt_domain = NULL; + int opt_dbflag = 0; + int opt_pdc = 0; + int opt_ds = 0; + int opt_dsp = 0; + int opt_gc = 0; + int opt_kdc = 0; + int opt_timeserv = 0; + int opt_gtimeserv = 0; + int opt_ws = 0; + int opt_netbios = 0; + int opt_dns = 0; + int opt_ip = 0; + int opt_force = 0; + int opt_writable = 0; + int opt_avoidself = 0; + int opt_ldaponly = 0; + int opt_backg = 0; + int opt_ds_6 = 0; + int opt_try_next_closest_site = 0; + char *opt_site = NULL; + char *opt_account = NULL; + int opt_ret_dns = 0; + int opt_ret_netbios = 0; + int opt_dsregdns = 0; + uint32_t query_level = 0; + uint8_t *buffer = NULL; + uint32_t flags = 0; + struct DOMAIN_CONTROLLER_INFO *dc_info = NULL; + + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "server", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_server, + .val = OPT_SERVER, + .descrip = "Servername", + .argDescrip = "SERVER", + }, + { + .longName = "dbflag", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &opt_dbflag, + .val = OPT_DBFLAG, + .descrip = "New Debug Flag", + .argDescrip = "HEXFLAGS", + }, + { + .longName = "sc_query", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_domain, + .val = OPT_SC_QUERY, + .descrip = "Query secure channel for domain on server", + .argDescrip = "DOMAIN", + }, + { + .longName = "sc_reset", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_domain, + .val = OPT_SC_RESET, + .descrip = "Reset secure channel for domain on server to dcname", + .argDescrip = "DOMAIN", + }, + { + .longName = "sc_verify", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_domain, + .val = OPT_SC_VERIFY, + .descrip = "Verify secure channel for domain on server", + .argDescrip = "DOMAIN", + }, + { + .longName = "sc_change_pwd", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_domain, + .val = OPT_SC_CHANGE_PWD, + .descrip = "Change a secure channel password for domain on server", + .argDescrip = "DOMAIN", + }, + { + .longName = "dsgetdc", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_domain, + .val = OPT_DSGETDC, + .descrip = "Call DsGetDcName", + .argDescrip = "DOMAIN", + }, + { + .longName = "pdc", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_pdc, + .val = OPT_PDC, + .descrip = NULL, + }, + { + .longName = "ds", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ds, + .val = OPT_DS, + .descrip = NULL, + }, + { + .longName = "dsp", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_dsp, + .val = OPT_DSP, + .descrip = NULL, + }, + { + .longName = "gc", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_gc, + .val = OPT_GC, + .descrip = NULL, + }, + { + .longName = "kdc", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_kdc, + .val = OPT_KDC, + .descrip = NULL, + }, + { + .longName = "timeserv", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_timeserv, + .val = OPT_TIMESERV, + .descrip = NULL, + }, + { + .longName = "gtimeserv", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_gtimeserv, + .val = OPT_GTIMESERV, + .descrip = NULL, + }, + { + .longName = "ws", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ws, + .val = OPT_WS, + .descrip = NULL, + }, + { + .longName = "netbios", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_netbios, + .val = OPT_NETBIOS, + .descrip = NULL, + }, + { + .longName = "dns", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_dns, + .val = OPT_DNS, + .descrip = NULL, + }, + { + .longName = "ip", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ip, + .val = OPT_IP, + .descrip = NULL, + }, + { + .longName = "force", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_force, + .val = OPT_FORCE, + .descrip = NULL, + }, + { + .longName = "writable", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_writable, + .val = OPT_WRITABLE, + .descrip = NULL, + }, + { + .longName = "avoidself", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_avoidself, + .val = OPT_AVOIDSELF, + .descrip = NULL, + }, + { + .longName = "ldaponly", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ldaponly, + .val = OPT_LDAPONLY, + .descrip = NULL, + }, + { + .longName = "backg", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_backg, + .val = OPT_BACKG, + .descrip = NULL, + }, + { + .longName = "ds_6", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ds_6, + .val = OPT_DS_6, + .descrip = NULL, + }, + { + .longName = "try_next_closest_site", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_try_next_closest_site, + .val = OPT_TRY_NEXT_CLOSEST_SITE, + .descrip = NULL, + }, + { + .longName = "site", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_site, + .val = OPT_SITE, + .descrip = "SITE", + }, + { + .longName = "account", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_account, + .val = OPT_ACCOUNT, + .descrip = "ACCOUNT", + }, + { + .longName = "ret_dns", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ret_dns, + .val = OPT_RET_DNS, + .descrip = NULL, + }, + { + .longName = "ret_netbios", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_ret_netbios, + .val = OPT_RET_NETBIOS, + .descrip = NULL, + }, + { + .longName = "dsregdns", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_dsregdns, + .val = OPT_DSREGDNS, + .descrip = "Force registration of all DC-specific DNS records", + }, + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("nltest", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "<options>"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (argc == 1) { + poptPrintHelp(pc, stderr, 0); + goto done; + } + + poptResetContext(pc); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + + case OPT_SERVER: + + if ((opt_server[0] == '/' && opt_server[1] == '/') || + (opt_server[0] == '\\' && opt_server[1] == '\\')) { + opt_server += 2; + } + + break; + + case OPT_DBFLAG: + query_level = 1; + status = I_NetLogonControl2(opt_server, + NETLOGON_CONTROL_SET_DBFLAG, + query_level, + (uint8_t *)&opt_dbflag, + &buffer); + if (status != 0) { + fprintf(stderr, "I_NetlogonControl failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_netlogon_info_result(query_level, buffer); + + break; + case OPT_SC_QUERY: + query_level = 2; + status = I_NetLogonControl2(opt_server, + NETLOGON_CONTROL_TC_QUERY, + query_level, + (uint8_t *)opt_domain, + &buffer); + if (status != 0) { + fprintf(stderr, "I_NetlogonControl failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_netlogon_info_result(query_level, buffer); + + break; + case OPT_SC_VERIFY: + query_level = 2; + status = I_NetLogonControl2(opt_server, + NETLOGON_CONTROL_TC_VERIFY, + query_level, + (uint8_t *)opt_domain, + &buffer); + if (status != 0) { + fprintf(stderr, "I_NetlogonControl failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_netlogon_info_result(query_level, buffer); + + break; + case OPT_SC_RESET: + query_level = 2; + status = I_NetLogonControl2(opt_server, + NETLOGON_CONTROL_REDISCOVER, + query_level, + (uint8_t *)opt_domain, + &buffer); + if (status != 0) { + fprintf(stderr, "I_NetlogonControl failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_netlogon_info_result(query_level, buffer); + + break; + case OPT_SC_CHANGE_PWD: + query_level = 1; + status = I_NetLogonControl2(opt_server, + NETLOGON_CONTROL_CHANGE_PASSWORD, + query_level, + (uint8_t *)opt_domain, + &buffer); + if (status != 0) { + fprintf(stderr, "I_NetlogonControl failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_netlogon_info_result(query_level, buffer); + + break; + case OPT_DSREGDNS: + query_level = 1; + status = I_NetLogonControl2(opt_server, + NETLOGON_CONTROL_FORCE_DNS_REG, + query_level, + NULL, + &buffer); + if (status != 0) { + fprintf(stderr, "I_NetlogonControl failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_netlogon_info_result(query_level, buffer); + + break; + case OPT_DSGETDC: + if (opt_pdc) + flags |= DS_PDC_REQUIRED; + if (opt_ds) + flags |= DS_DIRECTORY_SERVICE_REQUIRED; + if (opt_dsp) + flags |= DS_DIRECTORY_SERVICE_PREFERRED; + if (opt_kdc) + flags |= DS_KDC_REQUIRED; + if (opt_timeserv) + flags |= DS_TIMESERV_REQUIRED; + if (opt_gtimeserv) + flags |= DS_GOOD_TIMESERV_PREFERRED; + if (opt_ws) + flags |= DS_WEB_SERVICE_REQUIRED; + if (opt_netbios) + flags |= DS_IS_FLAT_NAME; + if (opt_dns) + flags |= DS_IS_DNS_NAME; + if (opt_ip) + flags |= DS_IP_REQUIRED; + if (opt_force) + flags |= DS_FORCE_REDISCOVERY; + if (opt_writable) + flags |= DS_WRITABLE_REQUIRED; + if (opt_avoidself) + flags |= DS_AVOID_SELF; + if (opt_ldaponly) + flags |= DS_ONLY_LDAP_NEEDED; + if (opt_backg) + flags |= DS_BACKGROUND_ONLY; + if (opt_ds_6) + flags |= DS_DIRECTORY_SERVICE_6_REQUIRED; + if (opt_try_next_closest_site) + flags |= DS_TRY_NEXTCLOSEST_SITE; + if (opt_ret_dns) + flags |= DS_RETURN_DNS_NAME; + if (opt_ret_netbios) + flags |= DS_RETURN_FLAT_NAME; + + status = DsGetDcName(opt_server, + opt_domain, + NULL, /* domain_guid */ + opt_site, + flags, + &dc_info); + if (status != 0) { + fprintf(stderr, "DsGetDcName failed: Status = %d 0x%x %s\n", + status, status, + libnetapi_get_error_string(ctx, status)); + goto done; + } + + print_dc_info(dc_info); + + break; + default: + continue; + } + } + + printf("The command completed successfully\n"); + status = 0; + + done: + + printf("\n"); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/server/remote_tod.c b/source3/lib/netapi/examples/server/remote_tod.c new file mode 100644 index 0000000..7636f6a --- /dev/null +++ b/source3/lib/netapi/examples/server/remote_tod.c @@ -0,0 +1,83 @@ +/* + * Unix SMB/CIFS implementation. + * NetRemoteTOD query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + struct TIME_OF_DAY_INFO *tod = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("tod", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + + /* NetRemoteTOD */ + + status = NetRemoteTOD(hostname, + (uint8_t **)&tod); + if (status != 0) { + printf("NetRemoteTOD failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } else { + printf("%d-%d-%d %d:%d:%d\n", + tod->tod_day, tod->tod_month, tod->tod_year, + tod->tod_hours, tod->tod_mins, tod->tod_secs); + NetApiBufferFree(tod); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/server/server_getinfo.c b/source3/lib/netapi/examples/server/server_getinfo.c new file mode 100644 index 0000000..7836faa --- /dev/null +++ b/source3/lib/netapi/examples/server/server_getinfo.c @@ -0,0 +1,136 @@ +/* + * Unix SMB/CIFS implementation. + * NetServerGetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint8_t *buffer = NULL; + uint32_t level = 100; + + struct SERVER_INFO_100 *i100; + struct SERVER_INFO_101 *i101; + struct SERVER_INFO_102 *i102; + struct SERVER_INFO_1005 *i1005; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("server_getinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetServerGetInfo */ + + status = NetServerGetInfo(hostname, + level, + &buffer); + if (status != 0) { + printf("NetServerGetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 100: + i100 = (struct SERVER_INFO_100 *)buffer; + printf("platform id: %d\n", i100->sv100_platform_id); + printf("name: %s\n", i100->sv100_name); + break; + case 101: + i101 = (struct SERVER_INFO_101 *)buffer; + printf("platform id: %d\n", i101->sv101_platform_id); + printf("name: %s\n", i101->sv101_name); + printf("version major: %d\n", i101->sv101_version_major); + printf("version minor: %d\n", i101->sv101_version_minor); + printf("type: 0x%08x\n", i101->sv101_type); + printf("comment: %s\n", i101->sv101_comment); + break; + case 102: + i102 = (struct SERVER_INFO_102 *)buffer; + printf("platform id: %d\n", i102->sv102_platform_id); + printf("name: %s\n", i102->sv102_name); + printf("version major: %d\n", i102->sv102_version_major); + printf("version minor: %d\n", i102->sv102_version_minor); + printf("type: 0x%08x\n", i102->sv102_type); + printf("comment: %s\n", i102->sv102_comment); + printf("users: %d\n", i102->sv102_users); + printf("disc: %d\n", i102->sv102_disc); + printf("hidden: %d\n", i102->sv102_hidden); + printf("announce: %d\n", i102->sv102_announce); + printf("anndelta: %d\n", i102->sv102_anndelta); + printf("licenses: %d\n", i102->sv102_licenses); + printf("userpath: %s\n", i102->sv102_userpath); + break; + case 402: + break; + case 403: + break; + case 502: + break; + case 503: + break; + case 1005: + i1005 = (struct SERVER_INFO_1005 *)buffer; + printf("comment: %s\n", i1005->sv1005_comment); + break; + default: + break; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/share/share_add.c b/source3/lib/netapi/examples/share/share_add.c new file mode 100644 index 0000000..760381a --- /dev/null +++ b/source3/lib/netapi/examples/share/share_add.c @@ -0,0 +1,105 @@ +/* + * Unix SMB/CIFS implementation. + * NetShareAdd query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *sharename = NULL; + const char *path = NULL; + uint32_t parm_err = 0; + + struct SHARE_INFO_2 i2; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("share_add", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname sharename path"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + sharename = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + path = poptGetArg(pc); + + /* NetShareAdd */ + + i2.shi2_netname = sharename; + i2.shi2_type = 0; + i2.shi2_remark = "Test share created via NetApi"; + i2.shi2_permissions = 0; + i2.shi2_max_uses = (uint32_t)-1; + i2.shi2_current_uses = 0; + i2.shi2_path = path; + i2.shi2_passwd = NULL; + + status = NetShareAdd(hostname, + 2, + (uint8_t *)&i2, + &parm_err); + if (status != 0) { + printf("NetShareAdd failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/share/share_del.c b/source3/lib/netapi/examples/share/share_del.c new file mode 100644 index 0000000..20e3ce5 --- /dev/null +++ b/source3/lib/netapi/examples/share/share_del.c @@ -0,0 +1,85 @@ +/* + * Unix SMB/CIFS implementation. + * NetShareDel query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *sharename = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("share_del", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname sharename"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + sharename = poptGetArg(pc); + + /* NetShareDel */ + + status = NetShareDel(hostname, + sharename, + 0); + if (status != 0) { + printf("NetShareDel failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/share/share_enum.c b/source3/lib/netapi/examples/share/share_enum.c new file mode 100644 index 0000000..b1f4043 --- /dev/null +++ b/source3/lib/netapi/examples/share/share_enum.c @@ -0,0 +1,142 @@ +/* + * Unix SMB/CIFS implementation. + * NetShareEnum query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int i; + + struct SHARE_INFO_0 *i0 = NULL; + struct SHARE_INFO_1 *i1 = NULL; + struct SHARE_INFO_2 *i2 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("share_enum", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetShareEnum */ + + do { + status = NetShareEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + printf("total entries: %d\n", total_entries); + switch (level) { + case 0: + i0 = (struct SHARE_INFO_0 *)buffer; + break; + case 1: + i1 = (struct SHARE_INFO_1 *)buffer; + break; + case 2: + i2 = (struct SHARE_INFO_2 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d netname: %s\n", i, i0->shi0_netname); + i0++; + break; + case 1: + printf("#%d netname: %s\n", i, i1->shi1_netname); + printf("#%d type: %d\n", i, i1->shi1_type); + printf("#%d remark: %s\n", i, i1->shi1_remark); + i1++; + break; + case 2: + printf("#%d netname: %s\n", i, i2->shi2_netname); + printf("#%d type: %d\n", i, i2->shi2_type); + printf("#%d remark: %s\n", i, i2->shi2_remark); + printf("#%d permissions: %d\n", i, i2->shi2_permissions); + printf("#%d max users: %d\n", i, i2->shi2_max_uses); + printf("#%d current users: %d\n", i, i2->shi2_current_uses); + printf("#%d path: %s\n", i, i2->shi2_path); + printf("#%d password: %s\n", i, i2->shi2_passwd); + i2++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetShareEnum failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/share/share_getinfo.c b/source3/lib/netapi/examples/share/share_getinfo.c new file mode 100644 index 0000000..479da5c --- /dev/null +++ b/source3/lib/netapi/examples/share/share_getinfo.c @@ -0,0 +1,152 @@ +/* + * Unix SMB/CIFS implementation. + * NetShareGetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *sharename = NULL; + uint32_t level = 2; + uint8_t *buffer = NULL; + + struct SHARE_INFO_0 *i0 = NULL; + struct SHARE_INFO_1 *i1 = NULL; + struct SHARE_INFO_2 *i2 = NULL; + struct SHARE_INFO_501 *i501 = NULL; + struct SHARE_INFO_1005 *i1005 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("share_getinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname sharename level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + sharename = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetShareGetInfo */ + + status = NetShareGetInfo(hostname, + sharename, + level, + &buffer); + if (status != 0) { + printf("NetShareGetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 0: + i0 = (struct SHARE_INFO_0 *)buffer; + break; + case 1: + i1 = (struct SHARE_INFO_1 *)buffer; + break; + case 2: + i2 = (struct SHARE_INFO_2 *)buffer; + break; + case 501: + i501 = (struct SHARE_INFO_501 *)buffer; + break; + case 1005: + i1005 = (struct SHARE_INFO_1005 *)buffer; + break; + + default: + break; + } + + switch (level) { + case 0: + printf("netname: %s\n", i0->shi0_netname); + break; + case 1: + printf("netname: %s\n", i1->shi1_netname); + printf("type: %d\n", i1->shi1_type); + printf("remark: %s\n", i1->shi1_remark); + break; + case 2: + printf("netname: %s\n", i2->shi2_netname); + printf("type: %d\n", i2->shi2_type); + printf("remark: %s\n", i2->shi2_remark); + printf("permissions: %d\n", i2->shi2_permissions); + printf("max users: %d\n", i2->shi2_max_uses); + printf("current users: %d\n", i2->shi2_current_uses); + printf("path: %s\n", i2->shi2_path); + printf("password: %s\n", i2->shi2_passwd); + break; + case 501: + printf("netname: %s\n", i501->shi501_netname); + printf("type: %d\n", i501->shi501_type); + printf("remark: %s\n", i501->shi501_remark); + printf("flags: %d\n", i501->shi501_flags); + break; + case 1005: + printf("flags: %d\n", i1005->shi1005_flags); + break; + default: + break; + } + NetApiBufferFree(buffer); + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/share/share_setinfo.c b/source3/lib/netapi/examples/share/share_setinfo.c new file mode 100644 index 0000000..f4748f4 --- /dev/null +++ b/source3/lib/netapi/examples/share/share_setinfo.c @@ -0,0 +1,105 @@ +/* + * Unix SMB/CIFS implementation. + * NetShareSetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *sharename = NULL; + const char *comment = "NetApi generated Share comment"; + uint32_t level = 1004; + uint8_t *buffer = NULL; + uint32_t parm_err = 0; + + struct SHARE_INFO_1004 i1004; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("share_setinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname sharename comment"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + sharename = poptGetArg(pc); + + if (poptPeekArg(pc)) { + comment = poptGetArg(pc); + } + + /* NetShareSetInfo */ + switch (level) { + case 1004: + i1004.shi1004_remark = comment; + buffer = (uint8_t *)&i1004; + break; + default: + break; + } + + status = NetShareSetInfo(hostname, + sharename, + level, + buffer, + &parm_err); + if (status != 0) { + printf("NetShareSetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/shutdown/shutdown_abort.c b/source3/lib/netapi/examples/shutdown/shutdown_abort.c new file mode 100644 index 0000000..8f24a7a --- /dev/null +++ b/source3/lib/netapi/examples/shutdown/shutdown_abort.c @@ -0,0 +1,76 @@ +/* + * Unix SMB/CIFS implementation. + * NetShutdownAbort query + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("shutdown_abort", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + /* NetShutdownAbort */ + + status = NetShutdownAbort(hostname); + if (status != 0) { + printf("NetShutdownAbort failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/shutdown/shutdown_init.c b/source3/lib/netapi/examples/shutdown/shutdown_init.c new file mode 100644 index 0000000..73d23bb --- /dev/null +++ b/source3/lib/netapi/examples/shutdown/shutdown_init.c @@ -0,0 +1,94 @@ +/* + * Unix SMB/CIFS implementation. + * NetShutdownInit query + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *message = NULL; + uint32_t timeout = 30; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("shutdown_init", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname message timeout"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + message = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + timeout = atoi(poptGetArg(pc)); + + /* NetShutdownInit */ + + status = NetShutdownInit(hostname, + message, + timeout, + 1, /* close apps */ + 1); /* reboot */ + if (status != 0) { + printf("NetShutdownInit failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_add.c b/source3/lib/netapi/examples/user/user_add.c new file mode 100644 index 0000000..5452f77 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_add.c @@ -0,0 +1,103 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserAdd query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + const char *password = NULL; + struct USER_INFO_1 info1; + uint32_t parm_error = 0; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_add", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username password"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + password = poptGetArg(pc); + + /* NetUserAdd */ + + info1.usri1_name = username; + info1.usri1_password = password; + info1.usri1_password_age = 0; + info1.usri1_priv = 0; + info1.usri1_home_dir = NULL; + info1.usri1_comment = "User created using Samba NetApi Example code"; + info1.usri1_flags = 0; + info1.usri1_script_path = NULL; + + status = NetUserAdd(hostname, + 1, + (uint8_t *)&info1, + &parm_error); + if (status != 0) { + printf("NetUserAdd failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_chgpwd.c b/source3/lib/netapi/examples/user/user_chgpwd.c new file mode 100644 index 0000000..8b37ec2 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_chgpwd.c @@ -0,0 +1,99 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserChangePassword query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + const char *old_password = NULL; + const char *new_password = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_chgpwd", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username old_password new_password"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + old_password = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + new_password = poptGetArg(pc); + + /* NetUserChangePassword */ + + status = NetUserChangePassword(hostname, + username, + old_password, + new_password); + if (status != 0) { + printf("NetUserChangePassword failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_del.c b/source3/lib/netapi/examples/user/user_del.c new file mode 100644 index 0000000..9cf28a9 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_del.c @@ -0,0 +1,82 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserDel query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_del", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + /* NetUserDel */ + + status = NetUserDel(hostname, username); + if (status != 0) { + printf("NetUserDel failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_dispinfo.c b/source3/lib/netapi/examples/user/user_dispinfo.c new file mode 100644 index 0000000..23024fe --- /dev/null +++ b/source3/lib/netapi/examples/user/user_dispinfo.c @@ -0,0 +1,100 @@ +/* + * Unix SMB/CIFS implementation. + * NetQueryDisplayInformation query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + void *buffer = NULL; + uint32_t entries_read = 0; + uint32_t idx = 0; + int i; + + struct NET_DISPLAY_USER *user; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_dispinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + /* NetQueryDisplayInformation */ + + do { + status = NetQueryDisplayInformation(hostname, + 1, + idx, + 1000, + (uint32_t)-1, + &entries_read, + &buffer); + if (status == 0 || status == ERROR_MORE_DATA) { + user = (struct NET_DISPLAY_USER *)buffer; + for (i=0; i<entries_read; i++) { + printf("user %d: %s\n", i + idx, + user->usri1_name); + user++; + } + NetApiBufferFree(buffer); + } + idx += entries_read; + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetQueryDisplayInformation failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_enum.c b/source3/lib/netapi/examples/user/user_enum.c new file mode 100644 index 0000000..cf77bf2 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_enum.c @@ -0,0 +1,157 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserEnum query + * Copyright (C) Guenther Deschner 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + char *sid_str = NULL; + int i; + + struct USER_INFO_0 *info0 = NULL; + struct USER_INFO_10 *info10 = NULL; + struct USER_INFO_20 *info20 = NULL; + struct USER_INFO_23 *info23 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_enum", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetUserEnum */ + + do { + status = NetUserEnum(hostname, + level, + FILTER_NORMAL_ACCOUNT, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + + switch (level) { + case 0: + info0 = (struct USER_INFO_0 *)buffer; + break; + case 10: + info10 = (struct USER_INFO_10 *)buffer; + break; + case 20: + info20 = (struct USER_INFO_20 *)buffer; + break; + case 23: + info23 = (struct USER_INFO_23 *)buffer; + break; + default: + break; + } + + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d user: %s\n", i, info0->usri0_name); + info0++; + break; + case 10: + printf("#%d user: %s\n", i, info10->usri10_name); + printf("#%d comment: %s\n", i, info10->usri10_comment); + printf("#%d usr_comment: %s\n", i, info10->usri10_usr_comment); + printf("#%d full_name: %s\n", i, info10->usri10_full_name); + info10++; + break; + case 20: + printf("#%d user: %s\n", i, info20->usri20_name); + printf("#%d comment: %s\n", i, info20->usri20_comment); + printf("#%d flags: 0x%08x\n", i, info20->usri20_flags); + printf("#%d rid: %d\n", i, info20->usri20_user_id); + info20++; + break; + case 23: + printf("#%d user: %s\n", i, info23->usri23_name); + printf("#%d comment: %s\n", i, info23->usri23_comment); + printf("#%d flags: 0x%08x\n", i, info23->usri23_flags); + if (ConvertSidToStringSid(info23->usri23_user_sid, + &sid_str)) { + printf("#%d sid: %s\n", i, sid_str); + free(sid_str); + } + info23++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetUserEnum failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_getgroups.c b/source3/lib/netapi/examples/user/user_getgroups.c new file mode 100644 index 0000000..939415e --- /dev/null +++ b/source3/lib/netapi/examples/user/user_getgroups.c @@ -0,0 +1,133 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserGetGroups query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + int i; + + struct GROUP_USERS_INFO_0 *info0 = NULL; + struct GROUP_USERS_INFO_1 *info1 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_getgroups", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetUserGetGroups */ + + do { + status = NetUserGetGroups(hostname, + username, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries); + if (status == 0 || status == ERROR_MORE_DATA) { + + switch (level) { + case 0: + info0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + break; + } + + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d group: %s\n", i, info0->grui0_name); + info0++; + break; + case 1: + printf("#%d group: %s\n", i, info1->grui1_name); + printf("#%d attributes: %d\n", i, info1->grui1_attributes); + info1++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetUserGetGroups failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_getinfo.c b/source3/lib/netapi/examples/user/user_getinfo.c new file mode 100644 index 0000000..9e95260 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_getinfo.c @@ -0,0 +1,293 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserGetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + char *sid_str = NULL; + int i; + + struct USER_INFO_0 *u0; + struct USER_INFO_1 *u1; + struct USER_INFO_2 *u2; + struct USER_INFO_3 *u3; + struct USER_INFO_4 *u4; + struct USER_INFO_10 *u10; + struct USER_INFO_11 *u11; + struct USER_INFO_20 *u20; + struct USER_INFO_23 *u23; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_getinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetUserGetInfo */ + + status = NetUserGetInfo(hostname, + username, + level, + &buffer); + if (status != 0) { + printf("NetUserGetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 0: + u0 = (struct USER_INFO_0 *)buffer; + printf("name: %s\n", u0->usri0_name); + break; + case 1: + u1 = (struct USER_INFO_1 *)buffer; + printf("name: %s\n", u1->usri1_name); + printf("password: %s\n", u1->usri1_password); + printf("password_age: %d\n", u1->usri1_password_age); + printf("priv: %d\n", u1->usri1_priv); + printf("homedir: %s\n", u1->usri1_home_dir); + printf("comment: %s\n", u1->usri1_comment); + printf("flags: 0x%08x\n", u1->usri1_flags); + printf("script: %s\n", u1->usri1_script_path); + break; + case 2: + u2 = (struct USER_INFO_2 *)buffer; + printf("name: %s\n", u2->usri2_name); + printf("password: %s\n", u2->usri2_password); + printf("password_age: %d\n", u2->usri2_password_age); + printf("priv: %d\n", u2->usri2_priv); + printf("homedir: %s\n", u2->usri2_home_dir); + printf("comment: %s\n", u2->usri2_comment); + printf("flags: 0x%08x\n", u2->usri2_flags); + printf("script: %s\n", u2->usri2_script_path); + printf("auth flags: 0x%08x\n", u2->usri2_auth_flags); + printf("full name: %s\n", u2->usri2_full_name); + printf("user comment: %s\n", u2->usri2_usr_comment); + printf("user parameters: %s\n", u2->usri2_parms); + printf("workstations: %s\n", u2->usri2_workstations); + printf("last logon (seconds since jan. 1, 1970 GMT): %d\n", + u2->usri2_last_logon); + printf("last logoff (seconds since jan. 1, 1970 GMT): %d\n", + u2->usri2_last_logoff); + printf("account expires (seconds since jan. 1, 1970 GMT): %d\n", + u2->usri2_acct_expires); + printf("max storage: %d\n", u2->usri2_max_storage); + printf("units per week: %d\n", u2->usri2_units_per_week); + printf("logon hours:"); + for (i=0; i<21; i++) { + printf(" %x", (uint8_t)u2->usri2_logon_hours[i]); + } + printf("\n"); + printf("bad password count: %d\n", u2->usri2_bad_pw_count); + printf("logon count: %d\n", u2->usri2_num_logons); + printf("logon server: %s\n", u2->usri2_logon_server); + printf("country code: %d\n", u2->usri2_country_code); + printf("code page: %d\n", u2->usri2_code_page); + break; + case 3: + u3 = (struct USER_INFO_3 *)buffer; + printf("name: %s\n", u3->usri3_name); + printf("password_age: %d\n", u3->usri3_password_age); + printf("priv: %d\n", u3->usri3_priv); + printf("homedir: %s\n", u3->usri3_home_dir); + printf("comment: %s\n", u3->usri3_comment); + printf("flags: 0x%08x\n", u3->usri3_flags); + printf("script: %s\n", u3->usri3_script_path); + printf("auth flags: 0x%08x\n", u3->usri3_auth_flags); + printf("full name: %s\n", u3->usri3_full_name); + printf("user comment: %s\n", u3->usri3_usr_comment); + printf("user parameters: %s\n", u3->usri3_parms); + printf("workstations: %s\n", u3->usri3_workstations); + printf("last logon (seconds since jan. 1, 1970 GMT): %d\n", + u3->usri3_last_logon); + printf("last logoff (seconds since jan. 1, 1970 GMT): %d\n", + u3->usri3_last_logoff); + printf("account expires (seconds since jan. 1, 1970 GMT): %d\n", + u3->usri3_acct_expires); + printf("max storage: %d\n", u3->usri3_max_storage); + printf("units per week: %d\n", u3->usri3_units_per_week); + printf("logon hours:"); + for (i=0; i<21; i++) { + printf(" %x", (uint8_t)u3->usri3_logon_hours[i]); + } + printf("\n"); + printf("bad password count: %d\n", u3->usri3_bad_pw_count); + printf("logon count: %d\n", u3->usri3_num_logons); + printf("logon server: %s\n", u3->usri3_logon_server); + printf("country code: %d\n", u3->usri3_country_code); + printf("code page: %d\n", u3->usri3_code_page); + printf("user id: %d\n", u3->usri3_user_id); + printf("primary group id: %d\n", u3->usri3_primary_group_id); + printf("profile: %s\n", u3->usri3_profile); + printf("home dir drive: %s\n", u3->usri3_home_dir_drive); + printf("password expired: %d\n", u3->usri3_password_expired); + break; + case 4: + u4 = (struct USER_INFO_4 *)buffer; + printf("name: %s\n", u4->usri4_name); + printf("password: %s\n", u4->usri4_password); + printf("password_age: %d\n", u4->usri4_password_age); + printf("priv: %d\n", u4->usri4_priv); + printf("homedir: %s\n", u4->usri4_home_dir); + printf("comment: %s\n", u4->usri4_comment); + printf("flags: 0x%08x\n", u4->usri4_flags); + printf("script: %s\n", u4->usri4_script_path); + printf("auth flags: 0x%08x\n", u4->usri4_auth_flags); + printf("full name: %s\n", u4->usri4_full_name); + printf("user comment: %s\n", u4->usri4_usr_comment); + printf("user parameters: %s\n", u4->usri4_parms); + printf("workstations: %s\n", u4->usri4_workstations); + printf("last logon (seconds since jan. 1, 1970 GMT): %d\n", + u4->usri4_last_logon); + printf("last logoff (seconds since jan. 1, 1970 GMT): %d\n", + u4->usri4_last_logoff); + printf("account expires (seconds since jan. 1, 1970 GMT): %d\n", + u4->usri4_acct_expires); + printf("max storage: %d\n", u4->usri4_max_storage); + printf("units per week: %d\n", u4->usri4_units_per_week); + printf("logon hours:"); + for (i=0; i<21; i++) { + printf(" %x", (uint8_t)u4->usri4_logon_hours[i]); + } + printf("\n"); + printf("bad password count: %d\n", u4->usri4_bad_pw_count); + printf("logon count: %d\n", u4->usri4_num_logons); + printf("logon server: %s\n", u4->usri4_logon_server); + printf("country code: %d\n", u4->usri4_country_code); + printf("code page: %d\n", u4->usri4_code_page); + if (ConvertSidToStringSid(u4->usri4_user_sid, + &sid_str)) { + printf("user_sid: %s\n", sid_str); + free(sid_str); + } + printf("primary group id: %d\n", u4->usri4_primary_group_id); + printf("profile: %s\n", u4->usri4_profile); + printf("home dir drive: %s\n", u4->usri4_home_dir_drive); + printf("password expired: %d\n", u4->usri4_password_expired); + break; + case 10: + u10 = (struct USER_INFO_10 *)buffer; + printf("name: %s\n", u10->usri10_name); + printf("comment: %s\n", u10->usri10_comment); + printf("usr_comment: %s\n", u10->usri10_usr_comment); + printf("full_name: %s\n", u10->usri10_full_name); + break; + case 11: + u11 = (struct USER_INFO_11 *)buffer; + printf("name: %s\n", u11->usri11_name); + printf("comment: %s\n", u11->usri11_comment); + printf("user comment: %s\n", u11->usri11_usr_comment); + printf("full name: %s\n", u11->usri11_full_name); + printf("priv: %d\n", u11->usri11_priv); + printf("auth flags: 0x%08x\n", u11->usri11_auth_flags); + printf("password_age: %d\n", u11->usri11_password_age); + printf("homedir: %s\n", u11->usri11_home_dir); + printf("user parameters: %s\n", u11->usri11_parms); + printf("last logon (seconds since jan. 1, 1970 GMT): %d\n", + u11->usri11_last_logon); + printf("last logoff (seconds since jan. 1, 1970 GMT): %d\n", + u11->usri11_last_logoff); + printf("bad password count: %d\n", u11->usri11_bad_pw_count); + printf("logon count: %d\n", u11->usri11_num_logons); + printf("logon server: %s\n", u11->usri11_logon_server); + printf("country code: %d\n", u11->usri11_country_code); + printf("workstations: %s\n", u11->usri11_workstations); + printf("max storage: %d\n", u11->usri11_max_storage); + printf("units per week: %d\n", u11->usri11_units_per_week); + printf("logon hours:"); + for (i=0; i<21; i++) { + printf(" %x", (uint8_t)u11->usri11_logon_hours[i]); + } + printf("\n"); + printf("code page: %d\n", u11->usri11_code_page); + break; + case 20: + u20 = (struct USER_INFO_20 *)buffer; + printf("name: %s\n", u20->usri20_name); + printf("comment: %s\n", u20->usri20_comment); + printf("flags: 0x%08x\n", u20->usri20_flags); + printf("rid: %d\n", u20->usri20_user_id); + break; + case 23: + u23 = (struct USER_INFO_23 *)buffer; + printf("name: %s\n", u23->usri23_name); + printf("comment: %s\n", u23->usri23_comment); + printf("flags: 0x%08x\n", u23->usri23_flags); + if (ConvertSidToStringSid(u23->usri23_user_sid, + &sid_str)) { + printf("user_sid: %s\n", sid_str); + free(sid_str); + } + break; + default: + break; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_getlocalgroups.c b/source3/lib/netapi/examples/user/user_getlocalgroups.c new file mode 100644 index 0000000..133104d --- /dev/null +++ b/source3/lib/netapi/examples/user/user_getlocalgroups.c @@ -0,0 +1,122 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserGetLocalGroups query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t flags = 0; + int i; + + struct LOCALGROUP_USERS_INFO_0 *info0 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_getlocalgroups", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + /* NetUserGetLocalGroups */ + + do { + status = NetUserGetLocalGroups(hostname, + username, + level, + flags, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries); + if (status == 0 || status == ERROR_MORE_DATA) { + + switch (level) { + case 0: + info0 = (struct LOCALGROUP_USERS_INFO_0 *)buffer; + break; + default: + break; + } + + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + printf("#%d group: %s\n", i, info0->lgrui0_name); + info0++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status != 0) { + printf("NetUserGetLocalGroups failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_modalsget.c b/source3/lib/netapi/examples/user/user_modalsget.c new file mode 100644 index 0000000..4dcb41b --- /dev/null +++ b/source3/lib/netapi/examples/user/user_modalsget.c @@ -0,0 +1,131 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserModalsGet query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + char *sid_str = NULL; + + struct USER_MODALS_INFO_0 *u0; + struct USER_MODALS_INFO_1 *u1; + struct USER_MODALS_INFO_2 *u2; + struct USER_MODALS_INFO_3 *u3; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_modalsget", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + /* NetUserModalsGet */ + + status = NetUserModalsGet(hostname, + level, + &buffer); + if (status != 0) { + printf("NetUserModalsGet failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + switch (level) { + case 0: + u0 = (struct USER_MODALS_INFO_0 *)buffer; + printf("min passwd len: %d character\n", + u0->usrmod0_min_passwd_len); + printf("max passwd age: %d (days)\n", + u0->usrmod0_max_passwd_age/86400); + printf("min passwd age: %d (days)\n", + u0->usrmod0_min_passwd_age/86400); + printf("force logoff: %d (seconds)\n", + u0->usrmod0_force_logoff); + printf("password history length: %d entries\n", + u0->usrmod0_password_hist_len); + break; + case 1: + u1 = (struct USER_MODALS_INFO_1 *)buffer; + printf("role: %d\n", u1->usrmod1_role); + printf("primary: %s\n", u1->usrmod1_primary); + break; + case 2: + u2 = (struct USER_MODALS_INFO_2 *)buffer; + printf("domain name: %s\n", u2->usrmod2_domain_name); + if (ConvertSidToStringSid(u2->usrmod2_domain_id, + &sid_str)) { + printf("domain sid: %s\n", sid_str); + free(sid_str); + } + break; + case 3: + u3 = (struct USER_MODALS_INFO_3 *)buffer; + printf("lockout duration: %d (seconds)\n", + u3->usrmod3_lockout_duration); + printf("lockout observation window: %d (seconds)\n", + u3->usrmod3_lockout_observation_window); + printf("lockout threshold: %d entries\n", + u3->usrmod3_lockout_threshold); + break; + default: + break; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_modalsset.c b/source3/lib/netapi/examples/user/user_modalsset.c new file mode 100644 index 0000000..a5cdc44 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_modalsset.c @@ -0,0 +1,132 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserModalsSet query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + uint8_t *buffer = NULL; + uint32_t level = 0; + uint32_t parm_err = 0; + + struct USER_MODALS_INFO_0 u0; + struct USER_MODALS_INFO_1001 u1001; + struct USER_MODALS_INFO_1002 u1002; + struct USER_MODALS_INFO_1003 u1003; + struct USER_MODALS_INFO_1004 u1004; + struct USER_MODALS_INFO_1005 u1005; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_modalsset", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname level value"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (poptPeekArg(pc)) { + level = atoi(poptGetArg(pc)); + } + + switch (level) { + case 0: + u0.usrmod0_min_passwd_len = 0; + u0.usrmod0_max_passwd_age = (86400 * 30); /* once a month */ + u0.usrmod0_min_passwd_age = 0; + u0.usrmod0_force_logoff = TIMEQ_FOREVER; + u0.usrmod0_password_hist_len = 0; + buffer = (uint8_t *)&u0; + break; + case 1: + case 2: + case 3: + break; + case 1001: + u1001.usrmod1001_min_passwd_len = 0; + buffer = (uint8_t *)&u1001; + break; + case 1002: + u1002.usrmod1002_max_passwd_age = 0; + buffer = (uint8_t *)&u1002; + break; + case 1003: + u1003.usrmod1003_min_passwd_age = (86400 * 30); /* once a month */ + buffer = (uint8_t *)&u1003; + break; + case 1004: + u1004.usrmod1004_force_logoff = TIMEQ_FOREVER; + buffer = (uint8_t *)&u1004; + break; + case 1005: + u1005.usrmod1005_password_hist_len = 0; + buffer = (uint8_t *)&u1005; + break; + case 1006: + case 1007: + default: + break; + } + + /* NetUserModalsSet */ + + status = NetUserModalsSet(hostname, + level, + buffer, + &parm_err); + if (status != 0) { + printf("NetUserModalsSet failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_setgroups.c b/source3/lib/netapi/examples/user/user_setgroups.c new file mode 100644 index 0000000..de3ff22 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_setgroups.c @@ -0,0 +1,144 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserSetGroups query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + uint32_t level = 0; + uint8_t *buffer = NULL; + uint32_t num_entries = 0; + const char **names = NULL; + int i = 0; + size_t buf_size = 0; + + struct GROUP_USERS_INFO_0 *g0 = NULL; + struct GROUP_USERS_INFO_1 *g1 = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_setgroups", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username group1 group2 ..."); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + + names = poptGetArgs(pc); + for (i=0; names[i] != NULL; i++) { + num_entries++; + } + + switch (level) { + case 0: + buf_size = sizeof(struct GROUP_USERS_INFO_0) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g0); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<num_entries; i++) { + g0[i].grui0_name = names[i]; + } + + buffer = (uint8_t *)g0; + break; + case 1: + buf_size = sizeof(struct GROUP_USERS_INFO_1) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g1); + if (status) { + printf("NetApiBufferAllocate failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + for (i=0; i<num_entries; i++) { + g1[i].grui1_name = names[i]; + g1[i].grui1_attributes = 0; /* ? */ + } + + buffer = (uint8_t *)g1; + break; + default: + break; + } + + /* NetUserSetGroups */ + + status = NetUserSetGroups(hostname, + username, + level, + buffer, + num_entries); + if (status != 0) { + printf("NetUserSetGroups failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + out: + NetApiBufferFree(buffer); + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/user/user_setinfo.c b/source3/lib/netapi/examples/user/user_setinfo.c new file mode 100644 index 0000000..56c9822 --- /dev/null +++ b/source3/lib/netapi/examples/user/user_setinfo.c @@ -0,0 +1,200 @@ +/* + * Unix SMB/CIFS implementation. + * NetUserSetInfo query + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + const char *username = NULL; + uint32_t level = 0; + uint32_t parm_err = 0; + uint8_t *buffer = NULL; + const char *val = NULL; + + struct USER_INFO_0 u0; + struct USER_INFO_1003 u1003; + struct USER_INFO_1005 u1005; + struct USER_INFO_1006 u1006; + struct USER_INFO_1007 u1007; + struct USER_INFO_1008 u1008; + struct USER_INFO_1009 u1009; + struct USER_INFO_1010 u1010; + struct USER_INFO_1011 u1011; + struct USER_INFO_1012 u1012; + struct USER_INFO_1014 u1014; + struct USER_INFO_1017 u1017; + struct USER_INFO_1024 u1024; + struct USER_INFO_1051 u1051; + struct USER_INFO_1052 u1052; + struct USER_INFO_1053 u1053; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("user_setinfo", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname username level"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + username = poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + level = atoi(poptGetArg(pc)); + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + val = poptGetArg(pc); + + /* NetUserSetInfo */ + + switch (level) { + case 0: + u0.usri0_name = val; + buffer = (uint8_t *)&u0; + break; + case 1: + case 2: + case 3: + case 4: + break; + case 21: + break; + case 22: + break; + case 1003: + u1003.usri1003_password = val; + buffer = (uint8_t *)&u1003; + break; + case 1005: + u1005.usri1005_priv = atoi(val); + buffer = (uint8_t *)&u1005; + break; + case 1006: + u1006.usri1006_home_dir = val; + buffer = (uint8_t *)&u1006; + break; + case 1007: + u1007.usri1007_comment = val; + buffer = (uint8_t *)&u1007; + break; + case 1008: + u1008.usri1008_flags = atoi(val); + buffer = (uint8_t *)&u1008; + break; + case 1009: + u1009.usri1009_script_path = val; + buffer = (uint8_t *)&u1009; + break; + case 1010: + u1010.usri1010_auth_flags = atoi(val); + buffer = (uint8_t *)&u1010; + break; + case 1011: + u1011.usri1011_full_name = val; + buffer = (uint8_t *)&u1011; + break; + case 1012: + u1012.usri1012_usr_comment = val; + buffer = (uint8_t *)&u1012; + break; + case 1014: + u1014.usri1014_workstations = val; + buffer = (uint8_t *)&u1014; + break; + case 1017: + u1017.usri1017_acct_expires = atoi(val); + buffer = (uint8_t *)&u1017; + break; + case 1020: + break; + case 1024: + u1024.usri1024_country_code = atoi(val); + buffer = (uint8_t *)&u1024; + break; + case 1051: + u1051.usri1051_primary_group_id = atoi(val); + buffer = (uint8_t *)&u1051; + break; + case 1052: + u1052.usri1052_profile = val; + buffer = (uint8_t *)&u1052; + break; + case 1053: + u1053.usri1053_home_dir_drive = val; + buffer = (uint8_t *)&u1053; + break; + default: + break; + } + + status = NetUserSetInfo(hostname, + username, + level, + buffer, + &parm_err); + if (status != 0) { + printf("NetUserSetInfo failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + goto out; + } + + out: + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/examples/wscript_build b/source3/lib/netapi/examples/wscript_build new file mode 100644 index 0000000..1d56840 --- /dev/null +++ b/source3/lib/netapi/examples/wscript_build @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# The remaining task is to build the gtk example, but we first need to find the gtk libs +# netdomjoin-gui/netdomjoin-gui.c + +names = [ + ("getdc", "getdc"), + ("dsgetdc", "dsgetdc"), + ("join", "netdomjoin"), + ("join", "getjoinableous"), + ("join", "getjoininformation"), + ("join", "rename_machine"), + ("join", "provision_computer_account"), + ("join", "request_offline_domain_join"), + ("join", "djoin"), + ("user", "user_add"), + ("user", "user_del"), + ("user", "user_enum"), + ("user", "user_dispinfo"), + ("user", "user_chgpwd"), + ("user", "user_getinfo"), + ("user", "user_setinfo"), + ("user", "user_modalsget"), + ("user", "user_modalsset"), + ("user", "user_getgroups"), + ("user", "user_setgroups"), + ("user", "user_getlocalgroups"), + ("group", "group_add"), + ("group", "group_del"), + ("group", "group_enum"), + ("group", "group_setinfo"), + ("group", "group_getinfo"), + ("group", "group_adduser"), + ("group", "group_deluser"), + ("group", "group_getusers"), + ("group", "group_setusers"), + ("localgroup", "localgroup_add"), + ("localgroup", "localgroup_del"), + ("localgroup", "localgroup_getinfo"), + ("localgroup", "localgroup_setinfo"), + ("localgroup", "localgroup_enum"), + ("localgroup", "localgroup_addmembers"), + ("localgroup", "localgroup_delmembers"), + ("localgroup", "localgroup_setmembers"), + ("localgroup", "localgroup_getmembers"), + ("server", "remote_tod"), + ("server", "server_getinfo"), + ("share", "share_add"), + ("share", "share_del"), + ("share", "share_enum"), + ("share", "share_getinfo"), + ("share", "share_setinfo"), + ("file", "file_close"), + ("file", "file_getinfo"), + ("file", "file_enum"), + ("shutdown", "shutdown_init"), + ("shutdown", "shutdown_abort"), + ("netlogon", "netlogon_control"), + ("netlogon", "netlogon_control2"), + ("netlogon", "nltest")] + + +bld.SAMBA_SUBSYSTEM('LIBNETAPI_EXAMPLES_COMMON', + source='common.c', + deps='netapi popt') + +for pattern in names: + (subdir, name) = pattern + bld.SAMBA_BINARY('%s/%s' % (subdir, name), + source='%s/%s.c' % (subdir, name), + deps='netapi popt LIBNETAPI_EXAMPLES_COMMON', + install=False) diff --git a/source3/lib/netapi/file.c b/source3/lib/netapi/file.c new file mode 100644 index 0000000..411747a --- /dev/null +++ b/source3/lib/netapi/file.c @@ -0,0 +1,283 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi File Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "../librpc/gen_ndr/ndr_srvsvc_c.h" + +/**************************************************************** +****************************************************************/ + +WERROR NetFileClose_r(struct libnetapi_ctx *ctx, + struct NetFileClose *r) +{ + WERROR werr; + NTSTATUS status; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_srvsvc_NetFileClose(b, talloc_tos(), + r->in.server_name, + r->in.fileid, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetFileClose_l(struct libnetapi_ctx *ctx, + struct NetFileClose *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetFileClose); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS map_srvsvc_FileInfo_to_FILE_INFO_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + union srvsvc_NetFileInfo *info, + uint8_t **buffer, + uint32_t *num_entries) +{ + struct FILE_INFO_2 i2; + struct FILE_INFO_3 i3; + + switch (level) { + case 2: + i2.fi2_id = info->info2->fid; + + ADD_TO_ARRAY(mem_ctx, struct FILE_INFO_2, i2, + (struct FILE_INFO_2 **)buffer, + num_entries); + break; + case 3: + i3.fi3_id = info->info3->fid; + i3.fi3_permissions = info->info3->permissions; + i3.fi3_num_locks = info->info3->num_locks; + i3.fi3_pathname = talloc_strdup(mem_ctx, info->info3->path); + i3.fi3_username = talloc_strdup(mem_ctx, info->info3->user); + + NT_STATUS_HAVE_NO_MEMORY(i3.fi3_pathname); + NT_STATUS_HAVE_NO_MEMORY(i3.fi3_username); + + ADD_TO_ARRAY(mem_ctx, struct FILE_INFO_3, i3, + (struct FILE_INFO_3 **)buffer, + num_entries); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetFileGetInfo_r(struct libnetapi_ctx *ctx, + struct NetFileGetInfo *r) +{ + WERROR werr; + NTSTATUS status; + union srvsvc_NetFileInfo info; + uint32_t num_entries = 0; + struct dcerpc_binding_handle *b; + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 2: + case 3: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_srvsvc_NetFileGetInfo(b, talloc_tos(), + r->in.server_name, + r->in.fileid, + r->in.level, + &info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = map_srvsvc_FileInfo_to_FILE_INFO_buffer(ctx, + r->in.level, + &info, + r->out.buffer, + &num_entries); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetFileGetInfo_l(struct libnetapi_ctx *ctx, + struct NetFileGetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetFileGetInfo); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetFileEnum_r(struct libnetapi_ctx *ctx, + struct NetFileEnum *r) +{ + WERROR werr; + NTSTATUS status; + struct srvsvc_NetFileInfoCtr info_ctr; + struct srvsvc_NetFileCtr2 ctr2; + struct srvsvc_NetFileCtr3 ctr3; + uint32_t num_entries = 0; + uint32_t i; + struct dcerpc_binding_handle *b; + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 2: + case 3: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + ZERO_STRUCT(info_ctr); + + info_ctr.level = r->in.level; + switch (r->in.level) { + case 2: + ZERO_STRUCT(ctr2); + info_ctr.ctr.ctr2 = &ctr2; + break; + case 3: + ZERO_STRUCT(ctr3); + info_ctr.ctr.ctr3 = &ctr3; + break; + } + + status = dcerpc_srvsvc_NetFileEnum(b, talloc_tos(), + r->in.server_name, + r->in.base_path, + r->in.user_name, + &info_ctr, + r->in.prefmaxlen, + r->out.total_entries, + r->out.resume_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr) && !W_ERROR_EQUAL(werr, WERR_MORE_DATA)) { + goto done; + } + + for (i=0; i < info_ctr.ctr.ctr2->count; i++) { + union srvsvc_NetFileInfo _i = {0}; + switch (r->in.level) { + case 2: + _i.info2 = &info_ctr.ctr.ctr2->array[i]; + break; + case 3: + _i.info3 = &info_ctr.ctr.ctr3->array[i]; + break; + } + + status = map_srvsvc_FileInfo_to_FILE_INFO_buffer(ctx, + r->in.level, + &_i, + r->out.buffer, + &num_entries); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + if (r->out.entries_read) { + *r->out.entries_read = num_entries; + } + + if (r->out.total_entries) { + *r->out.total_entries = num_entries; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetFileEnum_l(struct libnetapi_ctx *ctx, + struct NetFileEnum *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetFileEnum); +} diff --git a/source3/lib/netapi/getdc.c b/source3/lib/netapi/getdc.c new file mode 100644 index 0000000..7e5fab3 --- /dev/null +++ b/source3/lib/netapi/getdc.c @@ -0,0 +1,212 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi GetDC Support + * Copyright (C) Guenther Deschner 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "../librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "libsmb/dsgetdcname.h" + +/******************************************************************** +********************************************************************/ + +WERROR NetGetDCName_l(struct libnetapi_ctx *ctx, + struct NetGetDCName *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGetDCName); +} + +/******************************************************************** +********************************************************************/ + +WERROR NetGetDCName_r(struct libnetapi_ctx *ctx, + struct NetGetDCName *r) +{ + NTSTATUS status; + WERROR werr; + struct dcerpc_binding_handle *b; + const char *dcname; + void *buffer; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_netlogon, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_netr_GetDcName(b, talloc_tos(), + r->in.server_name, + r->in.domain_name, + &dcname, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + if (NetApiBufferAllocate(strlen_m_term(dcname), &buffer)) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + memcpy(buffer, dcname, strlen_m_term(dcname)); + *r->out.buffer = buffer; + done: + + return werr; +} + +/******************************************************************** +********************************************************************/ + +WERROR NetGetAnyDCName_l(struct libnetapi_ctx *ctx, + struct NetGetAnyDCName *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGetAnyDCName); +} + +/******************************************************************** +********************************************************************/ + +WERROR NetGetAnyDCName_r(struct libnetapi_ctx *ctx, + struct NetGetAnyDCName *r) +{ + NTSTATUS status; + WERROR werr; + struct dcerpc_binding_handle *b; + const char *dcname; + void *buffer; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_netlogon, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_netr_GetAnyDCName(b, talloc_tos(), + r->in.server_name, + r->in.domain_name, + &dcname, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + if (NetApiBufferAllocate(strlen_m_term(dcname), &buffer)) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + memcpy(buffer, dcname, strlen_m_term(dcname)); + *r->out.buffer = buffer; + + done: + + return werr; + +} + +/******************************************************************** +********************************************************************/ + +WERROR DsGetDcName_l(struct libnetapi_ctx *ctx, + struct DsGetDcName *r) +{ + NTSTATUS status; + struct libnetapi_private_ctx *priv; + + priv = talloc_get_type_abort(ctx->private_data, + struct libnetapi_private_ctx); + + status = dsgetdcname(ctx, + priv->msg_ctx, + r->in.domain_name, + r->in.domain_guid, + r->in.site_name, + r->in.flags, + (struct netr_DsRGetDCNameInfo **)r->out.dc_info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(ctx, + "failed to find DC: %s", + get_friendly_nt_error_msg(status)); + } + + return ntstatus_to_werror(status); +} + +/******************************************************************** +********************************************************************/ + +WERROR DsGetDcName_r(struct libnetapi_ctx *ctx, + struct DsGetDcName *r) +{ + WERROR werr; + NTSTATUS status; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_netlogon, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_netr_DsRGetDCNameEx(b, + ctx, + r->in.server_name, + r->in.domain_name, + r->in.domain_guid, + r->in.site_name, + r->in.flags, + (struct netr_DsRGetDCNameInfo **)r->out.dc_info, + &werr); + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_netr_DsRGetDCName(b, + ctx, + r->in.server_name, + r->in.domain_name, + r->in.domain_guid, + NULL, + r->in.flags, + (struct netr_DsRGetDCNameInfo **)r->out.dc_info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} diff --git a/source3/lib/netapi/group.c b/source3/lib/netapi/group.c new file mode 100644 index 0000000..5258549 --- /dev/null +++ b/source3/lib/netapi/group.c @@ -0,0 +1,1848 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Group Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx, + struct NetGroupAdd *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_group_name; + struct dom_sid2 *domain_sid = NULL; + uint32_t rid = 0; + struct dcerpc_binding_handle *b = NULL; + + struct GROUP_INFO_0 *info0 = NULL; + struct GROUP_INFO_1 *info1 = NULL; + struct GROUP_INFO_2 *info2 = NULL; + struct GROUP_INFO_3 *info3 = NULL; + union samr_GroupInfo info; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + info0 = (struct GROUP_INFO_0 *)r->in.buffer; + break; + case 1: + info1 = (struct GROUP_INFO_1 *)r->in.buffer; + break; + case 2: + info2 = (struct GROUP_INFO_2 *)r->in.buffer; + break; + case 3: + info3 = (struct GROUP_INFO_3 *)r->in.buffer; + break; + default: + werr = WERR_INVALID_LEVEL; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_CREATE_GROUP | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + switch (r->in.level) { + case 0: + init_lsa_String(&lsa_group_name, info0->grpi0_name); + break; + case 1: + init_lsa_String(&lsa_group_name, info1->grpi1_name); + break; + case 2: + init_lsa_String(&lsa_group_name, info2->grpi2_name); + break; + case 3: + init_lsa_String(&lsa_group_name, info3->grpi3_name); + break; + } + + status = dcerpc_samr_CreateDomainGroup(b, talloc_tos(), + &domain_handle, + &lsa_group_name, + SEC_STD_DELETE | + SAMR_GROUP_ACCESS_SET_INFO, + &group_handle, + &rid, + &result); + + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + switch (r->in.level) { + case 1: + if (info1->grpi1_comment) { + init_lsa_String(&info.description, + info1->grpi1_comment); + + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + } + break; + case 2: + if (info2->grpi2_comment) { + init_lsa_String(&info.description, + info2->grpi2_comment); + + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + if (any_nt_status_not_ok( + status, result, &status)) { + werr = ntstatus_to_werror(status); + goto failed; + } + } + + if (info2->grpi2_attributes != 0) { + info.attributes.attributes = info2->grpi2_attributes; + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &info, + &result); + + } + break; + case 3: + if (info3->grpi3_comment) { + init_lsa_String(&info.description, + info3->grpi3_comment); + + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + if (any_nt_status_not_ok( + status, result, &status)) { + werr = ntstatus_to_werror(status); + goto failed; + } + } + + if (info3->grpi3_attributes != 0) { + info.attributes.attributes = info3->grpi3_attributes; + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &info, + &result); + } + break; + default: + break; + } + + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto failed; + } + + werr = WERR_OK; + goto done; + + failed: + dcerpc_samr_DeleteDomainGroup(b, talloc_tos(), + &group_handle, &result); + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupAdd_l(struct libnetapi_ctx *ctx, + struct NetGroupAdd *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupAdd); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupDel_r(struct libnetapi_ctx *ctx, + struct NetGroupDel *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_group_name; + struct dom_sid2 *domain_sid = NULL; + int i = 0; + struct dcerpc_binding_handle *b = NULL; + + struct samr_Ids rids; + struct samr_Ids types; + union samr_GroupInfo *info = NULL; + struct samr_RidAttrArray *rid_array = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_group_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_group_name, + &rids, + &types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_DOM_GRP) { + werr = WERR_INVALID_DATATYPE; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SEC_STD_DELETE | + SAMR_GROUP_ACCESS_GET_MEMBERS | + SAMR_GROUP_ACCESS_REMOVE_MEMBER | + SAMR_GROUP_ACCESS_ADD_MEMBER | + SAMR_GROUP_ACCESS_LOOKUP_INFO, + rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_QueryGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + +#if 0 + /* breaks against NT4 */ + if (!(info->attributes.attributes & SE_GROUP_ENABLED)) { + werr = WERR_ACCESS_DENIED; + goto done; + } +#endif + status = dcerpc_samr_QueryGroupMember(b, talloc_tos(), + &group_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + { + struct lsa_Strings names; + struct samr_Ids member_types; + + status = dcerpc_samr_LookupRids(b, talloc_tos(), + &domain_handle, + rid_array->count, + rid_array->rids, + &names, + &member_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (names.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (member_types.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + } + + for (i=0; i < rid_array->count; i++) { + + status = dcerpc_samr_DeleteGroupMember(b, talloc_tos(), + &group_handle, + rid_array->rids[i], + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + status = dcerpc_samr_DeleteDomainGroup(b, talloc_tos(), + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + ZERO_STRUCT(group_handle); + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupDel_l(struct libnetapi_ctx *ctx, + struct NetGroupDel *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupDel); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx, + struct NetGroupSetInfo *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_group_name; + struct dom_sid2 *domain_sid = NULL; + struct dcerpc_binding_handle *b = NULL; + + struct samr_Ids rids; + struct samr_Ids types; + union samr_GroupInfo info; + struct GROUP_INFO_0 *g0; + struct GROUP_INFO_1 *g1; + struct GROUP_INFO_2 *g2; + struct GROUP_INFO_3 *g3; + struct GROUP_INFO_1002 *g1002; + struct GROUP_INFO_1005 *g1005; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_group_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_group_name, + &rids, + &types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_DOM_GRP) { + werr = WERR_INVALID_DATATYPE; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_SET_INFO | + SAMR_GROUP_ACCESS_LOOKUP_INFO, + rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + switch (r->in.level) { + case 0: + g0 = (struct GROUP_INFO_0 *)r->in.buffer; + init_lsa_String(&info.name, g0->grpi0_name); + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFONAME, + &info, + &result); + break; + case 1: + g1 = (struct GROUP_INFO_1 *)r->in.buffer; + init_lsa_String(&info.description, g1->grpi1_comment); + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + break; + case 2: + g2 = (struct GROUP_INFO_2 *)r->in.buffer; + init_lsa_String(&info.description, g2->grpi2_comment); + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + info.attributes.attributes = g2->grpi2_attributes; + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &info, + &result); + break; + case 3: + g3 = (struct GROUP_INFO_3 *)r->in.buffer; + init_lsa_String(&info.description, g3->grpi3_comment); + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + info.attributes.attributes = g3->grpi3_attributes; + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &info, + &result); + break; + case 1002: + g1002 = (struct GROUP_INFO_1002 *)r->in.buffer; + init_lsa_String(&info.description, g1002->grpi1002_comment); + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFODESCRIPTION, + &info, + &result); + break; + case 1005: + g1005 = (struct GROUP_INFO_1005 *)r->in.buffer; + info.attributes.attributes = g1005->grpi1005_attributes; + status = dcerpc_samr_SetGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &info, + &result); + break; + default: + status = NT_STATUS_INVALID_LEVEL; + break; + } + + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupSetInfo_l(struct libnetapi_ctx *ctx, + struct NetGroupSetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupSetInfo); +} + +/**************************************************************** +****************************************************************/ + +static WERROR map_group_info_to_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + struct samr_GroupInfoAll *info, + struct dom_sid2 *domain_sid, + uint32_t rid, + uint8_t **buffer) +{ + struct GROUP_INFO_0 info0; + struct GROUP_INFO_1 info1; + struct GROUP_INFO_2 info2; + struct GROUP_INFO_3 info3; + struct dom_sid sid; + + switch (level) { + case 0: + info0.grpi0_name = info->name.string; + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info0, sizeof(info0)); + + break; + case 1: + info1.grpi1_name = info->name.string; + info1.grpi1_comment = info->description.string; + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info1, sizeof(info1)); + + break; + case 2: + info2.grpi2_name = info->name.string; + info2.grpi2_comment = info->description.string; + info2.grpi2_group_id = rid; + info2.grpi2_attributes = info->attributes; + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info2, sizeof(info2)); + + break; + case 3: + if (!sid_compose(&sid, domain_sid, rid)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + info3.grpi3_name = info->name.string; + info3.grpi3_comment = info->description.string; + info3.grpi3_attributes = info->attributes; + info3.grpi3_group_sid = (struct domsid *)dom_sid_dup(mem_ctx, &sid); + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info3, sizeof(info3)); + + break; + default: + return WERR_INVALID_LEVEL; + } + + W_ERROR_HAVE_NO_MEMORY(*buffer); + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx, + struct NetGroupGetInfo *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_group_name; + struct dom_sid2 *domain_sid = NULL; + struct dcerpc_binding_handle *b = NULL; + + struct samr_Ids rids; + struct samr_Ids types; + union samr_GroupInfo *info = NULL; + bool group_info_all = false; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_group_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_group_name, + &rids, + &types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_DOM_GRP) { + werr = WERR_INVALID_DATATYPE; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_LOOKUP_INFO, + rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_QueryGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOALL2, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS)) { + status = dcerpc_samr_QueryGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOALL, + &info, + &result); + group_info_all = true; + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + if (!NT_STATUS_IS_OK(result)) { + werr = ntstatus_to_werror(result); + goto done; + } + + werr = map_group_info_to_buffer(ctx, r->in.level, + group_info_all ? &info->all : &info->all2, + domain_sid, rids.ids[0], + r->out.buffer); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupGetInfo_l(struct libnetapi_ctx *ctx, + struct NetGroupGetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupGetInfo); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx, + struct NetGroupAddUser *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_group_name, lsa_user_name; + struct dom_sid2 *domain_sid = NULL; + struct dcerpc_binding_handle *b = NULL; + + struct samr_Ids rids; + struct samr_Ids types; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_group_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_group_name, + &rids, + &types, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + werr = WERR_NERR_GROUPNOTFOUND; + goto done; + } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_DOM_GRP) { + werr = WERR_NERR_GROUPNOTFOUND; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_ADD_MEMBER, + rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + init_lsa_String(&lsa_user_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_user_name, + &rids, + &types, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + werr = WERR_NERR_USERNOTFOUND; + goto done; + } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_USER) { + werr = WERR_NERR_USERNOTFOUND; + goto done; + } + + status = dcerpc_samr_AddGroupMember(b, talloc_tos(), + &group_handle, + rids.ids[0], + 7, /* why ? */ + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupAddUser_l(struct libnetapi_ctx *ctx, + struct NetGroupAddUser *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupAddUser); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx, + struct NetGroupDelUser *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_group_name, lsa_user_name; + struct dom_sid2 *domain_sid = NULL; + struct dcerpc_binding_handle *b = NULL; + + struct samr_Ids rids; + struct samr_Ids types; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_group_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_group_name, + &rids, + &types, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + werr = WERR_NERR_GROUPNOTFOUND; + goto done; + } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_DOM_GRP) { + werr = WERR_NERR_GROUPNOTFOUND; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_REMOVE_MEMBER, + rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + init_lsa_String(&lsa_user_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_user_name, + &rids, + &types, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + werr = WERR_NERR_USERNOTFOUND; + goto done; + } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + if (types.ids[0] != SID_NAME_USER) { + werr = WERR_NERR_USERNOTFOUND; + goto done; + } + + status = dcerpc_samr_DeleteGroupMember(b, talloc_tos(), + &group_handle, + rids.ids[0], + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupDelUser_l(struct libnetapi_ctx *ctx, + struct NetGroupDelUser *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupDelUser); +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_disp_groups_to_GROUP_INFO_0_buffer(TALLOC_CTX *mem_ctx, + struct samr_DispInfoFullGroups *groups, + uint8_t **buffer) +{ + struct GROUP_INFO_0 *g0; + int i; + + g0 = talloc_zero_array(mem_ctx, struct GROUP_INFO_0, groups->count); + W_ERROR_HAVE_NO_MEMORY(g0); + + for (i=0; i<groups->count; i++) { + g0[i].grpi0_name = talloc_strdup(mem_ctx, + groups->entries[i].account_name.string); + W_ERROR_HAVE_NO_MEMORY(g0[i].grpi0_name); + } + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, g0, + sizeof(struct GROUP_INFO_0) * groups->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_disp_groups_to_GROUP_INFO_1_buffer(TALLOC_CTX *mem_ctx, + struct samr_DispInfoFullGroups *groups, + uint8_t **buffer) +{ + struct GROUP_INFO_1 *g1; + int i; + + g1 = talloc_zero_array(mem_ctx, struct GROUP_INFO_1, groups->count); + W_ERROR_HAVE_NO_MEMORY(g1); + + for (i=0; i<groups->count; i++) { + g1[i].grpi1_name = talloc_strdup(mem_ctx, + groups->entries[i].account_name.string); + g1[i].grpi1_comment = talloc_strdup(mem_ctx, + groups->entries[i].description.string); + W_ERROR_HAVE_NO_MEMORY(g1[i].grpi1_name); + } + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, g1, + sizeof(struct GROUP_INFO_1) * groups->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_disp_groups_to_GROUP_INFO_2_buffer(TALLOC_CTX *mem_ctx, + struct samr_DispInfoFullGroups *groups, + uint8_t **buffer) +{ + struct GROUP_INFO_2 *g2; + int i; + + g2 = talloc_zero_array(mem_ctx, struct GROUP_INFO_2, groups->count); + W_ERROR_HAVE_NO_MEMORY(g2); + + for (i=0; i<groups->count; i++) { + g2[i].grpi2_name = talloc_strdup(mem_ctx, + groups->entries[i].account_name.string); + g2[i].grpi2_comment = talloc_strdup(mem_ctx, + groups->entries[i].description.string); + g2[i].grpi2_group_id = groups->entries[i].rid; + g2[i].grpi2_attributes = groups->entries[i].acct_flags; + W_ERROR_HAVE_NO_MEMORY(g2[i].grpi2_name); + } + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, g2, + sizeof(struct GROUP_INFO_2) * groups->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_disp_groups_to_GROUP_INFO_3_buffer(TALLOC_CTX *mem_ctx, + struct samr_DispInfoFullGroups *groups, + const struct dom_sid *domain_sid, + uint8_t **buffer) +{ + struct GROUP_INFO_3 *g3; + int i; + + g3 = talloc_zero_array(mem_ctx, struct GROUP_INFO_3, groups->count); + W_ERROR_HAVE_NO_MEMORY(g3); + + for (i=0; i<groups->count; i++) { + + struct dom_sid sid; + + if (!sid_compose(&sid, domain_sid, groups->entries[i].rid)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + g3[i].grpi3_name = talloc_strdup(mem_ctx, + groups->entries[i].account_name.string); + g3[i].grpi3_comment = talloc_strdup(mem_ctx, + groups->entries[i].description.string); + g3[i].grpi3_group_sid = (struct domsid *)dom_sid_dup(mem_ctx, &sid); + g3[i].grpi3_attributes = groups->entries[i].acct_flags; + W_ERROR_HAVE_NO_MEMORY(g3[i].grpi3_name); + } + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, g3, + sizeof(struct GROUP_INFO_3) * groups->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_disp_groups_to_GROUP_INFO_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + struct samr_DispInfoFullGroups *groups, + const struct dom_sid *domain_sid, + uint32_t *entries_read, + uint8_t **buffer) +{ + if (entries_read) { + *entries_read = groups->count; + } + + switch (level) { + case 0: + return convert_samr_disp_groups_to_GROUP_INFO_0_buffer(mem_ctx, groups, buffer); + case 1: + return convert_samr_disp_groups_to_GROUP_INFO_1_buffer(mem_ctx, groups, buffer); + case 2: + return convert_samr_disp_groups_to_GROUP_INFO_2_buffer(mem_ctx, groups, buffer); + case 3: + return convert_samr_disp_groups_to_GROUP_INFO_3_buffer(mem_ctx, groups, domain_sid, buffer); + default: + return WERR_INVALID_LEVEL; + } +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupEnum_r(struct libnetapi_ctx *ctx, + struct NetGroupEnum *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle; + struct dom_sid2 *domain_sid = NULL; + struct policy_handle domain_handle; + union samr_DispInfo info; + union samr_DomainInfo *domain_info = NULL; + struct dcerpc_binding_handle *b = NULL; + + uint32_t total_size = 0; + uint32_t returned_size = 0; + + NTSTATUS result = NT_STATUS_OK; + NTSTATUS status; + WERROR werr, tmp_werr; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + + switch (r->in.level) { + case 0: + case 1: + case 2: + case 3: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_samr_QueryDomainInfo(b, talloc_tos(), + &domain_handle, + 2, + &domain_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (r->out.total_entries) { + *r->out.total_entries = domain_info->general.num_groups; + } + + status = dcerpc_samr_QueryDisplayInfo2(b, + ctx, + &domain_handle, + 3, + r->in.resume_handle ? + *r->in.resume_handle : 0, + (uint32_t)-1, + r->in.prefmaxlen, + &total_size, + &returned_size, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = ntstatus_to_werror(result); + if (NT_STATUS_IS_ERR(result)) { + goto done; + } + + if (r->out.resume_handle && info.info3.count > 0) { + *r->out.resume_handle = + info.info3.entries[info.info3.count-1].idx; + } + + tmp_werr = convert_samr_disp_groups_to_GROUP_INFO_buffer(ctx, + r->in.level, + &info.info3, + domain_sid, + r->out.entries_read, + r->out.buffer); + if (!W_ERROR_IS_OK(tmp_werr)) { + werr = tmp_werr; + goto done; + } + + done: + /* if last query */ + if (NT_STATUS_IS_OK(result) || + NT_STATUS_IS_ERR(result)) { + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupEnum_l(struct libnetapi_ctx *ctx, + struct NetGroupEnum *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupEnum); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupGetUsers_r(struct libnetapi_ctx *ctx, + struct NetGroupGetUsers *r) +{ + /* FIXME: this call needs to cope with large replies */ + + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + struct samr_Ids group_rids, name_types; + struct samr_RidAttrArray *rid_array = NULL; + struct lsa_Strings names; + struct samr_Ids member_types; + struct dcerpc_binding_handle *b = NULL; + + int i; + uint32_t entries_read = 0; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + *r->out.buffer = NULL; + *r->out.entries_read = 0; + *r->out.total_entries = 0; + + switch (r->in.level) { + case 0: + case 1: + break; + default: + return WERR_INVALID_LEVEL; + } + + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &group_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (group_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_GET_MEMBERS, + group_rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_QueryGroupMember(b, talloc_tos(), + &group_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_LookupRids(b, talloc_tos(), + &domain_handle, + rid_array->count, + rid_array->rids, + &names, + &member_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (names.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (member_types.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + for (i=0; i < names.count; i++) { + + if (member_types.ids[i] != SID_NAME_USER) { + continue; + } + + status = add_GROUP_USERS_INFO_X_buffer(ctx, + r->in.level, + names.names[i].string, + 7, + r->out.buffer, + &entries_read); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + *r->out.entries_read = entries_read; + *r->out.total_entries = entries_read; + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupGetUsers_l(struct libnetapi_ctx *ctx, + struct NetGroupGetUsers *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupGetUsers); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupSetUsers_r(struct libnetapi_ctx *ctx, + struct NetGroupSetUsers *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle, domain_handle, group_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + union samr_GroupInfo *group_info = NULL; + struct samr_Ids user_rids, name_types; + struct samr_Ids group_rids, group_types; + struct samr_RidAttrArray *rid_array = NULL; + struct lsa_String *lsa_names = NULL; + struct dcerpc_binding_handle *b = NULL; + + uint32_t *add_rids = NULL; + uint32_t *del_rids = NULL; + size_t num_add_rids = 0; + size_t num_del_rids = 0; + + uint32_t *member_rids = NULL; + + struct GROUP_USERS_INFO_0 *i0 = NULL; + struct GROUP_USERS_INFO_1 *i1 = NULL; + + int i, k; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.group_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &group_rids, + &group_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (group_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (group_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_GET_MEMBERS | + SAMR_GROUP_ACCESS_ADD_MEMBER | + SAMR_GROUP_ACCESS_REMOVE_MEMBER | + SAMR_GROUP_ACCESS_LOOKUP_INFO, + group_rids.ids[0], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_QueryGroupInfo(b, talloc_tos(), + &group_handle, + GROUPINFOATTRIBUTES, + &group_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + switch (r->in.level) { + case 0: + i0 = (struct GROUP_USERS_INFO_0 *)r->in.buffer; + break; + case 1: + i1 = (struct GROUP_USERS_INFO_1 *)r->in.buffer; + break; + } + + lsa_names = talloc_array(ctx, struct lsa_String, r->in.num_entries); + if (!lsa_names) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + for (i=0; i < r->in.num_entries; i++) { + + switch (r->in.level) { + case 0: + init_lsa_String(&lsa_names[i], i0->grui0_name); + i0++; + break; + case 1: + init_lsa_String(&lsa_names[i], i1->grui1_name); + i1++; + break; + } + } + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + r->in.num_entries, + lsa_names, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (r->in.num_entries != user_rids.count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (r->in.num_entries != name_types.count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + member_rids = user_rids.ids; + + status = dcerpc_samr_QueryGroupMember(b, talloc_tos(), + &group_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + /* add list */ + + for (i=0; i < r->in.num_entries; i++) { + bool already_member = false; + for (k=0; k < rid_array->count; k++) { + if (member_rids[i] == rid_array->rids[k]) { + already_member = true; + break; + } + } + if (!already_member) { + if (!add_rid_to_array_unique(ctx, + member_rids[i], + &add_rids, &num_add_rids)) { + werr = WERR_GEN_FAILURE; + goto done; + } + } + } + + /* del list */ + + for (k=0; k < rid_array->count; k++) { + bool keep_member = false; + for (i=0; i < r->in.num_entries; i++) { + if (member_rids[i] == rid_array->rids[k]) { + keep_member = true; + break; + } + } + if (!keep_member) { + if (!add_rid_to_array_unique(ctx, + rid_array->rids[k], + &del_rids, &num_del_rids)) { + werr = WERR_GEN_FAILURE; + goto done; + } + } + } + + /* add list */ + + for (i=0; i < num_add_rids; i++) { + status = dcerpc_samr_AddGroupMember(b, talloc_tos(), + &group_handle, + add_rids[i], + 7 /* ? */, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + /* del list */ + + for (i=0; i < num_del_rids; i++) { + status = dcerpc_samr_DeleteGroupMember(b, talloc_tos(), + &group_handle, + del_rids[i], + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGroupSetUsers_l(struct libnetapi_ctx *ctx, + struct NetGroupSetUsers *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupSetUsers); +} diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c new file mode 100644 index 0000000..04fc423 --- /dev/null +++ b/source3/lib/netapi/joindomain.c @@ -0,0 +1,1145 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Join Support + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "ads.h" +#include "librpc/gen_ndr/libnetapi.h" +#include "libcli/auth/libcli_auth.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "libnet/libnet_join.h" +#include "../librpc/gen_ndr/ndr_wkssvc_c.h" +#include "rpc_client/cli_pipe.h" +#include "secrets.h" +#include "libsmb/dsgetdcname.h" +#include "../librpc/gen_ndr/ndr_ODJ.h" +#include "lib/util/base64.h" +#include "libnet/libnet_join_offline.h" +#include "libcli/security/dom_sid.h" + +/**************************************************************** +****************************************************************/ + +WERROR NetJoinDomain_l(struct libnetapi_ctx *mem_ctx, + struct NetJoinDomain *r) +{ + struct libnet_JoinCtx *j = NULL; + struct libnetapi_private_ctx *priv; + WERROR werr; + + priv = talloc_get_type_abort(mem_ctx->private_data, + struct libnetapi_private_ctx); + + if (!r->in.domain) { + return WERR_INVALID_PARAMETER; + } + + werr = libnet_init_JoinCtx(mem_ctx, &j); + W_ERROR_NOT_OK_RETURN(werr); + + j->in.domain_name = talloc_strdup(mem_ctx, r->in.domain); + W_ERROR_HAVE_NO_MEMORY(j->in.domain_name); + + if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) { + NTSTATUS status; + struct netr_DsRGetDCNameInfo *info = NULL; + const char *dc = NULL; + uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | + DS_WRITABLE_REQUIRED | + DS_RETURN_DNS_NAME; + status = dsgetdcname(mem_ctx, priv->msg_ctx, r->in.domain, + NULL, NULL, flags, &info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(mem_ctx, + "%s", get_friendly_nt_error_msg(status)); + return ntstatus_to_werror(status); + } + + dc = strip_hostname(info->dc_unc); + j->in.dc_name = talloc_strdup(mem_ctx, dc); + W_ERROR_HAVE_NO_MEMORY(j->in.dc_name); + } + + if (r->in.account_ou) { + j->in.account_ou = talloc_strdup(mem_ctx, r->in.account_ou); + W_ERROR_HAVE_NO_MEMORY(j->in.account_ou); + } + + if (r->in.account) { + j->in.admin_account = talloc_strdup(mem_ctx, r->in.account); + W_ERROR_HAVE_NO_MEMORY(j->in.admin_account); + } + + if (r->in.password) { + j->in.admin_password = talloc_strdup(mem_ctx, r->in.password); + W_ERROR_HAVE_NO_MEMORY(j->in.admin_password); + } + + j->in.join_flags = r->in.join_flags; + j->in.modify_config = true; + j->in.debug = true; + + werr = libnet_Join(mem_ctx, j); + if (!W_ERROR_IS_OK(werr) && j->out.error_string) { + libnetapi_set_error_string(mem_ctx, "%s", j->out.error_string); + } + TALLOC_FREE(j); + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetJoinDomain_r(struct libnetapi_ctx *ctx, + struct NetJoinDomain *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; + NTSTATUS status; + WERROR werr; + unsigned int old_timeout = 0; + struct dcerpc_binding_handle *b; + DATA_BLOB session_key; + + if (IS_DC) { + return WERR_NERR_SETUPDOMAINCONTROLLER; + } + + werr = libnetapi_open_pipe(ctx, r->in.server, + &ndr_table_wkssvc, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + if (r->in.password) { + + status = cli_get_session_key(talloc_tos(), pipe_cli, &session_key); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = encode_wkssvc_join_password_buffer(ctx, + r->in.password, + &session_key, + &encrypted_password); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + } + + old_timeout = rpccli_set_timeout(pipe_cli, 600000); + + status = dcerpc_wkssvc_NetrJoinDomain2(b, talloc_tos(), + r->in.server, + r->in.domain, + r->in.account_ou, + r->in.account, + encrypted_password, + r->in.join_flags, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + if (pipe_cli && old_timeout) { + rpccli_set_timeout(pipe_cli, old_timeout); + } + + return werr; +} +/**************************************************************** +****************************************************************/ + +WERROR NetUnjoinDomain_l(struct libnetapi_ctx *mem_ctx, + struct NetUnjoinDomain *r) +{ + struct libnet_UnjoinCtx *u = NULL; + struct dom_sid domain_sid; + const char *domain = NULL; + WERROR werr; + struct libnetapi_private_ctx *priv; + const char *realm = lp_realm(); + + priv = talloc_get_type_abort(mem_ctx->private_data, + struct libnetapi_private_ctx); + + if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) { + return WERR_NERR_SETUPNOTJOINED; + } + + werr = libnet_init_UnjoinCtx(mem_ctx, &u); + W_ERROR_NOT_OK_RETURN(werr); + + if (realm[0] != '\0') { + domain = realm; + } else { + domain = lp_workgroup(); + } + + if (r->in.server_name) { + u->in.dc_name = talloc_strdup(mem_ctx, r->in.server_name); + W_ERROR_HAVE_NO_MEMORY(u->in.dc_name); + } else { + NTSTATUS status; + struct netr_DsRGetDCNameInfo *info = NULL; + const char *dc = NULL; + uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | + DS_WRITABLE_REQUIRED | + DS_RETURN_DNS_NAME; + status = dsgetdcname(mem_ctx, priv->msg_ctx, domain, + NULL, NULL, flags, &info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(mem_ctx, + "failed to find DC for domain %s: %s", + domain, + get_friendly_nt_error_msg(status)); + return ntstatus_to_werror(status); + } + + dc = strip_hostname(info->dc_unc); + u->in.dc_name = talloc_strdup(mem_ctx, dc); + W_ERROR_HAVE_NO_MEMORY(u->in.dc_name); + + u->in.domain_name = domain; + } + + if (r->in.account) { + u->in.admin_account = talloc_strdup(mem_ctx, r->in.account); + W_ERROR_HAVE_NO_MEMORY(u->in.admin_account); + } + + if (r->in.password) { + u->in.admin_password = talloc_strdup(mem_ctx, r->in.password); + W_ERROR_HAVE_NO_MEMORY(u->in.admin_password); + } + + u->in.domain_name = domain; + u->in.unjoin_flags = r->in.unjoin_flags; + u->in.delete_machine_account = false; + u->in.modify_config = true; + u->in.debug = true; + + u->in.domain_sid = &domain_sid; + + werr = libnet_Unjoin(mem_ctx, u); + if (!W_ERROR_IS_OK(werr) && u->out.error_string) { + libnetapi_set_error_string(mem_ctx, "%s", u->out.error_string); + } + TALLOC_FREE(u); + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUnjoinDomain_r(struct libnetapi_ctx *ctx, + struct NetUnjoinDomain *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; + NTSTATUS status; + WERROR werr; + unsigned int old_timeout = 0; + struct dcerpc_binding_handle *b; + DATA_BLOB session_key; + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_wkssvc, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + if (r->in.password) { + + status = cli_get_session_key(talloc_tos(), pipe_cli, &session_key); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = encode_wkssvc_join_password_buffer(ctx, + r->in.password, + &session_key, + &encrypted_password); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + } + + old_timeout = rpccli_set_timeout(pipe_cli, 60000); + + status = dcerpc_wkssvc_NetrUnjoinDomain2(b, talloc_tos(), + r->in.server_name, + r->in.account, + encrypted_password, + r->in.unjoin_flags, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + if (pipe_cli && old_timeout) { + rpccli_set_timeout(pipe_cli, old_timeout); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGetJoinInformation_r(struct libnetapi_ctx *ctx, + struct NetGetJoinInformation *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + const char *buffer = NULL; + struct dcerpc_binding_handle *b; + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_wkssvc, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + status = dcerpc_wkssvc_NetrGetJoinInformation(b, talloc_tos(), + r->in.server_name, + &buffer, + (enum wkssvc_NetJoinStatus *)r->out.name_type, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + *r->out.name_buffer = talloc_strdup(ctx, buffer); + W_ERROR_HAVE_NO_MEMORY(*r->out.name_buffer); + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGetJoinInformation_l(struct libnetapi_ctx *ctx, + struct NetGetJoinInformation *r) +{ + const char *realm = lp_realm(); + + if ((lp_security() == SEC_ADS) && realm[0] != '\0') { + *r->out.name_buffer = talloc_strdup(ctx, realm); + } else { + *r->out.name_buffer = talloc_strdup(ctx, lp_workgroup()); + } + if (!*r->out.name_buffer) { + return WERR_NOT_ENOUGH_MEMORY; + } + + switch (lp_server_role()) { + case ROLE_DOMAIN_MEMBER: + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + case ROLE_IPA_DC: + *r->out.name_type = NetSetupDomainName; + break; + case ROLE_STANDALONE: + default: + *r->out.name_type = NetSetupWorkgroupName; + break; + } + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGetJoinableOUs_l(struct libnetapi_ctx *ctx, + struct NetGetJoinableOUs *r) +{ +#ifdef HAVE_ADS + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + WERROR ret; + NTSTATUS status; + ADS_STATUS ads_status; + ADS_STRUCT *ads = NULL; + struct netr_DsRGetDCNameInfo *info = NULL; + const char *dc = NULL; + uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | + DS_RETURN_DNS_NAME; + struct libnetapi_private_ctx *priv; + char **p; + size_t s; + + priv = talloc_get_type_abort(ctx->private_data, + struct libnetapi_private_ctx); + + status = dsgetdcname(tmp_ctx, priv->msg_ctx, r->in.domain, + NULL, NULL, flags, &info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(ctx, "%s", + get_friendly_nt_error_msg(status)); + ret = ntstatus_to_werror(status); + goto out; + } + + dc = strip_hostname(info->dc_unc); + + ads = ads_init(tmp_ctx, + info->domain_name, + info->domain_name, + dc, + ADS_SASL_PLAIN); + if (!ads) { + ret = WERR_GEN_FAILURE; + goto out; + } + + ADS_TALLOC_CONST_FREE(ads->auth.user_name); + if (r->in.account) { + ads->auth.user_name = talloc_strdup(ads, r->in.account); + if (ads->auth.user_name == NULL) { + ret = WERR_NOT_ENOUGH_MEMORY; + goto out; + } + } else { + const char *username = NULL; + + libnetapi_get_username(ctx, &username); + if (username != NULL) { + ads->auth.user_name = talloc_strdup(ads, username); + if (ads->auth.user_name == NULL) { + ret = WERR_NOT_ENOUGH_MEMORY; + goto out; + } + } + } + + ADS_TALLOC_CONST_FREE(ads->auth.password); + if (r->in.password) { + ads->auth.password = talloc_strdup(ads, r->in.password); + if (ads->auth.password == NULL) { + ret = WERR_NOT_ENOUGH_MEMORY; + goto out; + } + } else { + const char *password = NULL; + + libnetapi_get_password(ctx, &password); + if (password != NULL) { + ads->auth.password = talloc_strdup(ads, password); + if (ads->auth.password == NULL) { + ret = WERR_NOT_ENOUGH_MEMORY; + goto out; + } + } + } + + ads_status = ads_connect_user_creds(ads); + if (!ADS_ERR_OK(ads_status)) { + ret = WERR_NERR_DEFAULTJOINREQUIRED; + goto out; + } + + ads_status = ads_get_joinable_ous(ads, ctx, &p, &s); + if (!ADS_ERR_OK(ads_status)) { + ret = WERR_NERR_DEFAULTJOINREQUIRED; + goto out; + } + *r->out.ous = discard_const_p(const char *, p); + *r->out.ou_count = s; + + ret = WERR_OK; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +#else + return WERR_NOT_SUPPORTED; +#endif +} + +/**************************************************************** +****************************************************************/ + +WERROR NetGetJoinableOUs_r(struct libnetapi_ctx *ctx, + struct NetGetJoinableOUs *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; + NTSTATUS status; + WERROR werr; + struct dcerpc_binding_handle *b; + DATA_BLOB session_key; + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_wkssvc, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + if (r->in.password) { + + status = cli_get_session_key(talloc_tos(), pipe_cli, &session_key); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = encode_wkssvc_join_password_buffer(ctx, + r->in.password, + &session_key, + &encrypted_password); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + } + + status = dcerpc_wkssvc_NetrGetJoinableOus2(b, talloc_tos(), + r->in.server_name, + r->in.domain, + r->in.account, + encrypted_password, + r->out.ou_count, + r->out.ous, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetRenameMachineInDomain_r(struct libnetapi_ctx *ctx, + struct NetRenameMachineInDomain *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; + NTSTATUS status; + WERROR werr; + struct dcerpc_binding_handle *b; + DATA_BLOB session_key; + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_wkssvc, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + if (r->in.password) { + + status = cli_get_session_key(talloc_tos(), pipe_cli, &session_key); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = encode_wkssvc_join_password_buffer(ctx, + r->in.password, + &session_key, + &encrypted_password); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + } + + status = dcerpc_wkssvc_NetrRenameMachineInDomain2(b, talloc_tos(), + r->in.server_name, + r->in.new_machine_name, + r->in.account, + encrypted_password, + r->in.rename_options, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetRenameMachineInDomain_l(struct libnetapi_ctx *ctx, + struct NetRenameMachineInDomain *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetRenameMachineInDomain); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetProvisionComputerAccount_r(struct libnetapi_ctx *ctx, + struct NetProvisionComputerAccount *r) +{ + return NetProvisionComputerAccount_l(ctx, r); +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetProvisionComputerAccount_backend(struct libnetapi_ctx *ctx, + struct NetProvisionComputerAccount *r, + TALLOC_CTX *mem_ctx, + struct ODJ_PROVISION_DATA **p) +{ + WERROR werr; + struct libnet_JoinCtx *j = NULL; + int use_kerberos = 0; + const char *username = NULL; + + werr = libnet_init_JoinCtx(mem_ctx, &j); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + j->in.domain_name = talloc_strdup(j, r->in.domain); + if (j->in.domain_name == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + + talloc_free(discard_const_p(char *, j->in.machine_name)); + j->in.machine_name = talloc_strdup(j, r->in.machine_name); + if (j->in.machine_name == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + + if (r->in.dcname) { + j->in.dc_name = talloc_strdup(j, r->in.dcname); + if (j->in.dc_name == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + } + + if (r->in.machine_account_ou) { + j->in.account_ou = talloc_strdup(j, r->in.machine_account_ou); + if (j->in.account_ou == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + } + + libnetapi_get_username(ctx, &username); + if (username == NULL) { + talloc_free(j); + return WERR_NERR_BADUSERNAME; + } + + j->in.admin_account = talloc_strdup(j, username); + if (j->in.admin_account == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + + libnetapi_get_use_kerberos(ctx, &use_kerberos); + if (!use_kerberos) { + const char *password = NULL; + + libnetapi_get_password(ctx, &password); + if (password == NULL) { + talloc_free(j); + return WERR_NERR_BADPASSWORD; + } + j->in.admin_password = talloc_strdup(j, password); + if (j->in.admin_password == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + } + + j->in.use_kerberos = use_kerberos; + j->in.debug = true; + j->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE; + + if (r->in.options & NETSETUP_PROVISION_REUSE_ACCOUNT) { + j->in.join_flags |= WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; + } + + if (r->in.options & NETSETUP_PROVISION_USE_DEFAULT_PASSWORD) { + j->in.join_flags |= WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED; + j->in.machine_password = talloc_strdup(j, r->in.machine_name); + if (j->in.machine_password == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + } + + j->in.provision_computer_account_only = true; + + werr = libnet_Join(mem_ctx, j); + if (!W_ERROR_IS_OK(werr) && j->out.error_string) { + libnetapi_set_error_string(ctx, "%s", j->out.error_string); + talloc_free(j); + return werr; + } + + werr = libnet_odj_compose_ODJ_PROVISION_DATA(mem_ctx, j, p); + if (!W_ERROR_IS_OK(werr)) { + talloc_free(j); + return werr; + } + + TALLOC_FREE(j); + + return WERR_OK; +} + +WERROR NetProvisionComputerAccount_l(struct libnetapi_ctx *ctx, + struct NetProvisionComputerAccount *r) +{ + WERROR werr; + enum ndr_err_code ndr_err; + const char *b64_bin_data_str; + DATA_BLOB blob; + struct ODJ_PROVISION_DATA_serialized_ptr odj_provision_data; + struct ODJ_PROVISION_DATA *p; + TALLOC_CTX *mem_ctx = talloc_new(ctx); + + if (r->in.provision_bin_data == NULL && + r->in.provision_text_data == NULL) { + return WERR_INVALID_PARAMETER; + } + if (r->in.provision_bin_data != NULL && + r->in.provision_text_data != NULL) { + return WERR_INVALID_PARAMETER; + } + if (r->in.provision_bin_data == NULL && + r->in.provision_bin_data_size != NULL) { + return WERR_INVALID_PARAMETER; + } + if (r->in.provision_bin_data != NULL && + r->in.provision_bin_data_size == NULL) { + return WERR_INVALID_PARAMETER; + } + + if (r->in.domain == NULL) { + return WERR_INVALID_PARAMETER; + } + + if (r->in.machine_name == NULL) { + return WERR_INVALID_PARAMETER; + } + + werr = NetProvisionComputerAccount_backend(ctx, r, mem_ctx, &p); + if (!W_ERROR_IS_OK(werr)) { + talloc_free(mem_ctx); + return werr; + } + + ZERO_STRUCT(odj_provision_data); + + odj_provision_data.s.p = p; + + ndr_err = ndr_push_struct_blob(&blob, ctx, &odj_provision_data, + (ndr_push_flags_fn_t)ndr_push_ODJ_PROVISION_DATA_serialized_ptr); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(mem_ctx); + return W_ERROR(NERR_BadOfflineJoinInfo); + } + + talloc_free(mem_ctx); + + if (r->out.provision_text_data != NULL) { + b64_bin_data_str = base64_encode_data_blob(ctx, blob); + if (b64_bin_data_str == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + *r->out.provision_text_data = b64_bin_data_str; + } + + if (r->out.provision_bin_data != NULL && + r->out.provision_bin_data_size != NULL) { + *r->out.provision_bin_data = blob.data; + *r->out.provision_bin_data_size = blob.length; + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetRequestOfflineDomainJoin_r(struct libnetapi_ctx *ctx, + struct NetRequestOfflineDomainJoin *r) +{ + return WERR_NOT_SUPPORTED; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetRequestOfflineDomainJoin_backend(struct libnetapi_ctx *ctx, + const struct ODJ_WIN7BLOB *win7blob, + const struct ODJ_PROVISION_DATA *odj_provision_data) +{ + struct libnet_JoinCtx *j = NULL; + WERROR werr; + + werr = libnet_init_JoinCtx(ctx, &j); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + j->in.domain_name = talloc_strdup(j, win7blob->lpDomain); + if (j->in.domain_name == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + + talloc_free(discard_const_p(char *, j->in.machine_name)); + j->in.machine_name = talloc_strdup(j, win7blob->lpMachineName); + if (j->in.machine_name == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + + j->in.machine_password = talloc_strdup(j, win7blob->lpMachinePassword); + if (j->in.machine_password == NULL) { + talloc_free(j); + return WERR_NOT_ENOUGH_MEMORY; + } + + j->in.request_offline_join = true; + j->in.odj_provision_data = discard_const(odj_provision_data); + j->in.debug = true; + j->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED; + + werr = libnet_Join(j, j); + if (!W_ERROR_IS_OK(werr)) { + if (j->out.error_string != NULL) { + libnetapi_set_error_string(ctx, "%s", j->out.error_string); + } + talloc_free(j); + return werr; + } + + TALLOC_FREE(j); + + return WERR_OK; +} + +WERROR NetRequestOfflineDomainJoin_l(struct libnetapi_ctx *ctx, + struct NetRequestOfflineDomainJoin *r) +{ + DATA_BLOB blob, blob_base64; + enum ndr_err_code ndr_err; + struct ODJ_PROVISION_DATA_serialized_ptr odj_provision_data; + bool ok; + struct ODJ_WIN7BLOB win7blob = { 0 }; + WERROR werr; + + if (r->in.provision_bin_data == NULL || + r->in.provision_bin_data_size == 0) { + return W_ERROR(NERR_NoOfflineJoinInfo); + } + + if (r->in.provision_bin_data_size < 2) { + return W_ERROR(NERR_BadOfflineJoinInfo); + } + + /* + * Windows produces and consumes UTF16/UCS2 encoded blobs. Check for the + * unicode BOM mark and convert back to UNIX charset if necessary. + */ + if (r->in.provision_bin_data[0] == 0xff && + r->in.provision_bin_data[1] == 0xfe) { + ok = convert_string_talloc(ctx, CH_UTF16LE, CH_UNIX, + r->in.provision_bin_data+2, + r->in.provision_bin_data_size-2, + &blob_base64.data, + &blob_base64.length); + if (!ok) { + return W_ERROR(NERR_BadOfflineJoinInfo); + } + } else { + blob_base64 = data_blob(r->in.provision_bin_data, + r->in.provision_bin_data_size); + } + + blob = base64_decode_data_blob_talloc(ctx, (const char *)blob_base64.data); + + ndr_err = ndr_pull_struct_blob(&blob, ctx, &odj_provision_data, + (ndr_pull_flags_fn_t)ndr_pull_ODJ_PROVISION_DATA_serialized_ptr); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return W_ERROR(NERR_BadOfflineJoinInfo); + } + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(ODJ_PROVISION_DATA_serialized_ptr, &odj_provision_data); + } + + if (odj_provision_data.s.p->ulVersion != 1) { + return W_ERROR(NERR_ProvisioningBlobUnsupported); + } + + werr = libnet_odj_find_win7blob(odj_provision_data.s.p, &win7blob); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + if (!(r->in.options & NETSETUP_PROVISION_ONLINE_CALLER)) { + return WERR_NERR_SETUPNOTJOINED; + } + + werr = NetRequestOfflineDomainJoin_backend(ctx, + &win7blob, + odj_provision_data.s.p); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + return W_ERROR(NERR_JoinPerformedMustRestart); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetComposeOfflineDomainJoin_r(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r) +{ + return WERR_NOT_SUPPORTED; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetComposeOfflineDomainJoin_backend(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r, + TALLOC_CTX *mem_ctx, + struct ODJ_PROVISION_DATA **p) +{ + struct libnet_JoinCtx *j = NULL; + WERROR werr; + + werr = libnet_init_JoinCtx(ctx, &j); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + j->in.domain_name = talloc_strdup(j, r->in.dns_domain_name); + if (j->in.domain_name == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + j->in.dc_name = talloc_strdup(j, r->in.dc_name); + W_ERROR_HAVE_NO_MEMORY(j->in.dc_name); + + j->in.machine_password = talloc_strdup(j, r->in.machine_account_password); + W_ERROR_HAVE_NO_MEMORY(j->in.machine_password); + + j->out.account_name = talloc_strdup(j, r->in.machine_account_name); + W_ERROR_HAVE_NO_MEMORY(j->out.account_name); + + j->out.dns_domain_name = talloc_strdup(j, r->in.dns_domain_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dns_domain_name); + + j->out.netbios_domain_name = talloc_strdup(j, r->in.netbios_domain_name); + W_ERROR_HAVE_NO_MEMORY(j->out.netbios_domain_name); + + j->out.domain_sid = dom_sid_dup(j, (struct dom_sid *)r->in.domain_sid); + W_ERROR_HAVE_NO_MEMORY(j->out.domain_sid); + + j->out.domain_guid = *r->in.domain_guid; + + j->out.forest_name = talloc_strdup(j, r->in.forest_name); + W_ERROR_HAVE_NO_MEMORY(j->out.forest_name); + + j->out.domain_is_ad = r->in.domain_is_ad; + + j->out.dcinfo = talloc_zero(j, struct netr_DsRGetDCNameInfo); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo); + + j->out.dcinfo->dc_unc = talloc_asprintf(j->out.dcinfo, "\\\\%s", r->in.dc_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->dc_unc); + + j->out.dcinfo->dc_address = talloc_asprintf(j->out.dcinfo, "\\\\%s", r->in.dc_address); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->dc_address); + + j->out.dcinfo->dc_address_type = DS_ADDRESS_TYPE_INET; + + j->out.dcinfo->domain_guid = *r->in.domain_guid; + + j->out.dcinfo->domain_name = talloc_strdup(j->out.dcinfo, r->in.dns_domain_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->domain_name); + + j->out.dcinfo->forest_name = talloc_strdup(j->out.dcinfo, r->in.forest_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->forest_name); + + werr = libnet_odj_compose_ODJ_PROVISION_DATA(mem_ctx, j, p); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + return WERR_OK; +} + +WERROR NetComposeOfflineDomainJoin_l(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r) +{ + WERROR werr; + enum ndr_err_code ndr_err; + const char *b64_bin_data_str; + DATA_BLOB blob; + struct ODJ_PROVISION_DATA_serialized_ptr odj_compose_data; + struct ODJ_PROVISION_DATA *p; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if (r->in.compose_bin_data == NULL && + r->in.compose_text_data == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + if (r->in.compose_bin_data != NULL && + r->in.compose_text_data != NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + if (r->in.compose_bin_data == NULL && + r->in.compose_bin_data_size != NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + if (r->in.compose_bin_data != NULL && + r->in.compose_bin_data_size == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.dns_domain_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.netbios_domain_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.domain_sid == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.domain_guid == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.forest_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.machine_account_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.machine_account_password == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.dc_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.dc_address == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + werr = NetComposeOfflineDomainJoin_backend(ctx, r, tmp_ctx, &p); + if (!W_ERROR_IS_OK(werr)) { + goto out; + } + + ZERO_STRUCT(odj_compose_data); + + odj_compose_data.s.p = p; + + ndr_err = ndr_push_struct_blob(&blob, ctx, &odj_compose_data, + (ndr_push_flags_fn_t)ndr_push_ODJ_PROVISION_DATA_serialized_ptr); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + werr = W_ERROR(NERR_BadOfflineJoinInfo); + goto out; + } + + if (r->out.compose_text_data != NULL) { + b64_bin_data_str = base64_encode_data_blob(ctx, blob); + if (b64_bin_data_str == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + } + *r->out.compose_text_data = b64_bin_data_str; + } + + if (r->out.compose_bin_data != NULL && + r->out.compose_bin_data_size != NULL) { + *r->out.compose_bin_data = blob.data; + *r->out.compose_bin_data_size = blob.length; + } + + werr = WERR_OK; +out: + talloc_free(tmp_ctx); + return werr; +} diff --git a/source3/lib/netapi/libnetapi.c b/source3/lib/netapi/libnetapi.c new file mode 100644 index 0000000..2907316 --- /dev/null +++ b/source3/lib/netapi/libnetapi.c @@ -0,0 +1,3019 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "librpc/gen_ndr/ndr_libnetapi.h" + +/**************************************************************** + NetJoinDomain +****************************************************************/ + +NET_API_STATUS NetJoinDomain(const char * server /* [in] [unique] */, + const char * domain /* [in] [ref] */, + const char * account_ou /* [in] [unique] */, + const char * account /* [in] [unique] */, + const char * password /* [in] [unique] */, + uint32_t join_flags /* [in] */) +{ + struct NetJoinDomain r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server = server; + r.in.domain = domain; + r.in.account_ou = account_ou; + r.in.account = account; + r.in.password = password; + r.in.join_flags = join_flags; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetJoinDomain, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server)) { + werr = NetJoinDomain_l(ctx, &r); + } else { + werr = NetJoinDomain_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetJoinDomain, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUnjoinDomain +****************************************************************/ + +NET_API_STATUS NetUnjoinDomain(const char * server_name /* [in] [unique] */, + const char * account /* [in] [unique] */, + const char * password /* [in] [unique] */, + uint32_t unjoin_flags /* [in] */) +{ + struct NetUnjoinDomain r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.account = account; + r.in.password = password; + r.in.unjoin_flags = unjoin_flags; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUnjoinDomain, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUnjoinDomain_l(ctx, &r); + } else { + werr = NetUnjoinDomain_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUnjoinDomain, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGetJoinInformation +****************************************************************/ + +NET_API_STATUS NetGetJoinInformation(const char * server_name /* [in] [unique] */, + const char * *name_buffer /* [out] [ref] */, + uint16_t *name_type /* [out] [ref] */) +{ + struct NetGetJoinInformation r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + + /* Out parameters */ + r.out.name_buffer = name_buffer; + r.out.name_type = name_type; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGetJoinInformation, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGetJoinInformation_l(ctx, &r); + } else { + werr = NetGetJoinInformation_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGetJoinInformation, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGetJoinableOUs +****************************************************************/ + +NET_API_STATUS NetGetJoinableOUs(const char * server_name /* [in] [unique] */, + const char * domain /* [in] [ref] */, + const char * account /* [in] [unique] */, + const char * password /* [in] [unique] */, + uint32_t *ou_count /* [out] [ref] */, + const char * **ous /* [out] [ref] */) +{ + struct NetGetJoinableOUs r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.domain = domain; + r.in.account = account; + r.in.password = password; + + /* Out parameters */ + r.out.ou_count = ou_count; + r.out.ous = ous; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGetJoinableOUs, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGetJoinableOUs_l(ctx, &r); + } else { + werr = NetGetJoinableOUs_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGetJoinableOUs, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetRenameMachineInDomain +****************************************************************/ + +NET_API_STATUS NetRenameMachineInDomain(const char * server_name /* [in] */, + const char * new_machine_name /* [in] */, + const char * account /* [in] */, + const char * password /* [in] */, + uint32_t rename_options /* [in] */) +{ + struct NetRenameMachineInDomain r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.new_machine_name = new_machine_name; + r.in.account = account; + r.in.password = password; + r.in.rename_options = rename_options; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetRenameMachineInDomain, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetRenameMachineInDomain_l(ctx, &r); + } else { + werr = NetRenameMachineInDomain_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetRenameMachineInDomain, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetProvisionComputerAccount +****************************************************************/ + +NET_API_STATUS NetProvisionComputerAccount(const char * domain /* [in] [ref] */, + const char * machine_name /* [in] [ref] */, + const char * machine_account_ou /* [in] [unique] */, + const char * dcname /* [in] [unique] */, + uint32_t options /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */) +{ + struct NetProvisionComputerAccount r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.domain = domain; + r.in.machine_name = machine_name; + r.in.machine_account_ou = machine_account_ou; + r.in.dcname = dcname; + r.in.options = options; + r.in.provision_bin_data = provision_bin_data; + r.in.provision_bin_data_size = provision_bin_data_size; + r.in.provision_text_data = provision_text_data; + + /* Out parameters */ + r.out.provision_bin_data = provision_bin_data; + r.out.provision_bin_data_size = provision_bin_data_size; + r.out.provision_text_data = provision_text_data; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetProvisionComputerAccount, &r); + } + + werr = NetProvisionComputerAccount_l(ctx, &r); + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetProvisionComputerAccount, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetRequestOfflineDomainJoin +****************************************************************/ + +NET_API_STATUS NetRequestOfflineDomainJoin(uint8_t *provision_bin_data /* [in] [unique] */, + uint32_t provision_bin_data_size /* [in] */, + uint32_t options /* [in] */, + const char * windows_path /* [in] [unique] */) +{ + struct NetRequestOfflineDomainJoin r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.provision_bin_data = provision_bin_data; + r.in.provision_bin_data_size = provision_bin_data_size; + r.in.options = options; + r.in.windows_path = windows_path; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetRequestOfflineDomainJoin, &r); + } + + werr = NetRequestOfflineDomainJoin_l(ctx, &r); + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetRequestOfflineDomainJoin, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetComposeOfflineDomainJoin +****************************************************************/ +NET_API_STATUS NetComposeOfflineDomainJoin(const char *dns_domain_name /* [in] [ref] */, + const char *netbios_domain_name /* [in] [ref] */, + struct domsid *domain_sid /* [in] [ref] */, + struct GUID *domain_guid /* [in] [ref] */, + const char *forest_name /* [in] [ref] */, + const char *machine_account_name /* [in] [ref] */, + const char *machine_account_password /* [in] [ref] */, + const char *dc_name /* [in] [unique] */, + const char *dc_address /* [in] [unique] */, + int domain_is_ad /* [in] */, + uint8_t **compose_bin_data /* [in,out] [unique] */, + uint32_t *compose_bin_data_size /* [in,out] [unique] */, + const char * *compose_text_data /* [in,out] [unique] */) +{ + struct NetComposeOfflineDomainJoin r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.dns_domain_name = dns_domain_name; + r.in.netbios_domain_name = netbios_domain_name; + r.in.domain_sid = domain_sid; + r.in.domain_guid = domain_guid; + r.in.forest_name = forest_name; + r.in.machine_account_name = machine_account_name; + r.in.machine_account_password = machine_account_password; + r.in.dc_name = dc_name; + r.in.dc_address = dc_address; + r.in.domain_is_ad = domain_is_ad; + r.in.compose_bin_data = compose_bin_data; + r.in.compose_bin_data_size = compose_bin_data_size; + r.in.compose_text_data = compose_text_data; + + /* Out parameters */ + r.out.compose_bin_data = compose_bin_data; + r.out.compose_bin_data_size = compose_bin_data_size; + r.out.compose_text_data = compose_text_data; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetComposeOfflineDomainJoin, &r); + } + + werr = NetComposeOfflineDomainJoin_l(ctx, &r); + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetComposeOfflineDomainJoin, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetServerGetInfo +****************************************************************/ + +NET_API_STATUS NetServerGetInfo(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetServerGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetServerGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetServerGetInfo_l(ctx, &r); + } else { + werr = NetServerGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetServerGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetServerSetInfo +****************************************************************/ + +NET_API_STATUS NetServerSetInfo(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_error /* [out] [ref] */) +{ + struct NetServerSetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_error = parm_error; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetServerSetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetServerSetInfo_l(ctx, &r); + } else { + werr = NetServerSetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetServerSetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetWkstaGetInfo +****************************************************************/ + +NET_API_STATUS NetWkstaGetInfo(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetWkstaGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetWkstaGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetWkstaGetInfo_l(ctx, &r); + } else { + werr = NetWkstaGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetWkstaGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGetDCName +****************************************************************/ + +NET_API_STATUS NetGetDCName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [unique] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetGetDCName r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.domain_name = domain_name; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGetDCName, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGetDCName_l(ctx, &r); + } else { + werr = NetGetDCName_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGetDCName, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGetAnyDCName +****************************************************************/ + +NET_API_STATUS NetGetAnyDCName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [unique] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetGetAnyDCName r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.domain_name = domain_name; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGetAnyDCName, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGetAnyDCName_l(ctx, &r); + } else { + werr = NetGetAnyDCName_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGetAnyDCName, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + DsGetDcName +****************************************************************/ + +NET_API_STATUS DsGetDcName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [ref] */, + struct GUID *domain_guid /* [in] [unique] */, + const char * site_name /* [in] [unique] */, + uint32_t flags /* [in] */, + struct DOMAIN_CONTROLLER_INFO **dc_info /* [out] [ref] */) +{ + struct DsGetDcName r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.domain_name = domain_name; + r.in.domain_guid = domain_guid; + r.in.site_name = site_name; + r.in.flags = flags; + + /* Out parameters */ + r.out.dc_info = dc_info; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(DsGetDcName, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = DsGetDcName_l(ctx, &r); + } else { + werr = DsGetDcName_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(DsGetDcName, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserAdd +****************************************************************/ + +NET_API_STATUS NetUserAdd(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_error /* [out] [ref] */) +{ + struct NetUserAdd r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_error = parm_error; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserAdd, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserAdd_l(ctx, &r); + } else { + werr = NetUserAdd_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserAdd, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserDel +****************************************************************/ + +NET_API_STATUS NetUserDel(const char * server_name /* [in] [unique] */, + const char * user_name /* [in] [ref] */) +{ + struct NetUserDel r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.user_name = user_name; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserDel, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserDel_l(ctx, &r); + } else { + werr = NetUserDel_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserDel, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserEnum +****************************************************************/ + +NET_API_STATUS NetUserEnum(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint32_t filter /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetUserEnum r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.filter = filter; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserEnum, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserEnum_l(ctx, &r); + } else { + werr = NetUserEnum_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserEnum, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserChangePassword +****************************************************************/ + +NET_API_STATUS NetUserChangePassword(const char * domain_name /* [in] */, + const char * user_name /* [in] */, + const char * old_password /* [in] */, + const char * new_password /* [in] */) +{ + struct NetUserChangePassword r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.domain_name = domain_name; + r.in.user_name = user_name; + r.in.old_password = old_password; + r.in.new_password = new_password; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserChangePassword, &r); + } + + werr = NetUserChangePassword_l(ctx, &r); + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserChangePassword, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserGetInfo +****************************************************************/ + +NET_API_STATUS NetUserGetInfo(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetUserGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.user_name = user_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserGetInfo_l(ctx, &r); + } else { + werr = NetUserGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserSetInfo +****************************************************************/ + +NET_API_STATUS NetUserSetInfo(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetUserSetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.user_name = user_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserSetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserSetInfo_l(ctx, &r); + } else { + werr = NetUserSetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserSetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserGetGroups +****************************************************************/ + +NET_API_STATUS NetUserGetGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */) +{ + struct NetUserGetGroups r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.user_name = user_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserGetGroups, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserGetGroups_l(ctx, &r); + } else { + werr = NetUserGetGroups_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserGetGroups, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserSetGroups +****************************************************************/ + +NET_API_STATUS NetUserSetGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t num_entries /* [in] */) +{ + struct NetUserSetGroups r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.user_name = user_name; + r.in.level = level; + r.in.buffer = buffer; + r.in.num_entries = num_entries; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserSetGroups, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserSetGroups_l(ctx, &r); + } else { + werr = NetUserSetGroups_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserSetGroups, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserGetLocalGroups +****************************************************************/ + +NET_API_STATUS NetUserGetLocalGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint32_t flags /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */) +{ + struct NetUserGetLocalGroups r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.user_name = user_name; + r.in.level = level; + r.in.flags = flags; + r.in.prefmaxlen = prefmaxlen; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserGetLocalGroups, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserGetLocalGroups_l(ctx, &r); + } else { + werr = NetUserGetLocalGroups_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserGetLocalGroups, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserModalsGet +****************************************************************/ + +NET_API_STATUS NetUserModalsGet(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetUserModalsGet r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserModalsGet, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserModalsGet_l(ctx, &r); + } else { + werr = NetUserModalsGet_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserModalsGet, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetUserModalsSet +****************************************************************/ + +NET_API_STATUS NetUserModalsSet(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetUserModalsSet r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetUserModalsSet, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetUserModalsSet_l(ctx, &r); + } else { + werr = NetUserModalsSet_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetUserModalsSet, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetQueryDisplayInformation +****************************************************************/ + +NET_API_STATUS NetQueryDisplayInformation(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint32_t idx /* [in] */, + uint32_t entries_requested /* [in] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + void **buffer /* [out] [noprint,ref] */) +{ + struct NetQueryDisplayInformation r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.idx = idx; + r.in.entries_requested = entries_requested; + r.in.prefmaxlen = prefmaxlen; + + /* Out parameters */ + r.out.entries_read = entries_read; + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetQueryDisplayInformation, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetQueryDisplayInformation_l(ctx, &r); + } else { + werr = NetQueryDisplayInformation_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetQueryDisplayInformation, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupAdd +****************************************************************/ + +NET_API_STATUS NetGroupAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetGroupAdd r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupAdd, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupAdd_l(ctx, &r); + } else { + werr = NetGroupAdd_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupAdd, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupDel +****************************************************************/ + +NET_API_STATUS NetGroupDel(const char * server_name /* [in] */, + const char * group_name /* [in] */) +{ + struct NetGroupDel r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupDel, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupDel_l(ctx, &r); + } else { + werr = NetGroupDel_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupDel, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupEnum +****************************************************************/ + +NET_API_STATUS NetGroupEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetGroupEnum r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupEnum, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupEnum_l(ctx, &r); + } else { + werr = NetGroupEnum_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupEnum, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupSetInfo +****************************************************************/ + +NET_API_STATUS NetGroupSetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetGroupSetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupSetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupSetInfo_l(ctx, &r); + } else { + werr = NetGroupSetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupSetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupGetInfo +****************************************************************/ + +NET_API_STATUS NetGroupGetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetGroupGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupGetInfo_l(ctx, &r); + } else { + werr = NetGroupGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupAddUser +****************************************************************/ + +NET_API_STATUS NetGroupAddUser(const char * server_name /* [in] */, + const char * group_name /* [in] */, + const char * user_name /* [in] */) +{ + struct NetGroupAddUser r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.user_name = user_name; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupAddUser, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupAddUser_l(ctx, &r); + } else { + werr = NetGroupAddUser_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupAddUser, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupDelUser +****************************************************************/ + +NET_API_STATUS NetGroupDelUser(const char * server_name /* [in] */, + const char * group_name /* [in] */, + const char * user_name /* [in] */) +{ + struct NetGroupDelUser r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.user_name = user_name; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupDelUser, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupDelUser_l(ctx, &r); + } else { + werr = NetGroupDelUser_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupDelUser, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupGetUsers +****************************************************************/ + +NET_API_STATUS NetGroupGetUsers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetGroupGetUsers r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupGetUsers, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupGetUsers_l(ctx, &r); + } else { + werr = NetGroupGetUsers_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupGetUsers, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetGroupSetUsers +****************************************************************/ + +NET_API_STATUS NetGroupSetUsers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t num_entries /* [in] */) +{ + struct NetGroupSetUsers r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.buffer = buffer; + r.in.num_entries = num_entries; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetGroupSetUsers, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetGroupSetUsers_l(ctx, &r); + } else { + werr = NetGroupSetUsers_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetGroupSetUsers, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupAdd +****************************************************************/ + +NET_API_STATUS NetLocalGroupAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetLocalGroupAdd r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupAdd, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupAdd_l(ctx, &r); + } else { + werr = NetLocalGroupAdd_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupAdd, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupDel +****************************************************************/ + +NET_API_STATUS NetLocalGroupDel(const char * server_name /* [in] */, + const char * group_name /* [in] */) +{ + struct NetLocalGroupDel r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupDel, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupDel_l(ctx, &r); + } else { + werr = NetLocalGroupDel_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupDel, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupGetInfo +****************************************************************/ + +NET_API_STATUS NetLocalGroupGetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetLocalGroupGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupGetInfo_l(ctx, &r); + } else { + werr = NetLocalGroupGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupSetInfo +****************************************************************/ + +NET_API_STATUS NetLocalGroupSetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetLocalGroupSetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupSetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupSetInfo_l(ctx, &r); + } else { + werr = NetLocalGroupSetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupSetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupEnum +****************************************************************/ + +NET_API_STATUS NetLocalGroupEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetLocalGroupEnum r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupEnum, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupEnum_l(ctx, &r); + } else { + werr = NetLocalGroupEnum_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupEnum, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupAddMembers +****************************************************************/ + +NET_API_STATUS NetLocalGroupAddMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */) +{ + struct NetLocalGroupAddMembers r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.buffer = buffer; + r.in.total_entries = total_entries; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupAddMembers, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupAddMembers_l(ctx, &r); + } else { + werr = NetLocalGroupAddMembers_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupAddMembers, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupDelMembers +****************************************************************/ + +NET_API_STATUS NetLocalGroupDelMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */) +{ + struct NetLocalGroupDelMembers r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.buffer = buffer; + r.in.total_entries = total_entries; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupDelMembers, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupDelMembers_l(ctx, &r); + } else { + werr = NetLocalGroupDelMembers_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupDelMembers, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupGetMembers +****************************************************************/ + +NET_API_STATUS NetLocalGroupGetMembers(const char * server_name /* [in] */, + const char * local_group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetLocalGroupGetMembers r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.local_group_name = local_group_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupGetMembers, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupGetMembers_l(ctx, &r); + } else { + werr = NetLocalGroupGetMembers_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupGetMembers, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetLocalGroupSetMembers +****************************************************************/ + +NET_API_STATUS NetLocalGroupSetMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */) +{ + struct NetLocalGroupSetMembers r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.group_name = group_name; + r.in.level = level; + r.in.buffer = buffer; + r.in.total_entries = total_entries; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetLocalGroupSetMembers, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetLocalGroupSetMembers_l(ctx, &r); + } else { + werr = NetLocalGroupSetMembers_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetLocalGroupSetMembers, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetRemoteTOD +****************************************************************/ + +NET_API_STATUS NetRemoteTOD(const char * server_name /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetRemoteTOD r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetRemoteTOD, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetRemoteTOD_l(ctx, &r); + } else { + werr = NetRemoteTOD_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetRemoteTOD, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShareAdd +****************************************************************/ + +NET_API_STATUS NetShareAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetShareAdd r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShareAdd, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShareAdd_l(ctx, &r); + } else { + werr = NetShareAdd_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShareAdd, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShareDel +****************************************************************/ + +NET_API_STATUS NetShareDel(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t reserved /* [in] */) +{ + struct NetShareDel r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.net_name = net_name; + r.in.reserved = reserved; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShareDel, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShareDel_l(ctx, &r); + } else { + werr = NetShareDel_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShareDel, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShareEnum +****************************************************************/ + +NET_API_STATUS NetShareEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetShareEnum r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShareEnum, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShareEnum_l(ctx, &r); + } else { + werr = NetShareEnum_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShareEnum, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShareGetInfo +****************************************************************/ + +NET_API_STATUS NetShareGetInfo(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetShareGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.net_name = net_name; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShareGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShareGetInfo_l(ctx, &r); + } else { + werr = NetShareGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShareGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShareSetInfo +****************************************************************/ + +NET_API_STATUS NetShareSetInfo(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */) +{ + struct NetShareSetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.net_name = net_name; + r.in.level = level; + r.in.buffer = buffer; + + /* Out parameters */ + r.out.parm_err = parm_err; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShareSetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShareSetInfo_l(ctx, &r); + } else { + werr = NetShareSetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShareSetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetFileClose +****************************************************************/ + +NET_API_STATUS NetFileClose(const char * server_name /* [in] */, + uint32_t fileid /* [in] */) +{ + struct NetFileClose r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.fileid = fileid; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetFileClose, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetFileClose_l(ctx, &r); + } else { + werr = NetFileClose_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetFileClose, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetFileGetInfo +****************************************************************/ + +NET_API_STATUS NetFileGetInfo(const char * server_name /* [in] */, + uint32_t fileid /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct NetFileGetInfo r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.fileid = fileid; + r.in.level = level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetFileGetInfo, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetFileGetInfo_l(ctx, &r); + } else { + werr = NetFileGetInfo_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetFileGetInfo, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetFileEnum +****************************************************************/ + +NET_API_STATUS NetFileEnum(const char * server_name /* [in] */, + const char * base_path /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */) +{ + struct NetFileEnum r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.base_path = base_path; + r.in.user_name = user_name; + r.in.level = level; + r.in.prefmaxlen = prefmaxlen; + r.in.resume_handle = resume_handle; + + /* Out parameters */ + r.out.buffer = buffer; + r.out.entries_read = entries_read; + r.out.total_entries = total_entries; + r.out.resume_handle = resume_handle; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetFileEnum, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetFileEnum_l(ctx, &r); + } else { + werr = NetFileEnum_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetFileEnum, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShutdownInit +****************************************************************/ + +NET_API_STATUS NetShutdownInit(const char * server_name /* [in] */, + const char * message /* [in] */, + uint32_t timeout /* [in] */, + uint8_t force_apps /* [in] */, + uint8_t do_reboot /* [in] */) +{ + struct NetShutdownInit r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.message = message; + r.in.timeout = timeout; + r.in.force_apps = force_apps; + r.in.do_reboot = do_reboot; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShutdownInit, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShutdownInit_l(ctx, &r); + } else { + werr = NetShutdownInit_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShutdownInit, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + NetShutdownAbort +****************************************************************/ + +NET_API_STATUS NetShutdownAbort(const char * server_name /* [in] */) +{ + struct NetShutdownAbort r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + + /* Out parameters */ + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetShutdownAbort, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = NetShutdownAbort_l(ctx, &r); + } else { + werr = NetShutdownAbort_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetShutdownAbort, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + I_NetLogonControl +****************************************************************/ + +NET_API_STATUS I_NetLogonControl(const char * server_name /* [in] */, + uint32_t function_code /* [in] */, + uint32_t query_level /* [in] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct I_NetLogonControl r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.function_code = function_code; + r.in.query_level = query_level; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(I_NetLogonControl, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = I_NetLogonControl_l(ctx, &r); + } else { + werr = I_NetLogonControl_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(I_NetLogonControl, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + +/**************************************************************** + I_NetLogonControl2 +****************************************************************/ + +NET_API_STATUS I_NetLogonControl2(const char * server_name /* [in] */, + uint32_t function_code /* [in] */, + uint32_t query_level /* [in] */, + uint8_t *data /* [in] [unique] */, + uint8_t **buffer /* [out] [ref] */) +{ + struct I_NetLogonControl2 r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.server_name = server_name; + r.in.function_code = function_code; + r.in.query_level = query_level; + r.in.data = data; + + /* Out parameters */ + r.out.buffer = buffer; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(I_NetLogonControl2, &r); + } + + if (LIBNETAPI_LOCAL_SERVER(server_name)) { + werr = I_NetLogonControl2_l(ctx, &r); + } else { + werr = I_NetLogonControl2_r(ctx, &r); + } + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(I_NetLogonControl2, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + diff --git a/source3/lib/netapi/libnetapi.h b/source3/lib/netapi/libnetapi.h new file mode 100644 index 0000000..784d467 --- /dev/null +++ b/source3/lib/netapi/libnetapi.h @@ -0,0 +1,523 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBNETAPI_LIBNETAPI__ +#define __LIBNETAPI_LIBNETAPI__ +NET_API_STATUS NetJoinDomain(const char * server /* [in] [unique] */, + const char * domain /* [in] [ref] */, + const char * account_ou /* [in] [unique] */, + const char * account /* [in] [unique] */, + const char * password /* [in] [unique] */, + uint32_t join_flags /* [in] */); +WERROR NetJoinDomain_r(struct libnetapi_ctx *ctx, + struct NetJoinDomain *r); +WERROR NetJoinDomain_l(struct libnetapi_ctx *ctx, + struct NetJoinDomain *r); +NET_API_STATUS NetUnjoinDomain(const char * server_name /* [in] [unique] */, + const char * account /* [in] [unique] */, + const char * password /* [in] [unique] */, + uint32_t unjoin_flags /* [in] */); +WERROR NetUnjoinDomain_r(struct libnetapi_ctx *ctx, + struct NetUnjoinDomain *r); +WERROR NetUnjoinDomain_l(struct libnetapi_ctx *ctx, + struct NetUnjoinDomain *r); +NET_API_STATUS NetGetJoinInformation(const char * server_name /* [in] [unique] */, + const char * *name_buffer /* [out] [ref] */, + uint16_t *name_type /* [out] [ref] */); +WERROR NetGetJoinInformation_r(struct libnetapi_ctx *ctx, + struct NetGetJoinInformation *r); +WERROR NetGetJoinInformation_l(struct libnetapi_ctx *ctx, + struct NetGetJoinInformation *r); +NET_API_STATUS NetGetJoinableOUs(const char * server_name /* [in] [unique] */, + const char * domain /* [in] [ref] */, + const char * account /* [in] [unique] */, + const char * password /* [in] [unique] */, + uint32_t *ou_count /* [out] [ref] */, + const char * **ous /* [out] [ref] */); +WERROR NetGetJoinableOUs_r(struct libnetapi_ctx *ctx, + struct NetGetJoinableOUs *r); +WERROR NetGetJoinableOUs_l(struct libnetapi_ctx *ctx, + struct NetGetJoinableOUs *r); +NET_API_STATUS NetRenameMachineInDomain(const char * server_name /* [in] */, + const char * new_machine_name /* [in] */, + const char * account /* [in] */, + const char * password /* [in] */, + uint32_t rename_options /* [in] */); +WERROR NetRenameMachineInDomain_r(struct libnetapi_ctx *ctx, + struct NetRenameMachineInDomain *r); +WERROR NetRenameMachineInDomain_l(struct libnetapi_ctx *ctx, + struct NetRenameMachineInDomain *r); +NET_API_STATUS NetProvisionComputerAccount(const char * domain /* [in] [ref] */, + const char * machine_name /* [in] [ref] */, + const char * machine_account_ou /* [in] [unique] */, + const char * dcname /* [in] [unique] */, + uint32_t options /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */); +WERROR NetProvisionComputerAccount_r(struct libnetapi_ctx *ctx, + struct NetProvisionComputerAccount *r); +WERROR NetProvisionComputerAccount_l(struct libnetapi_ctx *ctx, + struct NetProvisionComputerAccount *r); +NET_API_STATUS NetRequestOfflineDomainJoin(uint8_t *provision_bin_data /* [in] [unique] */, + uint32_t provision_bin_data_size /* [in] */, + uint32_t options /* [in] */, + const char * windows_path /* [in] [unique] */); +WERROR NetRequestOfflineDomainJoin_r(struct libnetapi_ctx *ctx, + struct NetRequestOfflineDomainJoin *r); +WERROR NetRequestOfflineDomainJoin_l(struct libnetapi_ctx *ctx, + struct NetRequestOfflineDomainJoin *r); +NET_API_STATUS NetComposeOfflineDomainJoin(const char *dns_domain_name /* [in] [ref] */, + const char *netbios_domain_name /* [in] [ref] */, + struct domsid *domain_sid /* [in] [ref] */, + struct GUID *domain_guid /* [in] [ref] */, + const char *forest_name /* [in] [ref] */, + const char *machine_account_name /* [in] [ref] */, + const char *machine_account_password /* [in] [ref] */, + const char *dc_name /* [in] [unique] */, + const char *dc_address /* [in] [unique] */, + int domain_is_ad /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */); +WERROR NetComposeOfflineDomainJoin_r(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r); +WERROR NetComposeOfflineDomainJoin_l(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r); +NET_API_STATUS NetServerGetInfo(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetServerGetInfo_r(struct libnetapi_ctx *ctx, + struct NetServerGetInfo *r); +WERROR NetServerGetInfo_l(struct libnetapi_ctx *ctx, + struct NetServerGetInfo *r); +NET_API_STATUS NetServerSetInfo(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_error /* [out] [ref] */); +WERROR NetServerSetInfo_r(struct libnetapi_ctx *ctx, + struct NetServerSetInfo *r); +WERROR NetServerSetInfo_l(struct libnetapi_ctx *ctx, + struct NetServerSetInfo *r); +NET_API_STATUS NetWkstaGetInfo(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetWkstaGetInfo_r(struct libnetapi_ctx *ctx, + struct NetWkstaGetInfo *r); +WERROR NetWkstaGetInfo_l(struct libnetapi_ctx *ctx, + struct NetWkstaGetInfo *r); +NET_API_STATUS NetGetDCName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [unique] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetGetDCName_r(struct libnetapi_ctx *ctx, + struct NetGetDCName *r); +WERROR NetGetDCName_l(struct libnetapi_ctx *ctx, + struct NetGetDCName *r); +NET_API_STATUS NetGetAnyDCName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [unique] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetGetAnyDCName_r(struct libnetapi_ctx *ctx, + struct NetGetAnyDCName *r); +WERROR NetGetAnyDCName_l(struct libnetapi_ctx *ctx, + struct NetGetAnyDCName *r); +NET_API_STATUS DsGetDcName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [ref] */, + struct GUID *domain_guid /* [in] [unique] */, + const char * site_name /* [in] [unique] */, + uint32_t flags /* [in] */, + struct DOMAIN_CONTROLLER_INFO **dc_info /* [out] [ref] */); +WERROR DsGetDcName_r(struct libnetapi_ctx *ctx, + struct DsGetDcName *r); +WERROR DsGetDcName_l(struct libnetapi_ctx *ctx, + struct DsGetDcName *r); +NET_API_STATUS NetUserAdd(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_error /* [out] [ref] */); +WERROR NetUserAdd_r(struct libnetapi_ctx *ctx, + struct NetUserAdd *r); +WERROR NetUserAdd_l(struct libnetapi_ctx *ctx, + struct NetUserAdd *r); +NET_API_STATUS NetUserDel(const char * server_name /* [in] [unique] */, + const char * user_name /* [in] [ref] */); +WERROR NetUserDel_r(struct libnetapi_ctx *ctx, + struct NetUserDel *r); +WERROR NetUserDel_l(struct libnetapi_ctx *ctx, + struct NetUserDel *r); +NET_API_STATUS NetUserEnum(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint32_t filter /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetUserEnum_r(struct libnetapi_ctx *ctx, + struct NetUserEnum *r); +WERROR NetUserEnum_l(struct libnetapi_ctx *ctx, + struct NetUserEnum *r); +NET_API_STATUS NetUserChangePassword(const char * domain_name /* [in] */, + const char * user_name /* [in] */, + const char * old_password /* [in] */, + const char * new_password /* [in] */); +WERROR NetUserChangePassword_r(struct libnetapi_ctx *ctx, + struct NetUserChangePassword *r); +WERROR NetUserChangePassword_l(struct libnetapi_ctx *ctx, + struct NetUserChangePassword *r); +NET_API_STATUS NetUserGetInfo(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx, + struct NetUserGetInfo *r); +WERROR NetUserGetInfo_l(struct libnetapi_ctx *ctx, + struct NetUserGetInfo *r); +NET_API_STATUS NetUserSetInfo(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx, + struct NetUserSetInfo *r); +WERROR NetUserSetInfo_l(struct libnetapi_ctx *ctx, + struct NetUserSetInfo *r); +NET_API_STATUS NetUserGetGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */); +WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx, + struct NetUserGetGroups *r); +WERROR NetUserGetGroups_l(struct libnetapi_ctx *ctx, + struct NetUserGetGroups *r); +NET_API_STATUS NetUserSetGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t num_entries /* [in] */); +WERROR NetUserSetGroups_r(struct libnetapi_ctx *ctx, + struct NetUserSetGroups *r); +WERROR NetUserSetGroups_l(struct libnetapi_ctx *ctx, + struct NetUserSetGroups *r); +NET_API_STATUS NetUserGetLocalGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint32_t flags /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */); +WERROR NetUserGetLocalGroups_r(struct libnetapi_ctx *ctx, + struct NetUserGetLocalGroups *r); +WERROR NetUserGetLocalGroups_l(struct libnetapi_ctx *ctx, + struct NetUserGetLocalGroups *r); +NET_API_STATUS NetUserModalsGet(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetUserModalsGet_r(struct libnetapi_ctx *ctx, + struct NetUserModalsGet *r); +WERROR NetUserModalsGet_l(struct libnetapi_ctx *ctx, + struct NetUserModalsGet *r); +NET_API_STATUS NetUserModalsSet(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetUserModalsSet_r(struct libnetapi_ctx *ctx, + struct NetUserModalsSet *r); +WERROR NetUserModalsSet_l(struct libnetapi_ctx *ctx, + struct NetUserModalsSet *r); +NET_API_STATUS NetQueryDisplayInformation(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint32_t idx /* [in] */, + uint32_t entries_requested /* [in] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + void **buffer /* [out] [noprint,ref] */); +WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx, + struct NetQueryDisplayInformation *r); +WERROR NetQueryDisplayInformation_l(struct libnetapi_ctx *ctx, + struct NetQueryDisplayInformation *r); +NET_API_STATUS NetGroupAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx, + struct NetGroupAdd *r); +WERROR NetGroupAdd_l(struct libnetapi_ctx *ctx, + struct NetGroupAdd *r); +NET_API_STATUS NetGroupDel(const char * server_name /* [in] */, + const char * group_name /* [in] */); +WERROR NetGroupDel_r(struct libnetapi_ctx *ctx, + struct NetGroupDel *r); +WERROR NetGroupDel_l(struct libnetapi_ctx *ctx, + struct NetGroupDel *r); +NET_API_STATUS NetGroupEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetGroupEnum_r(struct libnetapi_ctx *ctx, + struct NetGroupEnum *r); +WERROR NetGroupEnum_l(struct libnetapi_ctx *ctx, + struct NetGroupEnum *r); +NET_API_STATUS NetGroupSetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx, + struct NetGroupSetInfo *r); +WERROR NetGroupSetInfo_l(struct libnetapi_ctx *ctx, + struct NetGroupSetInfo *r); +NET_API_STATUS NetGroupGetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx, + struct NetGroupGetInfo *r); +WERROR NetGroupGetInfo_l(struct libnetapi_ctx *ctx, + struct NetGroupGetInfo *r); +NET_API_STATUS NetGroupAddUser(const char * server_name /* [in] */, + const char * group_name /* [in] */, + const char * user_name /* [in] */); +WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx, + struct NetGroupAddUser *r); +WERROR NetGroupAddUser_l(struct libnetapi_ctx *ctx, + struct NetGroupAddUser *r); +NET_API_STATUS NetGroupDelUser(const char * server_name /* [in] */, + const char * group_name /* [in] */, + const char * user_name /* [in] */); +WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx, + struct NetGroupDelUser *r); +WERROR NetGroupDelUser_l(struct libnetapi_ctx *ctx, + struct NetGroupDelUser *r); +NET_API_STATUS NetGroupGetUsers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetGroupGetUsers_r(struct libnetapi_ctx *ctx, + struct NetGroupGetUsers *r); +WERROR NetGroupGetUsers_l(struct libnetapi_ctx *ctx, + struct NetGroupGetUsers *r); +NET_API_STATUS NetGroupSetUsers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t num_entries /* [in] */); +WERROR NetGroupSetUsers_r(struct libnetapi_ctx *ctx, + struct NetGroupSetUsers *r); +WERROR NetGroupSetUsers_l(struct libnetapi_ctx *ctx, + struct NetGroupSetUsers *r); +NET_API_STATUS NetLocalGroupAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetLocalGroupAdd_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupAdd *r); +WERROR NetLocalGroupAdd_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupAdd *r); +NET_API_STATUS NetLocalGroupDel(const char * server_name /* [in] */, + const char * group_name /* [in] */); +WERROR NetLocalGroupDel_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupDel *r); +WERROR NetLocalGroupDel_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupDel *r); +NET_API_STATUS NetLocalGroupGetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetLocalGroupGetInfo_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetInfo *r); +WERROR NetLocalGroupGetInfo_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetInfo *r); +NET_API_STATUS NetLocalGroupSetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetLocalGroupSetInfo_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetInfo *r); +WERROR NetLocalGroupSetInfo_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetInfo *r); +NET_API_STATUS NetLocalGroupEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetLocalGroupEnum_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupEnum *r); +WERROR NetLocalGroupEnum_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupEnum *r); +NET_API_STATUS NetLocalGroupAddMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */); +WERROR NetLocalGroupAddMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupAddMembers *r); +WERROR NetLocalGroupAddMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupAddMembers *r); +NET_API_STATUS NetLocalGroupDelMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */); +WERROR NetLocalGroupDelMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupDelMembers *r); +WERROR NetLocalGroupDelMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupDelMembers *r); +NET_API_STATUS NetLocalGroupGetMembers(const char * server_name /* [in] */, + const char * local_group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetLocalGroupGetMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetMembers *r); +WERROR NetLocalGroupGetMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetMembers *r); +NET_API_STATUS NetLocalGroupSetMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */); +WERROR NetLocalGroupSetMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetMembers *r); +WERROR NetLocalGroupSetMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetMembers *r); +NET_API_STATUS NetRemoteTOD(const char * server_name /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetRemoteTOD_r(struct libnetapi_ctx *ctx, + struct NetRemoteTOD *r); +WERROR NetRemoteTOD_l(struct libnetapi_ctx *ctx, + struct NetRemoteTOD *r); +NET_API_STATUS NetShareAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetShareAdd_r(struct libnetapi_ctx *ctx, + struct NetShareAdd *r); +WERROR NetShareAdd_l(struct libnetapi_ctx *ctx, + struct NetShareAdd *r); +NET_API_STATUS NetShareDel(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t reserved /* [in] */); +WERROR NetShareDel_r(struct libnetapi_ctx *ctx, + struct NetShareDel *r); +WERROR NetShareDel_l(struct libnetapi_ctx *ctx, + struct NetShareDel *r); +NET_API_STATUS NetShareEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetShareEnum_r(struct libnetapi_ctx *ctx, + struct NetShareEnum *r); +WERROR NetShareEnum_l(struct libnetapi_ctx *ctx, + struct NetShareEnum *r); +NET_API_STATUS NetShareGetInfo(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetShareGetInfo_r(struct libnetapi_ctx *ctx, + struct NetShareGetInfo *r); +WERROR NetShareGetInfo_l(struct libnetapi_ctx *ctx, + struct NetShareGetInfo *r); +NET_API_STATUS NetShareSetInfo(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); +WERROR NetShareSetInfo_r(struct libnetapi_ctx *ctx, + struct NetShareSetInfo *r); +WERROR NetShareSetInfo_l(struct libnetapi_ctx *ctx, + struct NetShareSetInfo *r); +NET_API_STATUS NetFileClose(const char * server_name /* [in] */, + uint32_t fileid /* [in] */); +WERROR NetFileClose_r(struct libnetapi_ctx *ctx, + struct NetFileClose *r); +WERROR NetFileClose_l(struct libnetapi_ctx *ctx, + struct NetFileClose *r); +NET_API_STATUS NetFileGetInfo(const char * server_name /* [in] */, + uint32_t fileid /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR NetFileGetInfo_r(struct libnetapi_ctx *ctx, + struct NetFileGetInfo *r); +WERROR NetFileGetInfo_l(struct libnetapi_ctx *ctx, + struct NetFileGetInfo *r); +NET_API_STATUS NetFileEnum(const char * server_name /* [in] */, + const char * base_path /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); +WERROR NetFileEnum_r(struct libnetapi_ctx *ctx, + struct NetFileEnum *r); +WERROR NetFileEnum_l(struct libnetapi_ctx *ctx, + struct NetFileEnum *r); +NET_API_STATUS NetShutdownInit(const char * server_name /* [in] */, + const char * message /* [in] */, + uint32_t timeout /* [in] */, + uint8_t force_apps /* [in] */, + uint8_t do_reboot /* [in] */); +WERROR NetShutdownInit_r(struct libnetapi_ctx *ctx, + struct NetShutdownInit *r); +WERROR NetShutdownInit_l(struct libnetapi_ctx *ctx, + struct NetShutdownInit *r); +NET_API_STATUS NetShutdownAbort(const char * server_name /* [in] */); +WERROR NetShutdownAbort_r(struct libnetapi_ctx *ctx, + struct NetShutdownAbort *r); +WERROR NetShutdownAbort_l(struct libnetapi_ctx *ctx, + struct NetShutdownAbort *r); +NET_API_STATUS I_NetLogonControl(const char * server_name /* [in] */, + uint32_t function_code /* [in] */, + uint32_t query_level /* [in] */, + uint8_t **buffer /* [out] [ref] */); +WERROR I_NetLogonControl_r(struct libnetapi_ctx *ctx, + struct I_NetLogonControl *r); +WERROR I_NetLogonControl_l(struct libnetapi_ctx *ctx, + struct I_NetLogonControl *r); +NET_API_STATUS I_NetLogonControl2(const char * server_name /* [in] */, + uint32_t function_code /* [in] */, + uint32_t query_level /* [in] */, + uint8_t *data /* [in] [unique] */, + uint8_t **buffer /* [out] [ref] */); +WERROR I_NetLogonControl2_r(struct libnetapi_ctx *ctx, + struct I_NetLogonControl2 *r); +WERROR I_NetLogonControl2_l(struct libnetapi_ctx *ctx, + struct I_NetLogonControl2 *r); +#endif /* __LIBNETAPI_LIBNETAPI__ */ diff --git a/source3/lib/netapi/localgroup.c b/source3/lib/netapi/localgroup.c new file mode 100644 index 0000000..a63fca4 --- /dev/null +++ b/source3/lib/netapi/localgroup.c @@ -0,0 +1,1390 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi LocalGroup Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "rpc_client/cli_lsarpc.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" + +static NTSTATUS libnetapi_samr_lookup_and_open_alias(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + const char *group_name, + uint32_t access_rights, + struct policy_handle *alias_handle) +{ + NTSTATUS status, result; + + struct lsa_String lsa_account_name; + struct samr_Ids user_rids, name_types; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + init_lsa_String(&lsa_account_name, group_name); + + status = dcerpc_samr_LookupNames(b, mem_ctx, + domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + return status; + } + if (user_rids.count != 1) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (name_types.count != 1) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + switch (name_types.ids[0]) { + case SID_NAME_ALIAS: + case SID_NAME_WKN_GRP: + break; + default: + return NT_STATUS_INVALID_SID; + } + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + domain_handle, + access_rights, + user_rids.ids[0], + alias_handle, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return result; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS libnetapi_samr_open_alias_queryinfo(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *handle, + uint32_t rid, + uint32_t access_rights, + enum samr_AliasInfoEnum level, + union samr_AliasInfo **alias_info) +{ + NTSTATUS status, result; + struct policy_handle alias_handle; + union samr_AliasInfo *_alias_info = NULL; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + ZERO_STRUCT(alias_handle); + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + handle, + access_rights, + rid, + &alias_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + status = dcerpc_samr_QueryAliasInfo(b, mem_ctx, + &alias_handle, + level, + &_alias_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + *alias_info = _alias_info; + + done: + if (is_valid_policy_hnd(&alias_handle)) { + dcerpc_samr_Close(b, mem_ctx, &alias_handle, &result); + } + + return status; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupAdd_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupAdd *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct lsa_String lsa_account_name; + struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle; + struct dom_sid2 *domain_sid = NULL; + uint32_t rid; + struct dcerpc_binding_handle *b = NULL; + + struct LOCALGROUP_INFO_0 *info0 = NULL; + struct LOCALGROUP_INFO_1 *info1 = NULL; + + const char *alias_name = NULL; + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(alias_handle); + + switch (r->in.level) { + case 0: + info0 = (struct LOCALGROUP_INFO_0 *)r->in.buffer; + alias_name = info0->lgrpi0_name; + break; + case 1: + info1 = (struct LOCALGROUP_INFO_1 *)r->in.buffer; + alias_name = info1->lgrpi1_name; + break; + default: + werr = WERR_INVALID_LEVEL; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &builtin_handle, + alias_name, + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + &alias_handle); + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + } + + if (NT_STATUS_IS_OK(status)) { + werr = WERR_ALIAS_EXISTS; + goto done; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_CREATE_ALIAS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, alias_name); + + status = dcerpc_samr_CreateDomAlias(b, talloc_tos(), + &domain_handle, + &lsa_account_name, + SEC_STD_DELETE | + SAMR_ALIAS_ACCESS_SET_INFO, + &alias_handle, + &rid, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (r->in.level == 1 && info1->lgrpi1_comment) { + + union samr_AliasInfo alias_info; + + init_lsa_String(&alias_info.description, info1->lgrpi1_comment); + + status = dcerpc_samr_SetAliasInfo(b, talloc_tos(), + &alias_handle, + ALIASINFODESCRIPTION, + &alias_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&alias_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &alias_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupAdd_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupAdd *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupAdd); +} + +/**************************************************************** +****************************************************************/ + + +WERROR NetLocalGroupDel_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupDel *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle; + struct dom_sid2 *domain_sid = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(alias_handle); + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &builtin_handle, + r->in.group_name, + SEC_STD_DELETE, + &alias_handle); + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + } + + if (NT_STATUS_IS_OK(status)) { + goto delete_alias; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_CREATE_ALIAS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &domain_handle, + r->in.group_name, + SEC_STD_DELETE, + &alias_handle); + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + } + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + + delete_alias: + status = dcerpc_samr_DeleteDomAlias(b, talloc_tos(), + &alias_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + ZERO_STRUCT(alias_handle); + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&alias_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &alias_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupDel_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupDel *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupDel); +} + +/**************************************************************** +****************************************************************/ + +static WERROR map_alias_info_to_buffer(TALLOC_CTX *mem_ctx, + const char *alias_name, + struct samr_AliasInfoAll *info, + uint32_t level, + uint32_t *entries_read, + uint8_t **buffer) +{ + struct LOCALGROUP_INFO_0 g0; + struct LOCALGROUP_INFO_1 g1; + struct LOCALGROUP_INFO_1002 g1002; + + switch (level) { + case 0: + g0.lgrpi0_name = talloc_strdup(mem_ctx, alias_name); + W_ERROR_HAVE_NO_MEMORY(g0.lgrpi0_name); + + ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_INFO_0, g0, + (struct LOCALGROUP_INFO_0 **)buffer, entries_read); + + break; + case 1: + g1.lgrpi1_name = talloc_strdup(mem_ctx, alias_name); + g1.lgrpi1_comment = talloc_strdup(mem_ctx, info->description.string); + W_ERROR_HAVE_NO_MEMORY(g1.lgrpi1_name); + + ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_INFO_1, g1, + (struct LOCALGROUP_INFO_1 **)buffer, entries_read); + + break; + case 1002: + g1002.lgrpi1002_comment = talloc_strdup(mem_ctx, info->description.string); + + ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_INFO_1002, g1002, + (struct LOCALGROUP_INFO_1002 **)buffer, entries_read); + + break; + default: + return WERR_INVALID_LEVEL; + } + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupGetInfo_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetInfo *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle; + struct dom_sid2 *domain_sid = NULL; + union samr_AliasInfo *alias_info = NULL; + uint32_t entries_read = 0; + struct dcerpc_binding_handle *b = NULL; + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + case 1002: + break; + default: + return WERR_INVALID_LEVEL; + } + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(alias_handle); + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &builtin_handle, + r->in.group_name, + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + &alias_handle); + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + } + + if (NT_STATUS_IS_OK(status)) { + goto query_alias; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_CREATE_ALIAS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &domain_handle, + r->in.group_name, + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + &alias_handle); + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + } + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + query_alias: + status = dcerpc_samr_QueryAliasInfo(b, talloc_tos(), + &alias_handle, + ALIASINFOALL, + &alias_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = map_alias_info_to_buffer(ctx, + r->in.group_name, + &alias_info->all, + r->in.level, &entries_read, + r->out.buffer); + + done: + if (is_valid_policy_hnd(&alias_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &alias_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupGetInfo_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupGetInfo); +} + +/**************************************************************** +****************************************************************/ + +static WERROR map_buffer_to_alias_info(TALLOC_CTX *mem_ctx, + uint32_t level, + uint8_t *buffer, + enum samr_AliasInfoEnum *alias_level, + union samr_AliasInfo **alias_info) +{ + struct LOCALGROUP_INFO_0 *info0; + struct LOCALGROUP_INFO_1 *info1; + struct LOCALGROUP_INFO_1002 *info1002; + union samr_AliasInfo *info = NULL; + + info = talloc_zero(mem_ctx, union samr_AliasInfo); + W_ERROR_HAVE_NO_MEMORY(info); + + switch (level) { + case 0: + info0 = (struct LOCALGROUP_INFO_0 *)buffer; + init_lsa_String(&info->name, info0->lgrpi0_name); + *alias_level = ALIASINFONAME; + break; + case 1: + info1 = (struct LOCALGROUP_INFO_1 *)buffer; + /* group name will be ignored */ + init_lsa_String(&info->description, info1->lgrpi1_comment); + *alias_level = ALIASINFODESCRIPTION; + break; + case 1002: + info1002 = (struct LOCALGROUP_INFO_1002 *)buffer; + init_lsa_String(&info->description, info1002->lgrpi1002_comment); + *alias_level = ALIASINFODESCRIPTION; + break; + } + + *alias_info = info; + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupSetInfo_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetInfo *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct lsa_String lsa_account_name; + struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle; + struct dom_sid2 *domain_sid = NULL; + enum samr_AliasInfoEnum alias_level = 0; + union samr_AliasInfo *alias_info = NULL; + struct dcerpc_binding_handle *b = NULL; + + if (!r->in.group_name) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + case 1002: + break; + default: + return WERR_INVALID_LEVEL; + } + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(alias_handle); + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.group_name); + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &builtin_handle, + r->in.group_name, + SAMR_ALIAS_ACCESS_SET_INFO, + &alias_handle); + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + } + + if (NT_STATUS_IS_OK(status)) { + goto set_alias; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &domain_handle, + r->in.group_name, + SAMR_ALIAS_ACCESS_SET_INFO, + &alias_handle); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + } + + set_alias: + + werr = map_buffer_to_alias_info(ctx, r->in.level, r->in.buffer, + &alias_level, &alias_info); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_samr_SetAliasInfo(b, talloc_tos(), + &alias_handle, + alias_level, + alias_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&alias_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &alias_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupSetInfo_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupSetInfo); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupEnum_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupEnum *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle; + struct dom_sid2 *domain_sid = NULL; + uint32_t entries_read = 0; + union samr_DomainInfo *domain_info = NULL; + union samr_DomainInfo *builtin_info = NULL; + struct samr_SamArray *domain_sam_array = NULL; + struct samr_SamArray *builtin_sam_array = NULL; + int i; + struct dcerpc_binding_handle *b = NULL; + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + break; + default: + return WERR_INVALID_LEVEL; + } + + if (r->out.total_entries) { + *r->out.total_entries = 0; + } + if (r->out.entries_read) { + *r->out.entries_read = 0; + } + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(alias_handle); + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_samr_QueryDomainInfo(b, talloc_tos(), + &builtin_handle, + 2, + &builtin_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (r->out.total_entries) { + *r->out.total_entries += builtin_info->general.num_aliases; + } + + status = dcerpc_samr_QueryDomainInfo(b, talloc_tos(), + &domain_handle, + 2, + &domain_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (r->out.total_entries) { + *r->out.total_entries += domain_info->general.num_aliases; + } + + status = dcerpc_samr_EnumDomainAliases(b, talloc_tos(), + &builtin_handle, + r->in.resume_handle, + &builtin_sam_array, + r->in.prefmaxlen, + &entries_read, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + for (i=0; i<builtin_sam_array->count; i++) { + union samr_AliasInfo *alias_info = NULL; + + if (r->in.level == 1) { + + status = libnetapi_samr_open_alias_queryinfo(ctx, pipe_cli, + &builtin_handle, + builtin_sam_array->entries[i].idx, + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + ALIASINFOALL, + &alias_info); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + werr = map_alias_info_to_buffer(ctx, + builtin_sam_array->entries[i].name.string, + alias_info ? &alias_info->all : NULL, + r->in.level, + r->out.entries_read, + r->out.buffer); + } + + status = dcerpc_samr_EnumDomainAliases(b, talloc_tos(), + &domain_handle, + r->in.resume_handle, + &domain_sam_array, + r->in.prefmaxlen, + &entries_read, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + for (i=0; i<domain_sam_array->count; i++) { + + union samr_AliasInfo *alias_info = NULL; + + if (r->in.level == 1) { + status = libnetapi_samr_open_alias_queryinfo(ctx, pipe_cli, + &domain_handle, + domain_sam_array->entries[i].idx, + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + ALIASINFOALL, + &alias_info); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + werr = map_alias_info_to_buffer(ctx, + domain_sam_array->entries[i].name.string, + alias_info ? &alias_info->all : NULL, + r->in.level, + r->out.entries_read, + r->out.buffer); + } + + done: + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupEnum_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupEnum *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupEnum); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS libnetapi_lsa_lookup_names3(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *lsa_pipe, + const char *name, + struct dom_sid *sid) +{ + NTSTATUS status, result; + struct policy_handle lsa_handle; + struct dcerpc_binding_handle *b = lsa_pipe->binding_handle; + + struct lsa_RefDomainList *domains = NULL; + struct lsa_TransSidArray3 sids; + uint32_t count = 0; + + struct lsa_String names; + uint32_t num_names = 1; + union lsa_revision_info out_revision_info = { + .info1 = { + .revision = 0, + }, + }; + uint32_t out_version = 0; + + if (!sid || !name) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(sids); + + init_lsa_String(&names, name); + + status = dcerpc_lsa_open_policy_fallback( + b, + mem_ctx, + lsa_pipe->srv_name_slash, + false, + SEC_STD_READ_CONTROL | + LSA_POLICY_VIEW_LOCAL_INFORMATION | + LSA_POLICY_LOOKUP_NAMES, + &out_version, + &out_revision_info, + &lsa_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + return status; + } + + status = dcerpc_lsa_LookupNames3(b, mem_ctx, + &lsa_handle, + num_names, + &names, + &domains, + &sids, + LSA_LOOKUP_NAMES_ALL, /* sure ? */ + &count, + 0, 0, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + if (count != 1 || sids.count != 1) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + sid_copy(sid, sids.sids[0].sid); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetLocalGroupModifyMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupAddMembers *add, + struct NetLocalGroupDelMembers *del, + struct NetLocalGroupSetMembers *set) +{ + struct NetLocalGroupAddMembers *r = NULL; + + struct rpc_pipe_client *pipe_cli = NULL; + struct rpc_pipe_client *lsa_pipe = NULL; + NTSTATUS status, result; + WERROR werr; + struct lsa_String lsa_account_name; + struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle; + struct dom_sid2 *domain_sid = NULL; + struct dom_sid *member_sids = NULL; + int i = 0, k = 0; + + struct LOCALGROUP_MEMBERS_INFO_0 *info0 = NULL; + struct LOCALGROUP_MEMBERS_INFO_3 *info3 = NULL; + + struct dom_sid *add_sids = NULL; + struct dom_sid *del_sids = NULL; + uint32_t num_add_sids = 0; + uint32_t num_del_sids = 0; + struct dcerpc_binding_handle *b = NULL; + + if ((!add && !del && !set) || (add && del && set)) { + return WERR_INVALID_PARAMETER; + } + + if (add) { + r = add; + } + + if (del) { + r = (struct NetLocalGroupAddMembers *)del; + } + + if (set) { + r = (struct NetLocalGroupAddMembers *)set; + } + + if (r==NULL || r->in.group_name == NULL) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 3: + break; + default: + return WERR_INVALID_LEVEL; + } + + if (r->in.total_entries == 0 || !r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(alias_handle); + + member_sids = talloc_zero_array(ctx, struct dom_sid, + r->in.total_entries); + W_ERROR_HAVE_NO_MEMORY(member_sids); + + switch (r->in.level) { + case 0: + info0 = (struct LOCALGROUP_MEMBERS_INFO_0 *)r->in.buffer; + for (i=0; i < r->in.total_entries; i++) { + sid_copy(&member_sids[i], (struct dom_sid *)info0[i].lgrmi0_sid); + } + break; + case 3: + info3 = (struct LOCALGROUP_MEMBERS_INFO_3 *)r->in.buffer; + break; + default: + break; + } + + if (r->in.level == 3) { + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_lsarpc, + &lsa_pipe); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + for (i=0; i < r->in.total_entries; i++) { + status = libnetapi_lsa_lookup_names3(ctx, lsa_pipe, + info3[i].lgrmi3_domainandname, + &member_sids[i]); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + TALLOC_FREE(lsa_pipe); + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_LOOKUP_DOMAIN | + SAMR_ACCESS_ENUM_DOMAINS, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.group_name); + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &builtin_handle, + r->in.group_name, + SAMR_ALIAS_ACCESS_ADD_MEMBER | + SAMR_ALIAS_ACCESS_REMOVE_MEMBER | + SAMR_ALIAS_ACCESS_GET_MEMBERS | + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + &alias_handle); + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + } + + if (NT_STATUS_IS_OK(status)) { + goto modify_membership; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli, + &domain_handle, + r->in.group_name, + SAMR_ALIAS_ACCESS_ADD_MEMBER | + SAMR_ALIAS_ACCESS_REMOVE_MEMBER | + SAMR_ALIAS_ACCESS_GET_MEMBERS | + SAMR_ALIAS_ACCESS_LOOKUP_INFO, + &alias_handle); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + } + + modify_membership: + + if (add) { + for (i=0; i < r->in.total_entries; i++) { + status = add_sid_to_array_unique(ctx, &member_sids[i], + &add_sids, + &num_add_sids); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + } + + if (del) { + for (i=0; i < r->in.total_entries; i++) { + status = add_sid_to_array_unique(ctx, &member_sids[i], + &del_sids, + &num_del_sids); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + } + + if (set) { + + struct lsa_SidArray current_sids; + + status = dcerpc_samr_GetMembersInAlias(b, talloc_tos(), + &alias_handle, + ¤t_sids, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + /* add list */ + + for (i=0; i < r->in.total_entries; i++) { + bool already_member = false; + for (k=0; k < current_sids.num_sids; k++) { + if (dom_sid_equal(&member_sids[i], + current_sids.sids[k].sid)) { + already_member = true; + break; + } + } + if (!already_member) { + status = add_sid_to_array_unique(ctx, + &member_sids[i], + &add_sids, &num_add_sids); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + } + + /* del list */ + + for (k=0; k < current_sids.num_sids; k++) { + bool keep_member = false; + for (i=0; i < r->in.total_entries; i++) { + if (dom_sid_equal(&member_sids[i], + current_sids.sids[k].sid)) { + keep_member = true; + break; + } + } + if (!keep_member) { + status = add_sid_to_array_unique(ctx, + current_sids.sids[k].sid, + &del_sids, &num_del_sids); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + } + } + + /* add list */ + + for (i=0; i < num_add_sids; i++) { + status = dcerpc_samr_AddAliasMember(b, talloc_tos(), + &alias_handle, + &add_sids[i], + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + /* del list */ + + for (i=0; i < num_del_sids; i++) { + status = dcerpc_samr_DeleteAliasMember(b, talloc_tos(), + &alias_handle, + &del_sids[i], + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + werr = WERR_OK; + + done: + if (b && is_valid_policy_hnd(&alias_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &alias_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupAddMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupAddMembers *r) +{ + return NetLocalGroupModifyMembers_r(ctx, r, NULL, NULL); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupAddMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupAddMembers *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupAddMembers); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupDelMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupDelMembers *r) +{ + return NetLocalGroupModifyMembers_r(ctx, NULL, r, NULL); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupDelMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupDelMembers *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupDelMembers); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupGetMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetMembers *r) +{ + return WERR_NOT_SUPPORTED; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupGetMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupGetMembers *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupGetMembers); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupSetMembers_r(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetMembers *r) +{ + return NetLocalGroupModifyMembers_r(ctx, NULL, NULL, r); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetLocalGroupSetMembers_l(struct libnetapi_ctx *ctx, + struct NetLocalGroupSetMembers *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupSetMembers); +} diff --git a/source3/lib/netapi/netapi.c b/source3/lib/netapi/netapi.c new file mode 100644 index 0000000..9c049a8 --- /dev/null +++ b/source3/lib/netapi/netapi.c @@ -0,0 +1,523 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "../libcli/auth/netlogon_creds_cli.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "secrets.h" +#include "krb5_env.h" +#include "source3/param/loadparm.h" +#include "lib/param/param.h" +#include "auth/gensec/gensec.h" + +struct libnetapi_ctx *stat_ctx = NULL; +static bool libnetapi_initialized = false; + +/**************************************************************** +****************************************************************/ + +static NET_API_STATUS libnetapi_init_private_context(struct libnetapi_ctx *ctx) +{ + struct libnetapi_private_ctx *priv; + + if (!ctx) { + return W_ERROR_V(WERR_INVALID_PARAMETER); + } + + priv = talloc_zero(ctx, struct libnetapi_private_ctx); + if (!priv) { + return W_ERROR_V(WERR_NOT_ENOUGH_MEMORY); + } + + ctx->private_data = priv; + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +Create a libnetapi context, for use in non-Samba applications. This +loads the smb.conf file and sets the debug level to 0, so that +applications are not flooded with debug logs at level 10, when they +were not expecting it. +****************************************************************/ + +NET_API_STATUS libnetapi_init(struct libnetapi_ctx **context) +{ + NET_API_STATUS ret; + TALLOC_CTX *frame; + struct loadparm_context *lp_ctx = NULL; + + if (stat_ctx && libnetapi_initialized) { + *context = stat_ctx; + return NET_API_STATUS_SUCCESS; + } + +#if 0 + talloc_enable_leak_report(); +#endif + frame = talloc_stackframe(); + + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_NOT_ENOUGH_MEMORY); + } + + /* When libnetapi is invoked from an application, it does not + * want to be swamped with level 10 debug messages, even if + * this has been set for the server in smb.conf */ + lpcfg_set_cmdline(lp_ctx, "log level", "0"); + setup_logging("libnetapi", DEBUG_STDERR); + + if (!lp_load_global(get_dyn_CONFIGFILE())) { + TALLOC_FREE(frame); + fprintf(stderr, "error loading %s\n", get_dyn_CONFIGFILE() ); + return W_ERROR_V(WERR_GEN_FAILURE); + } + + load_interfaces(); + reopen_logs(); + + BlockSignals(True, SIGPIPE); + + ret = libnetapi_net_init(context, lp_ctx, NULL); + if (ret == NET_API_STATUS_SUCCESS) { + talloc_steal(*context, lp_ctx); + } + TALLOC_FREE(frame); + return ret; +} + +/**************************************************************** +Create a libnetapi context, for use inside the 'net' binary. + +As we know net has already loaded the smb.conf file, and set the debug +level etc, this avoids doing so again (which causes trouble with -d on +the command line). +****************************************************************/ + +NET_API_STATUS libnetapi_net_init(struct libnetapi_ctx **context, + struct loadparm_context *lp_ctx, + struct cli_credentials *creds) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + TALLOC_CTX *frame = NULL; + + if (stat_ctx != NULL && libnetapi_initialized) { + *context = stat_ctx; + return NET_API_STATUS_SUCCESS; + } + + frame = talloc_stackframe(); + ctx = talloc_zero(frame, struct libnetapi_ctx); + if (!ctx) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_NOT_ENOUGH_MEMORY); + } + + ctx->lp_ctx = lp_ctx; + + ctx->creds = creds; + if (ctx->creds == NULL) { + ctx->creds = cli_credentials_init(ctx); + if (ctx->creds == NULL) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_NOT_ENOUGH_MEMORY); + } + /* Ignore return code, as we might not have a smb.conf */ + (void)cli_credentials_guess(ctx->creds, lp_ctx); + } + + BlockSignals(True, SIGPIPE); + + status = libnetapi_init_private_context(ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + libnetapi_initialized = true; + + talloc_steal(NULL, ctx); + *context = stat_ctx = ctx; + + TALLOC_FREE(frame); + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** + Return the static libnetapi context +****************************************************************/ + +NET_API_STATUS libnetapi_getctx(struct libnetapi_ctx **ctx) +{ + if (stat_ctx) { + *ctx = stat_ctx; + return NET_API_STATUS_SUCCESS; + } + + return libnetapi_init(ctx); +} + +/**************************************************************** + Free the static libnetapi context +****************************************************************/ + +NET_API_STATUS libnetapi_free(struct libnetapi_ctx *ctx) +{ + TALLOC_CTX *frame; + + if (!ctx) { + return NET_API_STATUS_SUCCESS; + } + + frame = talloc_stackframe(); + libnetapi_samr_free(ctx); + + libnetapi_shutdown_cm(ctx); + + gfree_loadparm(); + gfree_charcnv(); + gfree_interfaces(); + + secrets_shutdown(); + + netlogon_creds_cli_close_global_db(); + + if (ctx == stat_ctx) { + stat_ctx = NULL; + } + TALLOC_FREE(ctx); + + gfree_debugsyms(); + talloc_free(frame); + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** + Override the current log level for libnetapi +****************************************************************/ + +NET_API_STATUS libnetapi_set_debuglevel(struct libnetapi_ctx *ctx, + const char *debuglevel) +{ + TALLOC_CTX *frame = talloc_stackframe(); + ctx->debuglevel = talloc_strdup(ctx, debuglevel); + + if (!lpcfg_set_cmdline(ctx->lp_ctx, "log level", debuglevel)) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_GEN_FAILURE); + } + TALLOC_FREE(frame); + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_logfile(struct libnetapi_ctx *ctx, + const char *logfile) +{ + TALLOC_CTX *frame = talloc_stackframe(); + ctx->logfile = talloc_strdup(ctx, logfile); + + if (!lpcfg_set_cmdline(ctx->lp_ctx, "log file", logfile)) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_GEN_FAILURE); + } + debug_set_logfile(logfile); + setup_logging("libnetapi", DEBUG_FILE); + TALLOC_FREE(frame); + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_get_debuglevel(struct libnetapi_ctx *ctx, + char **debuglevel) +{ + *debuglevel = ctx->debuglevel; + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +/** + * @brief Get the username of the libnet context + * + * @param[in] ctx The netapi context + * + * @param[in] username A pointer to hold the username. + * + * @return 0 on success, an werror code otherwise. + */ +NET_API_STATUS libnetapi_get_username(struct libnetapi_ctx *ctx, + const char **username) +{ + if (ctx == NULL) { + return W_ERROR_V(WERR_INVALID_PARAMETER); + } + + if (username != NULL) { + *username = cli_credentials_get_username(ctx->creds); + } + + return NET_API_STATUS_SUCCESS; +} + +/** + * @brief Get the password of the libnet context + * + * @param[in] ctx The netapi context + * + * @param[in] password A pointer to hold the password. + * + * @return 0 on success, an werror code otherwise. + */ +NET_API_STATUS libnetapi_get_password(struct libnetapi_ctx *ctx, + const char **password) +{ + if (ctx == NULL) { + return W_ERROR_V(WERR_INVALID_PARAMETER); + } + + if (password != NULL) { + *password = cli_credentials_get_password(ctx->creds); + } + + return NET_API_STATUS_SUCCESS; +} + +NET_API_STATUS libnetapi_set_username(struct libnetapi_ctx *ctx, + const char *username) +{ + if (ctx == NULL || username == NULL) { + return W_ERROR_V(WERR_INVALID_PARAMETER); + } + + cli_credentials_parse_string(ctx->creds, username, CRED_SPECIFIED); + + return NET_API_STATUS_SUCCESS; +} + +NET_API_STATUS libnetapi_set_password(struct libnetapi_ctx *ctx, + const char *password) +{ + bool ok; + + if (ctx == NULL || password == NULL) { + return W_ERROR_V(WERR_INVALID_PARAMETER); + } + + ok = cli_credentials_set_password(ctx->creds, password, CRED_SPECIFIED); + if (!ok) { + return W_ERROR_V(WERR_INTERNAL_ERROR); + } + + return NET_API_STATUS_SUCCESS; +} + +NET_API_STATUS libnetapi_set_workgroup(struct libnetapi_ctx *ctx, + const char *workgroup) +{ + bool ok; + + ok = cli_credentials_set_domain(ctx->creds, workgroup, CRED_SPECIFIED); + if (!ok) { + return W_ERROR_V(WERR_INTERNAL_ERROR); + } + + return NET_API_STATUS_SUCCESS; +} + +/** + * @brief Set the cli_credentials to be used in the netapi context + * + * @param[in] ctx The netapi context + * + * @param[in] creds The cli_credentials which should be used by netapi. + * + * @return 0 on success, an werror code otherwise. + */ +NET_API_STATUS libnetapi_set_creds(struct libnetapi_ctx *ctx, + struct cli_credentials *creds) +{ + if (ctx == NULL || creds == NULL) { + return W_ERROR_V(WERR_INVALID_PARAMETER); + } + + ctx->creds = creds; + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_use_kerberos(struct libnetapi_ctx *ctx) +{ + cli_credentials_set_kerberos_state(ctx->creds, + CRED_USE_KERBEROS_REQUIRED, + CRED_SPECIFIED); + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_get_use_kerberos(struct libnetapi_ctx *ctx, + int *use_kerberos) +{ + enum credentials_use_kerberos creds_use_kerberos; + + *use_kerberos = 0; + + creds_use_kerberos = cli_credentials_get_kerberos_state(ctx->creds); + if (creds_use_kerberos > CRED_USE_KERBEROS_DESIRED) { + *use_kerberos = 1; + } + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_use_ccache(struct libnetapi_ctx *ctx) +{ + uint32_t gensec_features; + + gensec_features = cli_credentials_get_gensec_features(ctx->creds); + gensec_features |= GENSEC_FEATURE_NTLM_CCACHE; + cli_credentials_set_gensec_features(ctx->creds, + gensec_features, + CRED_SPECIFIED); + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +Return a libnetapi error as a string, caller must free with NetApiBufferFree +****************************************************************/ + +char *libnetapi_errstr(NET_API_STATUS status) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *ret; + if (status & 0xc0000000) { + ret = talloc_strdup(NULL, + get_friendly_nt_error_msg(NT_STATUS(status))); + } else { + ret = talloc_strdup(NULL, + get_friendly_werror_msg(W_ERROR(status))); + } + TALLOC_FREE(frame); + return ret; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_error_string(struct libnetapi_ctx *ctx, + const char *format, ...) +{ + va_list args; + + TALLOC_FREE(ctx->error_string); + + va_start(args, format); + ctx->error_string = talloc_vasprintf(ctx, format, args); + va_end(args); + + if (!ctx->error_string) { + return W_ERROR_V(WERR_NOT_ENOUGH_MEMORY); + } + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +Return a libnetapi_errstr(), caller must free with NetApiBufferFree +****************************************************************/ + +char *libnetapi_get_error_string(struct libnetapi_ctx *ctx, + NET_API_STATUS status_in) +{ + NET_API_STATUS status; + struct libnetapi_ctx *tmp_ctx = ctx; + + if (!tmp_ctx) { + status = libnetapi_getctx(&tmp_ctx); + if (status != 0) { + return NULL; + } + } + + if (tmp_ctx->error_string) { + return talloc_strdup(NULL, tmp_ctx->error_string); + } + + return libnetapi_errstr(status_in); +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS NetApiBufferAllocate(uint32_t byte_count, + void **buffer) +{ + void *buf = NULL; + + if (!buffer) { + return W_ERROR_V(WERR_INSUFFICIENT_BUFFER); + } + + if (byte_count == 0) { + goto done; + } + + buf = talloc_size(NULL, byte_count); + if (!buf) { + return W_ERROR_V(WERR_NOT_ENOUGH_MEMORY); + } + + done: + *buffer = buf; + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS NetApiBufferFree(void *buffer) +{ + if (!buffer) { + return W_ERROR_V(WERR_INSUFFICIENT_BUFFER); + } + + talloc_free(buffer); + + return NET_API_STATUS_SUCCESS; +} diff --git a/source3/lib/netapi/netapi.h b/source3/lib/netapi/netapi.h new file mode 100644 index 0000000..660a776 --- /dev/null +++ b/source3/lib/netapi/netapi.h @@ -0,0 +1,2778 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2007-2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_NETAPI_H__ +#define __LIB_NETAPI_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct cli_credentials; + +/**************************************************************** + NET_API_STATUS +****************************************************************/ +typedef enum { + NET_API_STATUS_SUCCESS = 0 +} NET_API_STATUS; + +#define ERROR_MORE_DATA ( 234L ) + +#define ENCRYPTED_PWLEN ( 16 ) + +/**************************************************************** +****************************************************************/ + +#ifndef _HEADER_misc + +struct GUID { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq[2]; + uint8_t node[6]; +}; + +#endif /* _HEADER_misc */ + +#ifndef _HEADER_libnetapi + +#ifndef MAXSUBAUTHS +#define MAXSUBAUTHS 15 /* max sub authorities in a SID */ +#endif + +struct domsid { + uint8_t sid_rev_num; + uint8_t num_auths; + uint8_t id_auth[6]; + uint32_t sub_auths[MAXSUBAUTHS]; +}; + +struct DOMAIN_CONTROLLER_INFO { + const char * domain_controller_name; + const char * domain_controller_address; + uint32_t domain_controller_address_type; + struct GUID domain_guid; + const char * domain_name; + const char * dns_forest_name; + uint32_t flags; + const char * dc_site_name; + const char * client_site_name; +}; + +/* bitmap NetJoinFlags */ +#define NETSETUP_JOIN_DOMAIN ( 0x00000001 ) +#define NETSETUP_ACCT_CREATE ( 0x00000002 ) +#define NETSETUP_ACCT_DELETE ( 0x00000004 ) +#define NETSETUP_WIN9X_UPGRADE ( 0x00000010 ) +#define NETSETUP_DOMAIN_JOIN_IF_JOINED ( 0x00000020 ) +#define NETSETUP_JOIN_UNSECURE ( 0x00000040 ) +#define NETSETUP_MACHINE_PWD_PASSED ( 0x00000080 ) +#define NETSETUP_DEFER_SPN_SET ( 0x00000100 ) +#define NETSETUP_JOIN_DC_ACCOUNT ( 0x00000200 ) +#define NETSETUP_JOIN_WITH_NEW_NAME ( 0x00000400 ) +#define NETSETUP_JOIN_READONLY ( 0x00000800 ) +#define NETSETUP_AMBIGUOUS_DC ( 0x00001000 ) +#define NETSETUP_NO_NETLOGON_CACHE ( 0x00002000 ) +#define NETSETUP_DONT_CONTROL_SERVICES ( 0x00004000 ) +#define NETSETUP_SET_MACHINE_NAME ( 0x00008000 ) +#define NETSETUP_FORCE_SPN_SET ( 0x00010000 ) +#define NETSETUP_NO_ACCT_REUSE ( 0x00020000 ) +#define NETSETUP_INSTALL_INVOCATION ( 0x00040000 ) +#define NETSETUP_IGNORE_UNSUPPORTED_FLAGS ( 0x10000000 ) + +/* bitmap NetProvisionFlags */ +#define NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT ( 0x00000001 ) +#define NETSETUP_PROVISION_REUSE_ACCOUNT ( 0x00000002 ) +#define NETSETUP_PROVISION_USE_DEFAULT_PASSWORD ( 0x00000004 ) +#define NETSETUP_PROVISION_SKIP_ACCOUNT_SEARCH ( 0x00000008 ) +#define NETSETUP_PROVISION_ROOT_CA_CERTS ( 0x00000010 ) + +/* bitmap NetProvisionJoinFlags */ +#define NETSETUP_PROVISION_ONLINE_CALLER ( 0x40000000 ) + +#define FILTER_TEMP_DUPLICATE_ACCOUNT ( 0x0001 ) +#define FILTER_NORMAL_ACCOUNT ( 0x0002 ) +#define FILTER_INTERDOMAIN_TRUST_ACCOUNT ( 0x0008 ) +#define FILTER_WORKSTATION_TRUST_ACCOUNT ( 0x0010 ) +#define FILTER_SERVER_TRUST_ACCOUNT ( 0x0020 ) + +#define TIMEQ_FOREVER ( (uint32_t)-1L ) + +enum NETSETUP_JOIN_STATUS { + NetSetupUnknownStatus=0, + NetSetupUnjoined=1, + NetSetupWorkgroupName=2, + NetSetupDomainName=3 +}; + +struct SERVER_INFO_100 { + uint32_t sv100_platform_id; + const char * sv100_name; +}; + +struct SERVER_INFO_101 { + uint32_t sv101_platform_id; + const char * sv101_name; + uint32_t sv101_version_major; + uint32_t sv101_version_minor; + uint32_t sv101_type; + const char * sv101_comment; +}; + +struct SERVER_INFO_102 { + uint32_t sv102_platform_id; + const char * sv102_name; + uint32_t sv102_version_major; + uint32_t sv102_version_minor; + uint32_t sv102_type; + const char * sv102_comment; + uint32_t sv102_users; + uint32_t sv102_disc; + uint8_t sv102_hidden; + uint32_t sv102_announce; + uint32_t sv102_anndelta; + uint32_t sv102_licenses; + const char * sv102_userpath; +}; + +struct SERVER_INFO_402 { + uint32_t sv402_ulist_mtime; + uint32_t sv402_glist_mtime; + uint32_t sv402_alist_mtime; + const char * sv402_alerts; + uint32_t sv402_security; + uint32_t sv402_numadmin; + uint32_t sv402_lanmask; + const char * sv402_guestacct; + uint32_t sv402_chdevs; + uint32_t sv402_chdevq; + uint32_t sv402_chdevjobs; + uint32_t sv402_connections; + uint32_t sv402_shares; + uint32_t sv402_openfiles; + uint32_t sv402_sessopens; + uint32_t sv402_sessvcs; + uint32_t sv402_sessreqs; + uint32_t sv402_opensearch; + uint32_t sv402_activelocks; + uint32_t sv402_numreqbuf; + uint32_t sv402_sizreqbuf; + uint32_t sv402_numbigbuf; + uint32_t sv402_numfiletasks; + uint32_t sv402_alertsched; + uint32_t sv402_erroralert; + uint32_t sv402_logonalert; + uint32_t sv402_accessalert; + uint32_t sv402_diskalert; + uint32_t sv402_netioalert; + uint32_t sv402_maxauditsz; + const char * sv402_srvheuristics; +}; + +struct SERVER_INFO_403 { + uint32_t sv403_ulist_mtime; + uint32_t sv403_glist_mtime; + uint32_t sv403_alist_mtime; + const char * sv403_alerts; + uint32_t sv403_security; + uint32_t sv403_numadmin; + uint32_t sv403_lanmask; + const char * sv403_guestacct; + uint32_t sv403_chdevs; + uint32_t sv403_chdevq; + uint32_t sv403_chdevjobs; + uint32_t sv403_connections; + uint32_t sv403_shares; + uint32_t sv403_openfiles; + uint32_t sv403_sessopens; + uint32_t sv403_sessvcs; + uint32_t sv403_sessreqs; + uint32_t sv403_opensearch; + uint32_t sv403_activelocks; + uint32_t sv403_numreqbuf; + uint32_t sv403_sizreqbuf; + uint32_t sv403_numbigbuf; + uint32_t sv403_numfiletasks; + uint32_t sv403_alertsched; + uint32_t sv403_erroralert; + uint32_t sv403_logonalert; + uint32_t sv403_accessalert; + uint32_t sv403_diskalert; + uint32_t sv403_netioalert; + uint32_t sv403_maxauditsz; + const char * sv403_srvheuristics; + uint32_t sv403_auditedevents; + uint32_t sv403_autoprofile; + const char * sv403_autopath; +}; + +struct SERVER_INFO_502 { + uint32_t sv502_sessopens; + uint32_t sv502_sessvcs; + uint32_t sv502_opensearch; + uint32_t sv502_sizreqbuf; + uint32_t sv502_initworkitems; + uint32_t sv502_maxworkitems; + uint32_t sv502_rawworkitems; + uint32_t sv502_irpstacksize; + uint32_t sv502_maxrawbuflen; + uint32_t sv502_sessusers; + uint32_t sv502_sessconns; + uint32_t sv502_maxpagedmemoryusage; + uint32_t sv502_maxnonpagedmemoryusage; + uint8_t sv502_enablesoftcompat; + uint8_t sv502_enableforcedlogoff; + uint8_t sv502_timesource; + uint8_t sv502_acceptdownlevelapis; + uint8_t sv502_lmannounce; +}; + +struct SERVER_INFO_503 { + uint32_t sv503_sessopens; + uint32_t sv503_sessvcs; + uint32_t sv503_opensearch; + uint32_t sv503_sizreqbuf; + uint32_t sv503_initworkitems; + uint32_t sv503_maxworkitems; + uint32_t sv503_rawworkitems; + uint32_t sv503_irpstacksize; + uint32_t sv503_maxrawbuflen; + uint32_t sv503_sessusers; + uint32_t sv503_sessconns; + uint32_t sv503_maxpagedmemoryusage; + uint32_t sv503_maxnonpagedmemoryusage; + uint8_t sv503_enablesoftcompat; + uint8_t sv503_enableforcedlogoff; + uint8_t sv503_timesource; + uint8_t sv503_acceptdownlevelapis; + uint8_t sv503_lmannounce; + const char * sv503_domain; + uint32_t sv503_maxcopyreadlen; + uint32_t sv503_maxcopywritelen; + uint32_t sv503_minkeepsearch; + uint32_t sv503_maxkeepsearch; + uint32_t sv503_minkeepcomplsearch; + uint32_t sv503_maxkeepcomplsearch; + uint32_t sv503_threadcountadd; + uint32_t sv503_numblockthreads; + uint32_t sv503_scavtimeout; + uint32_t sv503_minrcvqueue; + uint32_t sv503_minfreeworkitems; + uint32_t sv503_xactmemsize; + uint32_t sv503_threadpriority; + uint32_t sv503_maxmpxct; + uint32_t sv503_oplockbreakwait; + uint32_t sv503_oplockbreakresponsewait; + uint8_t sv503_enableoplocks; + uint8_t sv503_enableoplockforceclose; + uint8_t sv503_enablefcbopens; + uint8_t sv503_enableraw; + uint8_t sv503_enablesharednetdrives; + uint32_t sv503_minfreeconnections; + uint32_t sv503_maxfreeconnections; +}; + +struct SERVER_INFO_599 { + uint32_t sv599_sessopens; + uint32_t sv599_sessvcs; + uint32_t sv599_opensearch; + uint32_t sv599_sizreqbuf; + uint32_t sv599_initworkitems; + uint32_t sv599_maxworkitems; + uint32_t sv599_rawworkitems; + uint32_t sv599_irpstacksize; + uint32_t sv599_maxrawbuflen; + uint32_t sv599_sessusers; + uint32_t sv599_sessconns; + uint32_t sv599_maxpagedmemoryusage; + uint32_t sv599_maxnonpagedmemoryusage; + uint8_t sv599_enablesoftcompat; + uint8_t sv599_enableforcedlogoff; + uint8_t sv599_timesource; + uint8_t sv599_acceptdownlevelapis; + uint8_t sv599_lmannounce; + const char * sv599_domain; + uint32_t sv599_maxcopyreadlen; + uint32_t sv599_maxcopywritelen; + uint32_t sv599_minkeepsearch; + uint32_t sv599_maxkeepsearch; + uint32_t sv599_minkeepcomplsearch; + uint32_t sv599_maxkeepcomplsearch; + uint32_t sv599_threadcountadd; + uint32_t sv599_numblockthreads; + uint32_t sv599_scavtimeout; + uint32_t sv599_minrcvqueue; + uint32_t sv599_minfreeworkitems; + uint32_t sv599_xactmemsize; + uint32_t sv599_threadpriority; + uint32_t sv599_maxmpxct; + uint32_t sv599_oplockbreakwait; + uint32_t sv599_oplockbreakresponsewait; + uint8_t sv599_enableoplocks; + uint8_t sv599_enableoplockforceclose; + uint8_t sv599_enablefcbopens; + uint8_t sv599_enableraw; + uint8_t sv599_enablesharednetdrives; + uint32_t sv599_minfreeconnections; + uint32_t sv599_maxfreeconnections; + uint32_t sv599_initsesstable; + uint32_t sv599_initconntable; + uint32_t sv599_initfiletable; + uint32_t sv599_initsearchtable; + uint32_t sv599_alertschedule; + uint32_t sv599_errorthreshold; + uint32_t sv599_networkerrorthreshold; + uint32_t sv599_diskspacethreshold; + uint32_t sv599_reserved; + uint32_t sv599_maxlinkdelay; + uint32_t sv599_minlinkthroughput; + uint32_t sv599_linkinfovalidtime; + uint32_t sv599_scavqosinfoupdatetime; + uint32_t sv599_maxworkitemidletime; +}; + +struct SERVER_INFO_598 { + uint32_t sv598_maxrawworkitems; + uint32_t sv598_maxthreadsperqueue; + uint32_t sv598_producttype; + uint32_t sv598_serversize; + uint32_t sv598_connectionlessautodisc; + uint32_t sv598_sharingviolationretries; + uint32_t sv598_sharingviolationdelay; + uint32_t sv598_maxglobalopensearch; + uint32_t sv598_removeduplicatesearches; + uint32_t sv598_lockviolationoffset; + uint32_t sv598_lockviolationdelay; + uint32_t sv598_mdlreadswitchover; + uint32_t sv598_cachedopenlimit; + uint32_t sv598_otherqueueaffinity; + uint8_t sv598_restrictnullsessaccess; + uint8_t sv598_enablewfw311directipx; + uint32_t sv598_queuesamplesecs; + uint32_t sv598_balancecount; + uint32_t sv598_preferredaffinity; + uint32_t sv598_maxfreerfcbs; + uint32_t sv598_maxfreemfcbs; + uint32_t sv598_maxfreelfcbs; + uint32_t sv598_maxfreepagedpoolchunks; + uint32_t sv598_minpagedpoolchunksize; + uint32_t sv598_maxpagedpoolchunksize; + uint8_t sv598_sendsfrompreferredprocessor; + uint32_t sv598_cacheddirectorylimit; + uint32_t sv598_maxcopylength; + uint8_t sv598_enablecompression; + uint8_t sv598_autosharewks; + uint8_t sv598_autoshareserver; + uint8_t sv598_enablesecuritysignature; + uint8_t sv598_requiresecuritysignature; + uint32_t sv598_minclientbuffersize; + struct GUID sv598_serverguid; + uint32_t sv598_ConnectionNoSessionsTimeout; + uint32_t sv598_IdleThreadTimeOut; + uint8_t sv598_enableW9xsecuritysignature; + uint8_t sv598_enforcekerberosreauthentication; + uint8_t sv598_disabledos; + uint32_t sv598_lowdiskspaceminimum; + uint8_t sv598_disablestrictnamechecking; +}; + +struct SERVER_INFO_1005 { + const char * sv1005_comment; +}; + +struct SERVER_INFO_1107 { + uint32_t sv1107_users; +}; + +struct SERVER_INFO_1010 { + int32_t sv1010_disc; +}; + +struct SERVER_INFO_1016 { + uint8_t sv1016_hidden; +}; + +struct SERVER_INFO_1017 { + uint32_t sv1017_announce; +}; + +struct SERVER_INFO_1018 { + uint32_t sv1018_anndelta; +}; + +struct SERVER_INFO_1501 { + uint32_t sv1501_sessopens; +}; + +struct SERVER_INFO_1502 { + uint32_t sv1502_sessvcs; +}; + +struct SERVER_INFO_1503 { + uint32_t sv1503_opensearch; +}; + +struct SERVER_INFO_1506 { + uint32_t sv1506_maxworkitems; +}; + +struct SERVER_INFO_1509 { + uint32_t sv1509_maxrawbuflen; +}; + +struct SERVER_INFO_1510 { + uint32_t sv1510_sessusers; +}; + +struct SERVER_INFO_1511 { + uint32_t sv1511_sessconns; +}; + +struct SERVER_INFO_1512 { + uint32_t sv1512_maxnonpagedmemoryusage; +}; + +struct SERVER_INFO_1513 { + uint32_t sv1513_maxpagedmemoryusage; +}; + +struct SERVER_INFO_1514 { + uint8_t sv1514_enablesoftcompat; +}; + +struct SERVER_INFO_1515 { + uint8_t sv1515_enableforcedlogoff; +}; + +struct SERVER_INFO_1516 { + uint8_t sv1516_timesource; +}; + +struct SERVER_INFO_1518 { + uint8_t sv1518_lmannounce; +}; + +struct SERVER_INFO_1520 { + uint32_t sv1520_maxcopyreadlen; +}; + +struct SERVER_INFO_1521 { + uint32_t sv1521_maxcopywritelen; +}; + +struct SERVER_INFO_1522 { + uint32_t sv1522_minkeepsearch; +}; + +struct SERVER_INFO_1523 { + uint32_t sv1523_maxkeepsearch; +}; + +struct SERVER_INFO_1524 { + uint32_t sv1524_minkeepcomplsearch; +}; + +struct SERVER_INFO_1525 { + uint32_t sv1525_maxkeepcomplsearch; +}; + +struct SERVER_INFO_1528 { + uint32_t sv1528_scavtimeout; +}; + +struct SERVER_INFO_1529 { + uint32_t sv1529_minrcvqueue; +}; + +struct SERVER_INFO_1530 { + uint32_t sv1530_minfreeworkitems; +}; + +struct SERVER_INFO_1533 { + uint32_t sv1533_maxmpxct; +}; + +struct SERVER_INFO_1534 { + uint32_t sv1534_oplockbreakwait; +}; + +struct SERVER_INFO_1535 { + uint32_t sv1535_oplockbreakresponsewait; +}; + +struct SERVER_INFO_1536 { + uint8_t sv1536_enableoplocks; +}; + +struct SERVER_INFO_1537 { + uint8_t sv1537_enableoplockforceclose; +}; + +struct SERVER_INFO_1538 { + uint8_t sv1538_enablefcbopens; +}; + +struct SERVER_INFO_1539 { + uint8_t sv1539_enableraw; +}; + +struct SERVER_INFO_1540 { + uint8_t sv1540_enablesharednetdrives; +}; + +struct SERVER_INFO_1541 { + uint8_t sv1541_minfreeconnections; +}; + +struct SERVER_INFO_1542 { + uint8_t sv1542_maxfreeconnections; +}; + +struct SERVER_INFO_1543 { + uint32_t sv1543_initsesstable; +}; + +struct SERVER_INFO_1544 { + uint32_t sv1544_initconntable; +}; + +struct SERVER_INFO_1545 { + uint32_t sv1545_initfiletable; +}; + +struct SERVER_INFO_1546 { + uint32_t sv1546_initsearchtable; +}; + +struct SERVER_INFO_1547 { + uint32_t sv1547_alertschedule; +}; + +struct SERVER_INFO_1548 { + uint32_t sv1548_errorthreshold; +}; + +struct SERVER_INFO_1549 { + uint32_t sv1549_networkerrorthreshold; +}; + +struct SERVER_INFO_1550 { + uint32_t sv1550_diskspacethreshold; +}; + +struct SERVER_INFO_1552 { + uint32_t sv1552_maxlinkdelay; +}; + +struct SERVER_INFO_1553 { + uint32_t sv1553_minlinkthroughput; +}; + +struct SERVER_INFO_1554 { + uint32_t sv1554_linkinfovalidtime; +}; + +struct SERVER_INFO_1555 { + uint32_t sv1555_scavqosinfoupdatetime; +}; + +struct SERVER_INFO_1556 { + uint32_t sv1556_maxworkitemidletime; +}; + +struct SERVER_INFO_1557 { + uint32_t sv1557_maxrawworkitems; +}; + +struct SERVER_INFO_1560 { + uint32_t sv1560_producttype; +}; + +struct SERVER_INFO_1561 { + uint32_t sv1561_serversize; +}; + +struct SERVER_INFO_1562 { + uint32_t sv1562_connectionlessautodisc; +}; + +struct SERVER_INFO_1563 { + uint32_t sv1563_sharingviolationretries; +}; + +struct SERVER_INFO_1564 { + uint32_t sv1564_sharingviolationdelay; +}; + +struct SERVER_INFO_1565 { + uint32_t sv1565_maxglobalopensearch; +}; + +struct SERVER_INFO_1566 { + uint8_t sv1566_removeduplicatesearches; +}; + +struct SERVER_INFO_1567 { + uint32_t sv1567_lockviolationretries; +}; + +struct SERVER_INFO_1568 { + uint32_t sv1568_lockviolationoffset; +}; + +struct SERVER_INFO_1569 { + uint32_t sv1569_lockviolationdelay; +}; + +struct SERVER_INFO_1570 { + uint32_t sv1570_mdlreadswitchover; +}; + +struct SERVER_INFO_1571 { + uint32_t sv1571_cachedopenlimit; +}; + +struct SERVER_INFO_1572 { + uint32_t sv1572_criticalthreads; +}; + +struct SERVER_INFO_1573 { + uint32_t sv1573_restrictnullsessaccess; +}; + +struct SERVER_INFO_1574 { + uint32_t sv1574_enablewfw311directipx; +}; + +struct SERVER_INFO_1575 { + uint32_t sv1575_otherqueueaffinity; +}; + +struct SERVER_INFO_1576 { + uint32_t sv1576_queuesamplesecs; +}; + +struct SERVER_INFO_1577 { + uint32_t sv1577_balancecount; +}; + +struct SERVER_INFO_1578 { + uint32_t sv1578_preferredaffinity; +}; + +struct SERVER_INFO_1579 { + uint32_t sv1579_maxfreerfcbs; +}; + +struct SERVER_INFO_1580 { + uint32_t sv1580_maxfreemfcbs; +}; + +struct SERVER_INFO_1581 { + uint32_t sv1581_maxfreemlcbs; +}; + +struct SERVER_INFO_1582 { + uint32_t sv1582_maxfreepagedpoolchunks; +}; + +struct SERVER_INFO_1583 { + uint32_t sv1583_minpagedpoolchunksize; +}; + +struct SERVER_INFO_1584 { + uint32_t sv1584_maxpagedpoolchunksize; +}; + +struct SERVER_INFO_1585 { + uint8_t sv1585_sendsfrompreferredprocessor; +}; + +struct SERVER_INFO_1586 { + uint32_t sv1586_maxthreadsperqueue; +}; + +struct SERVER_INFO_1587 { + uint32_t sv1587_cacheddirectorylimit; +}; + +struct SERVER_INFO_1588 { + uint32_t sv1588_maxcopylength; +}; + +struct SERVER_INFO_1590 { + uint32_t sv1590_enablecompression; +}; + +struct SERVER_INFO_1591 { + uint32_t sv1591_autosharewks; +}; + +struct SERVER_INFO_1592 { + uint32_t sv1592_autosharewks; +}; + +struct SERVER_INFO_1593 { + uint32_t sv1593_enablesecuritysignature; +}; + +struct SERVER_INFO_1594 { + uint32_t sv1594_requiresecuritysignature; +}; + +struct SERVER_INFO_1595 { + uint32_t sv1595_minclientbuffersize; +}; + +struct SERVER_INFO_1596 { + uint32_t sv1596_ConnectionNoSessionsTimeout; +}; + +struct SERVER_INFO_1597 { + uint32_t sv1597_IdleThreadTimeOut; +}; + +struct SERVER_INFO_1598 { + uint32_t sv1598_enableW9xsecuritysignature; +}; + +struct SERVER_INFO_1599 { + uint8_t sv1598_enforcekerberosreauthentication; +}; + +struct SERVER_INFO_1600 { + uint8_t sv1598_disabledos; +}; + +struct SERVER_INFO_1601 { + uint32_t sv1598_lowdiskspaceminimum; +}; + +struct SERVER_INFO_1602 { + uint8_t sv_1598_disablestrictnamechecking; +}; + +struct USER_INFO_0 { + const char * usri0_name; +}; + +#define USER_PRIV_GUEST ( 0 ) +#define USER_PRIV_USER ( 1 ) +#define USER_PRIV_ADMIN ( 2 ) + +struct USER_INFO_1 { + const char * usri1_name; + const char * usri1_password; + uint32_t usri1_password_age; + uint32_t usri1_priv; + const char * usri1_home_dir; + const char * usri1_comment; + uint32_t usri1_flags; + const char * usri1_script_path; +}; + +#define AF_OP_PRINT ( 0x1 ) +#define AF_OP_COMM ( 0x2 ) +#define AF_OP_SERVER ( 0x4 ) +#define AF_OP_ACCOUNTS ( 0x8 ) + +struct USER_INFO_2 { + const char * usri2_name; + const char * usri2_password; + uint32_t usri2_password_age; + uint32_t usri2_priv; + const char * usri2_home_dir; + const char * usri2_comment; + uint32_t usri2_flags; + const char * usri2_script_path; + uint32_t usri2_auth_flags; + const char * usri2_full_name; + const char * usri2_usr_comment; + const char * usri2_parms; + const char * usri2_workstations; + uint32_t usri2_last_logon; + uint32_t usri2_last_logoff; + uint32_t usri2_acct_expires; + uint32_t usri2_max_storage; + uint32_t usri2_units_per_week; + uint8_t *usri2_logon_hours;/* [unique] */ + uint32_t usri2_bad_pw_count; + uint32_t usri2_num_logons; + const char * usri2_logon_server; + uint32_t usri2_country_code; + uint32_t usri2_code_page; +}; + +struct USER_INFO_3 { + const char * usri3_name; + uint32_t usri3_password_age; + uint32_t usri3_priv; + const char * usri3_home_dir; + const char * usri3_comment; + uint32_t usri3_flags; + const char * usri3_script_path; + uint32_t usri3_auth_flags; + const char * usri3_full_name; + const char * usri3_usr_comment; + const char * usri3_parms; + const char * usri3_workstations; + uint32_t usri3_last_logon; + uint32_t usri3_last_logoff; + uint32_t usri3_acct_expires; + uint32_t usri3_max_storage; + uint32_t usri3_units_per_week; + uint8_t *usri3_logon_hours;/* [unique] */ + uint32_t usri3_bad_pw_count; + uint32_t usri3_num_logons; + const char * usri3_logon_server; + uint32_t usri3_country_code; + uint32_t usri3_code_page; + uint32_t usri3_user_id; + uint32_t usri3_primary_group_id; + const char * usri3_profile; + const char * usri3_home_dir_drive; + uint32_t usri3_password_expired; +}; + +struct USER_INFO_4 { + const char * usri4_name; + const char * usri4_password; + uint32_t usri4_password_age; + uint32_t usri4_priv; + const char * usri4_home_dir; + const char * usri4_comment; + uint32_t usri4_flags; + const char * usri4_script_path; + uint32_t usri4_auth_flags; + const char * usri4_full_name; + const char * usri4_usr_comment; + const char * usri4_parms; + const char * usri4_workstations; + uint32_t usri4_last_logon; + uint32_t usri4_last_logoff; + uint32_t usri4_acct_expires; + uint32_t usri4_max_storage; + uint32_t usri4_units_per_week; + uint8_t *usri4_logon_hours;/* [unique] */ + uint32_t usri4_bad_pw_count; + uint32_t usri4_num_logons; + const char * usri4_logon_server; + uint32_t usri4_country_code; + uint32_t usri4_code_page; + struct domsid *usri4_user_sid;/* [unique] */ + uint32_t usri4_primary_group_id; + const char * usri4_profile; + const char * usri4_home_dir_drive; + uint32_t usri4_password_expired; +}; + +struct USER_INFO_10 { + const char * usri10_name; + const char * usri10_comment; + const char * usri10_usr_comment; + const char * usri10_full_name; +}; + +struct USER_INFO_11 { + const char * usri11_name; + const char * usri11_comment; + const char * usri11_usr_comment; + const char * usri11_full_name; + uint32_t usri11_priv; + uint32_t usri11_auth_flags; + uint32_t usri11_password_age; + const char * usri11_home_dir; + const char * usri11_parms; + uint32_t usri11_last_logon; + uint32_t usri11_last_logoff; + uint32_t usri11_bad_pw_count; + uint32_t usri11_num_logons; + const char * usri11_logon_server; + uint32_t usri11_country_code; + const char * usri11_workstations; + uint32_t usri11_max_storage; + uint32_t usri11_units_per_week; + uint8_t *usri11_logon_hours;/* [unique] */ + uint32_t usri11_code_page; +}; + +struct USER_INFO_20 { + const char * usri20_name; + const char * usri20_full_name; + const char * usri20_comment; + uint32_t usri20_flags; + uint32_t usri20_user_id; +}; + +struct USER_INFO_21 { + uint8_t *usri21_password; +}; + +struct USER_INFO_22 { + const char * usri22_name; + uint8_t *usri22_password; + uint32_t usri22_password_age; + uint32_t usri22_priv; + const char * usri22_home_dir; + const char * usri22_comment; + uint32_t usri22_flags; + uint32_t usri22_script_path; + uint32_t usri22_auth_flags; + const char * usri22_full_name; + const char * usri22_usr_comment; + const char * usri22_parms; + const char * usri22_workstations; + uint32_t usri22_last_logon; + uint32_t usri22_last_logoff; + uint32_t usri22_acct_expires; + uint32_t usri22_max_storage; + uint32_t usri22_units_per_week; + uint8_t *usri22_logon_hours;/* [unique] */ + uint32_t usri22_bad_pw_count; + uint32_t usri22_num_logons; + const char * usri22_logon_server; + uint32_t usri22_country_code; + uint32_t usri22_code_page; +}; + +struct USER_INFO_23 { + const char * usri23_name; + const char * usri23_full_name; + const char * usri23_comment; + uint32_t usri23_flags; + struct domsid *usri23_user_sid;/* [unique] */ +}; + +struct USER_INFO_1003 { + const char * usri1003_password; +}; + +struct USER_INFO_1005 { + uint32_t usri1005_priv; +}; + +struct USER_INFO_1006 { + const char * usri1006_home_dir; +}; + +struct USER_INFO_1007 { + const char * usri1007_comment; +}; + +struct USER_INFO_1008 { + uint32_t usri1008_flags; +}; + +struct USER_INFO_1009 { + const char * usri1009_script_path; +}; + +struct USER_INFO_1010 { + uint32_t usri1010_auth_flags; +}; + +struct USER_INFO_1011 { + const char * usri1011_full_name; +}; + +struct USER_INFO_1012 { + const char * usri1012_usr_comment; +}; + +struct USER_INFO_1013 { + const char * usri1013_parms; +}; + +struct USER_INFO_1014 { + const char * usri1014_workstations; +}; + +struct USER_INFO_1017 { + uint32_t usri1017_acct_expires; +}; + +struct USER_INFO_1018 { + uint32_t usri1018_max_storage; +}; + +struct USER_INFO_1020 { + uint32_t usri1020_units_per_week; + uint8_t *usri1020_logon_hours;/* [unique] */ +}; + +struct USER_INFO_1023 { + const char * usri1023_logon_server; +}; + +struct USER_INFO_1024 { + uint32_t usri1024_country_code; +}; + +struct USER_INFO_1025 { + uint32_t usri1025_code_page; +}; + +struct USER_INFO_1051 { + uint32_t usri1051_primary_group_id; +}; + +struct USER_INFO_1052 { + const char * usri1052_profile; +}; + +struct USER_INFO_1053 { + const char * usri1053_home_dir_drive; +}; + +struct USER_MODALS_INFO_0 { + uint32_t usrmod0_min_passwd_len; + uint32_t usrmod0_max_passwd_age; + uint32_t usrmod0_min_passwd_age; + uint32_t usrmod0_force_logoff; + uint32_t usrmod0_password_hist_len; +}; + +struct USER_MODALS_INFO_1 { + uint32_t usrmod1_role; + const char * usrmod1_primary; +}; + +struct USER_MODALS_INFO_2 { + const char * usrmod2_domain_name; + struct domsid *usrmod2_domain_id;/* [unique] */ +}; + +struct USER_MODALS_INFO_3 { + uint32_t usrmod3_lockout_duration; + uint32_t usrmod3_lockout_observation_window; + uint32_t usrmod3_lockout_threshold; +}; + +struct USER_MODALS_INFO_1001 { + uint32_t usrmod1001_min_passwd_len; +}; + +struct USER_MODALS_INFO_1002 { + uint32_t usrmod1002_max_passwd_age; +}; + +struct USER_MODALS_INFO_1003 { + uint32_t usrmod1003_min_passwd_age; +}; + +struct USER_MODALS_INFO_1004 { + uint32_t usrmod1004_force_logoff; +}; + +struct USER_MODALS_INFO_1005 { + uint32_t usrmod1005_password_hist_len; +}; + +struct USER_MODALS_INFO_1006 { + uint32_t usrmod1006_role; +}; + +struct USER_MODALS_INFO_1007 { + const char * usrmod1007_primary; +}; + +struct NET_DISPLAY_USER { + const char * usri1_name; + const char * usri1_comment; + uint32_t usri1_flags; + const char * usri1_full_name; + uint32_t usri1_user_id; + uint32_t usri1_next_index; +}; + +struct NET_DISPLAY_MACHINE { + const char * usri2_name; + const char * usri2_comment; + uint32_t usri2_flags; + uint32_t usri2_user_id; + uint32_t usri2_next_index; +}; + +struct NET_DISPLAY_GROUP { + const char * grpi3_name; + const char * grpi3_comment; + uint32_t grpi3_group_id; + uint32_t grpi3_attributes; + uint32_t grpi3_next_index; +}; + +struct GROUP_INFO_0 { + const char * grpi0_name; +}; + +struct GROUP_INFO_1 { + const char * grpi1_name; + const char * grpi1_comment; +}; + +struct GROUP_INFO_2 { + const char * grpi2_name; + const char * grpi2_comment; + uint32_t grpi2_group_id; + uint32_t grpi2_attributes; +}; + +struct GROUP_INFO_3 { + const char * grpi3_name; + const char * grpi3_comment; + struct domsid * grpi3_group_sid; + uint32_t grpi3_attributes; +}; + +struct GROUP_INFO_1002 { + const char * grpi1002_comment; +}; + +struct GROUP_INFO_1005 { + uint32_t grpi1005_attributes; +}; + +struct GROUP_USERS_INFO_0 { + const char * grui0_name; +}; + +struct GROUP_USERS_INFO_1 { + const char * grui1_name; + uint32_t grui1_attributes; +}; + +struct LOCALGROUP_INFO_0 { + const char * lgrpi0_name; +}; + +struct LOCALGROUP_INFO_1 { + const char * lgrpi1_name; + const char * lgrpi1_comment; +}; + +struct LOCALGROUP_INFO_1002 { + const char * lgrpi1002_comment; +}; + +enum SID_NAME_USE { + SidTypeUser=1, + SidTypeGroup=2, + SidTypeDomain=3, + SidTypeAlias=4, + SidTypeWellKnownGroup=5, + SidTypeDeletedAccount=6, + SidTypeInvalid=7, + SidTypeUnknown=8, + SidTypeComputer=9, + SidTypeLabel=10 +}; + +struct LOCALGROUP_MEMBERS_INFO_0 { + struct domsid *lgrmi0_sid;/* [unique] */ +}; + +struct LOCALGROUP_MEMBERS_INFO_1 { + struct domsid *lgrmi1_sid;/* [unique] */ + enum SID_NAME_USE lgrmi1_sidusage; + const char * lgrmi1_name; +}; + +struct LOCALGROUP_MEMBERS_INFO_2 { + struct domsid *lgrmi2_sid;/* [unique] */ + enum SID_NAME_USE lgrmi2_sidusage; + const char * lgrmi2_domainandname; +}; + +struct LOCALGROUP_MEMBERS_INFO_3 { + const char * lgrmi3_domainandname; +}; + +struct LOCALGROUP_USERS_INFO_0 { + const char * lgrui0_name; +}; + +struct TIME_OF_DAY_INFO { + uint32_t tod_elapsedt; + uint32_t tod_msecs; + uint32_t tod_hours; + uint32_t tod_mins; + uint32_t tod_secs; + uint32_t tod_hunds; + int32_t tod_timezone; + uint32_t tod_tinterval; + uint32_t tod_day; + uint32_t tod_month; + uint32_t tod_year; + uint32_t tod_weekday; +}; + +struct SHARE_INFO_0 { + const char * shi0_netname; +}; + +struct SHARE_INFO_1 { + const char * shi1_netname; + uint32_t shi1_type; + const char * shi1_remark; +}; + +struct SHARE_INFO_2 { + const char * shi2_netname; + uint32_t shi2_type; + const char * shi2_remark; + uint32_t shi2_permissions; + uint32_t shi2_max_uses; + uint32_t shi2_current_uses; + const char * shi2_path; + const char * shi2_passwd; +}; + +struct SHARE_INFO_501 { + const char * shi501_netname; + uint32_t shi501_type; + const char * shi501_remark; + uint32_t shi501_flags; +}; + +struct SHARE_INFO_502 { + const char * shi502_netname; + uint32_t shi502_type; + const char * shi502_remark; + uint32_t shi502_permissions; + uint32_t shi502_max_uses; + uint32_t shi502_current_uses; + const char * shi502_path; + const char * shi502_passwd; + uint32_t shi502_reserved; + struct security_descriptor * shi502_security_descriptor; +}; + +struct SHARE_INFO_1004 { + const char * shi1004_remark; +}; + +struct SHARE_INFO_1005 { + uint32_t shi1005_flags; +}; + +struct SHARE_INFO_1006 { + uint32_t shi1006_max_uses; +}; + +struct FILE_INFO_2 { + uint32_t fi2_id; +}; + +struct FILE_INFO_3 { + uint32_t fi3_id; + uint32_t fi3_permissions; + uint32_t fi3_num_locks; + const char * fi3_pathname; + const char * fi3_username; +}; + +struct NETLOGON_INFO_1 { + uint32_t netlog1_flags; + NET_API_STATUS netlog1_pdc_connection_status; +}; + +struct NETLOGON_INFO_2 { + uint32_t netlog2_flags; + NET_API_STATUS netlog2_pdc_connection_status; + const char * netlog2_trusted_dc_name; + NET_API_STATUS netlog2_tc_connection_status; +}; + +struct NETLOGON_INFO_3 { + uint32_t netlog1_flags; + uint32_t netlog3_logon_attempts; + uint32_t netlog3_reserved1; + uint32_t netlog3_reserved2; + uint32_t netlog3_reserved3; + uint32_t netlog3_reserved4; + uint32_t netlog3_reserved5; +}; + +struct NETLOGON_INFO_4 { + const char * netlog4_trusted_dc_name; + const char * netlog4_trusted_domain_name; +}; + +#define DS_PDC_FLAG ( 0x00000001 ) +#define DS_GC_FLAG ( 0x00000004 ) +#define DS_LDAP_FLAG ( 0x00000008 ) +#define DS_DS_FLAG ( 0x00000010 ) +#define DS_KDC_FLAG ( 0x00000020 ) +#define DS_TIMESERV_FLAG ( 0x00000040 ) +#define DS_CLOSEST_FLAG ( 0x00000080 ) +#define DS_WRITABLE_FLAG ( 0x00000100 ) +#define DS_GOOD_TIMESERV_FLAG ( 0x00000200 ) +#define DS_NDNC_FLAG ( 0x00000400 ) +#define DS_SELECT_SECRET_DOMAIN_6_FLAG ( 0x00000800 ) +#define DS_FULL_SECRET_DOMAIN_6_FLAG ( 0x00001000 ) +#define DS_WS_FLAG ( 0x00002000 ) +#define DS_DS_8_FLAG ( 0x00004000 ) +#define DS_DNS_CONTROLLER_FLAG ( 0x20000000 ) +#define DS_DNS_DOMAIN_FLAG ( 0x40000000 ) +#define DS_DNS_FOREST_FLAG ( 0x80000000 ) + +#endif /* _HEADER_libnetapi */ + +#ifndef _HEADER_netlogon + +#define NETLOGON_CONTROL_QUERY ( 0x00000001 ) +#define NETLOGON_CONTROL_REPLICATE ( 0x00000002 ) +#define NETLOGON_CONTROL_SYNCHRONIZE ( 0x00000003 ) +#define NETLOGON_CONTROL_PDC_REPLICATE ( 0x00000004 ) +#define NETLOGON_CONTROL_REDISCOVER ( 0x00000005 ) +#define NETLOGON_CONTROL_TC_QUERY ( 0x00000006 ) +#define NETLOGON_CONTROL_TRANSPORT_NOTIFY ( 0x00000007 ) +#define NETLOGON_CONTROL_FIND_USER ( 0x00000008 ) +#define NETLOGON_CONTROL_CHANGE_PASSWORD ( 0x00000009 ) +#define NETLOGON_CONTROL_TC_VERIFY ( 0x0000000A ) +#define NETLOGON_CONTROL_FORCE_DNS_REG ( 0x0000000B ) +#define NETLOGON_CONTROL_QUERY_DNS_REG ( 0x0000000C ) +#define NETLOGON_CONTROL_BACKUP_CHANGE_LOG ( 0x0000FFFC ) +#define NETLOGON_CONTROL_TRUNCATE_LOG ( 0x0000FFFD ) +#define NETLOGON_CONTROL_SET_DBFLAG ( 0x0000FFFE ) +#define NETLOGON_CONTROL_BREAKPOINT ( 0x0000FFFF ) + +#define DS_FORCE_REDISCOVERY ( 0x00000001 ) +#define DS_DIRECTORY_SERVICE_REQUIRED ( 0x00000010 ) +#define DS_DIRECTORY_SERVICE_PREFERRED ( 0x00000020 ) +#define DS_GC_SERVER_REQUIRED ( 0x00000040 ) +#define DS_PDC_REQUIRED ( 0x00000080 ) +#define DS_BACKGROUND_ONLY ( 0x00000100 ) +#define DS_IP_REQUIRED ( 0x00000200 ) +#define DS_KDC_REQUIRED ( 0x00000400 ) +#define DS_TIMESERV_REQUIRED ( 0x00000800 ) +#define DS_WRITABLE_REQUIRED ( 0x00001000 ) +#define DS_GOOD_TIMESERV_PREFERRED ( 0x00002000 ) +#define DS_AVOID_SELF ( 0x00004000 ) +#define DS_ONLY_LDAP_NEEDED ( 0x00008000 ) +#define DS_IS_FLAT_NAME ( 0x00010000 ) +#define DS_IS_DNS_NAME ( 0x00020000 ) +#define DS_TRY_NEXTCLOSEST_SITE ( 0x00040000 ) +#define DS_DIRECTORY_SERVICE_6_REQUIRED ( 0x00080000 ) +#define DS_WEB_SERVICE_REQUIRED ( 0x00100000 ) +#define DS_RETURN_DNS_NAME ( 0x40000000 ) +#define DS_RETURN_FLAT_NAME ( 0x80000000 ) + +#endif /* _HEADER_netlogon */ + +/**************************************************************** +****************************************************************/ + +struct libnetapi_ctx; + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_init(struct libnetapi_ctx **ctx); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_free(struct libnetapi_ctx *ctx); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_getctx(struct libnetapi_ctx **ctx); + +NET_API_STATUS libnetapi_get_username(struct libnetapi_ctx *ctx, + const char **username); + +NET_API_STATUS libnetapi_get_password(struct libnetapi_ctx *ctx, + const char **password); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_debuglevel(struct libnetapi_ctx *ctx, + const char *debuglevel); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_creds(struct libnetapi_ctx *ctx, + struct cli_credentials *creds); + + +NET_API_STATUS libnetapi_set_username(struct libnetapi_ctx *ctx, + const char *username); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_password(struct libnetapi_ctx *ctx, + const char *password); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_workgroup(struct libnetapi_ctx *ctx, + const char *workgroup); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_use_kerberos(struct libnetapi_ctx *ctx); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_get_use_kerberos(struct libnetapi_ctx *ctx, + int *use_kerberos); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_logfile(struct libnetapi_ctx *ctx, + const char *logfile); + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_use_ccache(struct libnetapi_ctx *ctx); + +/**************************************************************** +Return a specific libnetapi error as a string, caller must free with NetApiBufferFree +****************************************************************/ + +char *libnetapi_errstr(NET_API_STATUS status); + +/**************************************************************** +Return the last libnetapi error as a string, caller must free with NetApiBufferFree +ctx is optional +****************************************************************/ + +char *libnetapi_get_error_string(struct libnetapi_ctx *ctx, + NET_API_STATUS status); + +/**************************************************************** + NetApiBufferAllocate +****************************************************************/ + +NET_API_STATUS NetApiBufferAllocate(uint32_t byte_count, + void **buffer); + +/**************************************************************** + NetApiBufferFree +****************************************************************/ + +NET_API_STATUS NetApiBufferFree(void *buffer); + +/************************************************************//** + * + * ConvertSidToStringSid + * + * @brief Convert a domain sid into a string + * + * @param[in] sid A pointer to a sid structure + * @param[in,out] sid_string A pointer that holds a pointer to a sid string. Caller + * needs to free with free(3) + * @return bool + ***************************************************************/ + +int ConvertSidToStringSid(const struct domsid *sid, + char **sid_string); + +/************************************************************//** + * + * ConvertStringSidToSid + * + * @brief Convert a string into a domain sid + * + * @param[in] sid_string A pointer to a sid string. + * @param[in,out] sid A pointer that holds a pointer to a sid structure. + * Caller needs to free with free(3) + * @return bool + ***************************************************************/ + +int ConvertStringSidToSid(const char *sid_string, + struct domsid **sid); + +/************************************************************//** + * + * NetJoinDomain + * + * @brief Join a computer to a domain or workgroup + * + * @param[in] server The server name to connect to + * @param[in] domain The domain or workgroup to join + * @param[in] account_ou The organizational Unit to create the computer account + * in (AD only) + * @param[in] account The domain account used for joining a domain + * @param[in] password The domain account's password used for joining a domain + * @param[in] join_flags Bitmask field to define specific join features + * @return NET_API_STATUS + * + * example netdomjoin/netdomjoin.c + ***************************************************************/ + +NET_API_STATUS NetJoinDomain(const char * server /* [in] */, + const char * domain /* [in] [ref] */, + const char * account_ou /* [in] */, + const char * account /* [in] */, + const char * password /* [in] */, + uint32_t join_flags /* [in] */); + +/************************************************************//** + * + * NetUnjoinDomain + * + * @brief Unjoin a computer from a domain or workgroup + * + * @param[in] server_name The server name to connect to + * @param[in] account The domain account used for unjoining a domain + * @param[in] password The domain account's password used for unjoining a domain + * @param[in] unjoin_flags Bitmask field to define specific unjoin features + * @return NET_API_STATUS + * + ***************************************************************/ + +NET_API_STATUS NetUnjoinDomain(const char * server_name /* [in] */, + const char * account /* [in] */, + const char * password /* [in] */, + uint32_t unjoin_flags /* [in] */); + +/************************************************************//** + * + * NetGetJoinInformation + * + * @brief Unjoin a computer from a domain or workgroup + * + * @param[in] server_name The server name to connect to + * @param[out] name_buffer Returns the name of the workgroup or domain + * @param[out] name_type Returns the type of that name + * @return NET_API_STATUS + * + * example netdomjoin-gui/netdomjoin-gui.c + * + ***************************************************************/ + +NET_API_STATUS NetGetJoinInformation(const char * server_name /* [in] */, + const char * *name_buffer /* [out] [ref] */, + uint16_t *name_type /* [out] [ref] */); + +/************************************************************//** + * + * NetGetJoinableOUs + * + * @brief Query for the list of joinable organizational Units that can be used + * for joining AD + * + * @param[in] server_name The server name to connect to + * @param[in] domain The AD domain to query + * @param[in] account The domain account used for the query + * @param[in] password The domain account's password used for the query + * @param[out] ou_count The number of ous returned + * @param[out] ous Returned string array containing the ous + * @return NET_API_STATUS + * + * example netdomjoin-gui/netdomjoin-gui.c + * + ***************************************************************/ + +NET_API_STATUS NetGetJoinableOUs(const char * server_name /* [in] */, + const char * domain /* [in] [ref] */, + const char * account /* [in] */, + const char * password /* [in] */, + uint32_t *ou_count /* [out] [ref] */, + const char * **ous /* [out] [ref] */); + +/************************************************************//** + * + * NetRenameMachineInDomain + * + * @brief Rename a machine in a domain + * + * @param[in] server_name The server name to connect to + * @param[in] new_machine_name The new machine name + * @param[in] account The domain account used for the query + * @param[in] password The domain account's password used for the query + * @param[in] rename_options Options used for the rename operation + * @return NET_API_STATUS + * + * example join/rename_machine.c + * + ***************************************************************/ + +NET_API_STATUS NetRenameMachineInDomain(const char * server_name /* [in] */, + const char * new_machine_name /* [in] */, + const char * account /* [in] */, + const char * password /* [in] */, + uint32_t rename_options /* [in] */); + +/************************************************************//** + * + * NetProvisionComputerAccount + * + * @brief Provision a machine for offline join + * + * @param[in] domain The domain to provision for + * @param[in] machine_name The machine account name + * @param[in] machine_account_ou The machine account ou to create the account in + * @param[in] dcname A specific domain controller to use for account creation + * @param[in] options The options used for account creation + * @param[in,out] provision_bin_data The generated binary buffer + * @param[in,out] provision_bin_data_size The generated binary buffer size + * @param[in,out] provision_text_data The generated text data blob + * @return NET_API_STATUS + * + * example join/provision_computer_account.c + * + ***************************************************************/ + +NET_API_STATUS NetProvisionComputerAccount(const char * domain /* [in] [ref] */, + const char * machine_name /* [in] [ref] */, + const char * machine_account_ou /* [in] [unique] */, + const char * dcname /* [in] [unique] */, + uint32_t options /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */); + +/************************************************************//** + * + * NetRequestOfflineDomainJoin + * + * @brief Request an offline domain join + * + * @param[in] provision_bin_data The provided binary buffer + * @param[in] provision_bin_data_size The provided binary buffer size + * @param[in] options The options used for account creation + * @param[in] windows_path The path for the joined image + * @return NET_API_STATUS + * + * example join/request_offline_domain_join.c + * + ***************************************************************/ + +NET_API_STATUS NetRequestOfflineDomainJoin(uint8_t *provision_bin_data /* [in] [unique] */, + uint32_t provision_bin_data_size /* [in] */, + uint32_t options /* [in] */, + const char * windows_path /* [in] [unique] */); + +/************************************************************//** + * + * NetComposeOfflineDomainJoin + * + * @brief Compose an offline domain join blob + * + * Intended to be used by external applications who provision the computer + * acconut on their own. + * + * + * @param[in] dns_domain_name The domain DNS name + * @param[in] netbios_domain_name The domain NETBIOS name + * @param[in] domain_sid The domain SID + * @param[in] domain_guid The domain GUID + * @param[in] forest_name The forest name + * @param[in] machine_account_name The machine account name + * @param[in] machine_account_password The machine account password + * @param[in] dc_name The domain controller name used to provision the account + * @param[in] dc_address The domain controller address used to provision the account + * @param[in] domain_is_ad True if the domain is AD + * @param[in,out] compose_bin_data The generated binary buffer + * @param[in,out] compose_bin_data_size The generated binary buffer size + * @param[in,out] compose_text_data The generated text data blob + * @return NET_API_STATUS + * + * example join/compose_offline_domain_join.c + * + ***************************************************************/ + +NET_API_STATUS NetComposeOfflineDomainJoin(const char *dns_domain_name /* [in] [ref] */, + const char *netbios_domain_name /* [in] [ref] */, + struct domsid *domain_sid /* [in] [ref] */, + struct GUID *domain_guid /* [in] [ref] */, + const char *forest_name /* [in] [ref] */, + const char *machine_account_name /* [in] [ref] */, + const char *machine_account_password /* [in] [ref] */, + const char *dc_name /* [in] [unique] */, + const char *dc_address /* [in] [unique] */, + int domain_is_ad /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */); + +/************************************************************//** + * + * NetServerGetInfo + * + * @brief Get Information on a server + * + * @param[in] server_name The server name to connect to + * @param[in] level The level to define which information is requested + * @param[out] buffer The returned buffer carrying the SERVER_INFO structure + * @return NET_API_STATUS + * + ***************************************************************/ + +NET_API_STATUS NetServerGetInfo(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetServerSetInfo + * + * @brief Get Information on a server + * + * @param[in] server_name The server name to connect to + * @param[in] level The level to define which information is set + * @param[in] buffer The buffer carrying the SERVER_INFO structure + * @param[out] parm_error On failure returns the invalid SERVER_INFO member + * @return NET_API_STATUS + * + ***************************************************************/ + +NET_API_STATUS NetServerSetInfo(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_error /* [out] [ref] */); + +/************************************************************//** + * + * NetWkstaGetInfo + * + * @brief Get Information on a workstation + * + * @param[in] wksta_name The workstation name to connect to + * @param[in] level The level to define which information is requested + * @param[out] buffer The returned buffer carrying the WKSTA_INFO structure + * @return NET_API_STATUS + * + ***************************************************************/ + +NET_API_STATUS NetWkstaGetInfo(const char * wksta_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetGetDCName + * + * @brief Query for the PDC for a given domain + * + * @param[in] server_name The server name to connect to + * @param[in] domain_name The name of the domain to lookup + * @param[out] buffer The name of the domain to lookup + * @return NET_API_STATUS + * + * example getdc/getdc.c + ***************************************************************/ + +NET_API_STATUS NetGetDCName(const char * server_name /* [in] */, + const char * domain_name /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetGetAnyDCName + * + * @brief Query for any DC for a given domain + * + * @param[in] server_name The server name to connect to + * @param[in] domain_name The name of the domain to lookup + * @param[out] buffer The name of the domain to lookup + * @return NET_API_STATUS + * + * example getdc/getdc.c + ***************************************************************/ + +NET_API_STATUS NetGetAnyDCName(const char * server_name /* [in] */, + const char * domain_name /* [in] */, + uint8_t **buffer /* [out] [ref] */); + + +/************************************************************//** + * + * DsGetDcName + * + * @brief Lookup a DC for a given domain and return information structure + * + * @param[in] server_name The server name to connect to + * @param[in] domain_name The name of the domain to lookup (cannot be NULL) + * @param[in] domain_guid The GUID of the domain to lookup (optional) + * @param[in] site_name The name of the site the DC should reside in + * @param[in] flags A bitmask to request specific features supported by the DC + * @param[out] dc_info Pointer to a DOMAIN_CONTROLLER_INFO structure + * @return NET_API_STATUS + * + * example dsgetdc/dsgetdc.c + ***************************************************************/ + +NET_API_STATUS DsGetDcName(const char * server_name /* [in] [unique] */, + const char * domain_name /* [in] [ref] */, + struct GUID *domain_guid /* [in] [unique] */, + const char * site_name /* [in] [unique] */, + uint32_t flags /* [in] */, + struct DOMAIN_CONTROLLER_INFO **dc_info /* [out] [ref] */); + +/************************************************************//** + * + * NetUserAdd + * + * @brief Create a user on a given server + * + * @param[in] server_name The server name to connect to + * @param[in] level The level of the USER_INFO structure passed in (Currently + * only level 1 is supported) + * @param[in] buffer The buffer carrying the USER_INFO structure + * @param[out] parm_error In case of error returns the failing member of the + * structure + * @return NET_API_STATUS + * + * example user/user_add.c + ***************************************************************/ + +NET_API_STATUS NetUserAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_error /* [out] [ref] */); + +/************************************************************//** + * + * NetUserDel + * + * @brief Delete a user on a given server + * + * @param[in] server_name The server name to connect to + * @param[in] user_name The user account to delete + * @return NET_API_STATUS + * + * example user/user_del.c + ***************************************************************/ + +NET_API_STATUS NetUserDel(const char * server_name /* [in] */, + const char * user_name /* [in] */); + +/************************************************************//** + * + * NetUserEnum + * + * @brief Enumerate accounts on a server + * + * @param[in] server_name The server name to connect to + * @param[in] level The enumeration level used for the query (Currently only + * level 0 is supported) + * @param[in] filter The account flags filter used for the query + * @param[out] buffer The returned enumeration buffer + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] total_entries The number of total entries + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example user/user_enum.c + ***************************************************************/ + +NET_API_STATUS NetUserEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint32_t filter /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetUserChangePassword + * + * @brief Change the password for a user on a given server or in a given domain + * + * @param[in] domain_name The server or domain name to connect to + * @param[in] user_name The user account to change the password for + * @param[in] old_password The user account's old password + * @param[in] new_password The user account's new password + * @return NET_API_STATUS + * + * example user/user_chgpwd.c + ***************************************************************/ + +NET_API_STATUS NetUserChangePassword(const char * domain_name /* [in] */, + const char * user_name /* [in] */, + const char * old_password /* [in] */, + const char * new_password /* [in] */); + +/************************************************************//** + * + * NetUserGetInfo + * + * @brief Get User Information + * + * @param[in] server_name The server name to connect to + * @param[in] user_name The name of the user that is going to be queried + * @param[in] level The level defining the requested USER_INFO_X structure + * @param[out] buffer The buffer containing a USER_INFO_X structure + * @return NET_API_STATUS + * + * example user/user_getinfo.c + ***************************************************************/ + +NET_API_STATUS NetUserGetInfo(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetUserSetInfo + * + * @brief Set User Information + * + * @param[in] server_name The server name to connect to + * @param[in] user_name The name of the user that is going to be modified + * @param[in] level The level defining the requested USER_INFO_X structure + * @param[in] buffer The buffer containing a USER_INFO_X structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example user/user_setinfo.c + ***************************************************************/ + +NET_API_STATUS NetUserSetInfo(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetUserModalsGet + * + * @brief Get SAM domain and password information + * + * @param[in] server_name The server name to connect to + * @param[in] level The level defining which USER_MODALS_INFO_X buffer to query + * @param[out] buffer The returned USER_MODALS_INFO_X buffer + * @return NET_API_STATUS + * + * example user/user_modalsget.c + ***************************************************************/ + +NET_API_STATUS NetUserModalsGet(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetUserModalsSet + * + * @brief Set SAM domain and password information + * + * @param[in] server_name The server name to connect to + * @param[in] level The level defining which USER_MODALS_INFO_X buffer to query + * @param[out] buffer The buffer conntaing a USER_MODALS_INFO_X structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example user/user_modalsset.c + ***************************************************************/ + +NET_API_STATUS NetUserModalsSet(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetUserGetGroups + * + * @brief Enumerate grouplist of a user on a server + * + * @param[in] server_name The server name to connect to + * @param[in] user_name The user name to query + * @param[in] level The enumeration level used for the query (Currently only + * level 0 is supported) + * @param[out] buffer The returned enumeration buffer + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] total_entries The number of total entries + * @return NET_API_STATUS + * + * example user/user_getgroups.c + ***************************************************************/ + +NET_API_STATUS NetUserGetGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */); + +/************************************************************//** + * + * NetUserSetGroups + * + * @brief Set grouplist of a user on a server + * + * @param[in] server_name The server name to connect to + * @param[in] user_name The user name to query + * @param[in] level The level defining the GROUP_USERS_INFO_X structures in the buffer + * @param[in] buffer The buffer containing GROUP_USERS_INFO_X structures + * @param[in] num_entries The number of X structures in the buffer + * @return NET_API_STATUS + * + * example user/user_setgroups.c + ***************************************************************/ + +NET_API_STATUS NetUserSetGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t num_entries /* [in] */); + +/************************************************************//** + * + * NetUserGetLocalGroups + * + * @brief Enumerate local grouplist of a user on a server + * + * @param[in] server_name The server name to connect to + * @param[in] user_name The user name to query + * @param[in] level The enumeration level used for the query + * @param[in] flags The flags used for the query + * @param[out] buffer The returned enumeration buffer + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] total_entries The number of total entries + * @return NET_API_STATUS + * + * example user/user_getlocalgroups.c + ***************************************************************/ + +NET_API_STATUS NetUserGetLocalGroups(const char * server_name /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint32_t flags /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */); + +/************************************************************//** + * + * NetQueryDisplayInformation + * + * @brief Enumerate accounts on a server + * + * @param[in] server_name The server name to connect to + * @param[in] level The enumeration level used for the query + * @param[in] idx The index to start the the display enumeration at + * @param[in] entries_requested The number of entries requested + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] buffer The returned display information buffer + * @return NET_API_STATUS + * + * example user/user_dispinfo.c + ***************************************************************/ + +NET_API_STATUS NetQueryDisplayInformation(const char * server_name /* [in] [unique] */, + uint32_t level /* [in] */, + uint32_t idx /* [in] */, + uint32_t entries_requested /* [in] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + void **buffer /* [out] [noprint,ref] */); + +/************************************************************//** + * + * NetGroupAdd + * + * @brief Create Domain Group + * + * @param[in] server_name The server name to connect to + * @param[in] level The level used for the new group creation + * @param[in] buf The buffer containing the group structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example group/group_add.c + ***************************************************************/ + +NET_API_STATUS NetGroupAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buf /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetGroupDel + * + * @brief Delete Domain Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be deleted + * @return NET_API_STATUS + * + * example group/group_del.c + ***************************************************************/ + +NET_API_STATUS NetGroupDel(const char * server_name /* [in] */, + const char * group_name /* [in] */); + +/************************************************************//** + * + * NetGroupEnum + * + * @brief Enumerate groups on a server + * + * @param[in] server_name The server name to connect to + * @param[in] level The enumeration level used for the query (Currently only + * level 0 is supported) + * @param[out] buffer The returned enumeration buffer + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] total_entries The number of total entries + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example group/group_enum.c + ***************************************************************/ + +NET_API_STATUS NetGroupEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetGroupSetInfo + * + * @brief Set Domain Group Information + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be modified + * @param[in] level The level defining the structure type in buf + * @param[in] buf The buffer containing a GROUP_INFO_X structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example group/group_setinfo.c + ***************************************************************/ + +NET_API_STATUS NetGroupSetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buf /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetGroupGetInfo + * + * @brief Get Domain Group Information + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be queried + * @param[in] level The level defining the requested GROUP_INFO_X structure + * @param[out] buf The buffer containing a GROUP_INFO_X structure + * @return NET_API_STATUS + * + * example group/group_getinfo.c + ***************************************************************/ + +NET_API_STATUS NetGroupGetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buf /* [out] [ref] */); + +/************************************************************//** + * + * NetGroupAddUser + * + * @brief Add existing User to existing Domain Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be modified + * @param[in] user_name The name of the user that is going to be added to the + * group + * @return NET_API_STATUS + * + * example group/group_adduser.c + ***************************************************************/ + +NET_API_STATUS NetGroupAddUser(const char * server_name /* [in] */, + const char * group_name /* [in] */, + const char * user_name /* [in] */); + +/************************************************************//** + * + * NetGroupDelUser + * + * @brief Remove User from Domain Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be modified + * @param[in] user_name The name of the user that is going to be removed from + * the group + * @return NET_API_STATUS + * + * example group/group_deluser.c + ***************************************************************/ + +NET_API_STATUS NetGroupDelUser(const char * server_name /* [in] */, + const char * group_name /* [in] */, + const char * user_name /* [in] */); + +/************************************************************//** + * + * NetGroupGetUsers + * + * @brief Get Users for a group on a server + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The group name to enumerate users for + * @param[in] level The enumeration level used for the query + * @param[out] buffer The returned enumeration buffer + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] total_entries The number of total entries + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example group/group_getusers.c + ***************************************************************/ + +NET_API_STATUS NetGroupGetUsers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetGroupSetUsers + * + * @brief Set Users for a group on a server + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The group name to enumerate users for + * @param[in] level The enumeration level used for the query + * @param[in] buffer The buffer containing a X structure + * @param[in] num_entries The number of X entries in the buffer + * @return NET_API_STATUS + * + * example group/group_setusers.c + ***************************************************************/ + +NET_API_STATUS NetGroupSetUsers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t num_entries /* [in] */); + +/************************************************************//** + * + * NetLocalGroupAdd + * + * @brief Create Local Group + * + * @param[in] server_name The server name to connect to + * @param[in] level The level used for the new group creation + * @param[in] buf The buffer containing the group structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example localgroup/localgroup_add.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buf /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetLocalGroupDel + * + * @brief Delete Local Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be deleted + * @return NET_API_STATUS + * + * example localgroup/localgroup_del.c + ***************************************************************/ + + +NET_API_STATUS NetLocalGroupDel(const char * server_name /* [in] */, + const char * group_name /* [in] */); + +/************************************************************//** + * + * NetLocalGroupGetInfo + * + * @brief Get Local Group Information + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be queried + * @param[in] level The level defining the requested LOCALGROUP_INFO_X structure + * @param[out] buf The buffer containing a LOCALGROUP_INFO_X structure + * @return NET_API_STATUS + * + * example localgroup/localgroup_getinfo.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupGetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buf /* [out] [ref] */); + +/************************************************************//** + * + * NetLocalGroupSetInfo + * + * @brief Set Local Group Information + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to be modified + * @param[in] level The level defining the requested LOCALGROUP_INFO_X structure + * @param[in] buf The buffer containing a LOCALGROUP_INFO_X structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example localgroup/localgroup_setinfo.c + ***************************************************************/ + + +NET_API_STATUS NetLocalGroupSetInfo(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buf /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetLocalGroupEnum + * + * @brief Enumerate local groups on a server + * + * @param[in] server_name The server name to connect to + * @param[in] level The enumeration level used for the query (Currently only + * level 0 is supported) + * @param[out] buffer The returned enumeration buffer + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of returned entries + * @param[out] total_entries The number of total entries + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example localgroup/localgroup_enum.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetLocalGroupAddMembers + * + * @brief Add Members to a Local Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to modified + * @param[in] level The level defining the LOCALGROUP_MEMBERS_INFO_X structure + * @param[in] buffer The buffer containing a LOCALGROUP_MEMBERS_INFO_X structure + * @param[in] total_entries The number of LOCALGROUP_MEMBERS_INFO_X entries in + * the buffer + * @return NET_API_STATUS + * + * example localgroup/localgroup_addmembers.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupAddMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */); + +/************************************************************//** + * + * NetLocalGroupDelMembers + * + * @brief Delete Members from a Local Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to modified + * @param[in] level The level defining the LOCALGROUP_MEMBERS_INFO_X structure + * @param[in] buffer The buffer containing a LOCALGROUP_MEMBERS_INFO_X structure + * @param[in] total_entries The number of LOCALGROUP_MEMBERS_INFO_X entries in + * the buffer + * @return NET_API_STATUS + * + * example localgroup/localgroup_delmembers.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupDelMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */); + +/************************************************************//** + * + * NetLocalGroupGetMembers + * + * @brief Enumerate Members in a local group + * + * @param[in] server_name The server name to connect to + * @param[in] local_group_name The localgroup that is going to be queried + * @param[in] level The level defining the LOCALGROUP_MEMBERS_INFO_X structure + * @param[out] buffer The buffer containing a LOCALGROUP_MEMBERS_INFO_X + * structure + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of LOCALGROUP_MEMBERS_INFO_X entries in the buffer + * @param[out] total_entries The total number of LOCALGROUP_MEMBERS_INFO_X entries for that group + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example localgroup/localgroup_getmembers.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupGetMembers(const char * server_name /* [in] */, + const char * local_group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetLocalGroupSetMembers + * + * @brief Set Members in a Local Group + * + * @param[in] server_name The server name to connect to + * @param[in] group_name The name of the group that is going to modified + * @param[in] level The level defining the LOCALGROUP_MEMBERS_INFO_X structure + * @param[in] buffer The buffer containing a LOCALGROUP_MEMBERS_INFO_X structure + * @param[in] total_entries The number of LOCALGROUP_MEMBERS_INFO_X entries in + * the buffer + * @return NET_API_STATUS + * + * example localgroup/localgroup_setmembers.c + ***************************************************************/ + +NET_API_STATUS NetLocalGroupSetMembers(const char * server_name /* [in] */, + const char * group_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t total_entries /* [in] */); + +/************************************************************//** + * + * NetRemoteTOD + * + * @brief Query remote Time of Day + * + * @param[in] server_name The server name to connect to + * @param[out] buf The buffer containing a TIME_OF_DAY_INFO structure + * @return NET_API_STATUS + * + * example server/remote_tod.c + ***************************************************************/ + +NET_API_STATUS NetRemoteTOD(const char * server_name /* [in] */, + uint8_t **buf /* [out] [ref] */); + +/************************************************************//** + * + * NetShareAdd + * + * @brief Add Share + * + * @param[in] server_name The server name to connect to + * @param[in] level The level defining the requested SHARE_INFO_X structure + * @param[in] buffer The buffer containing a SHARE_INFO_X structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example share/share_add.c + ***************************************************************/ + +NET_API_STATUS NetShareAdd(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetShareDel + * + * @brief Delete Share + * + * @param[in] server_name The server name to connect to + * @param[in] net_name The name of the share to delete + * @param[in] reserved + * @return NET_API_STATUS + * + * example share/share_del.c + ***************************************************************/ + +NET_API_STATUS NetShareDel(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t reserved /* [in] */); + +/************************************************************//** + * + * NetShareEnum + * + * @brief Enumerate Shares + * + * @param[in] server_name The server name to connect to + * @param[in] level The level defining the SHARE_INFO_X structure + * @param[out] buffer The buffer containing a SHARE_INFO_X structure + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of SHARE_INFO_X entries in the buffer + * @param[out] total_entries The total number of SHARE_INFO_X entries + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example share/share_enum.c + ***************************************************************/ + +NET_API_STATUS NetShareEnum(const char * server_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetShareGetInfo + * + * @brief Get Share Info + * + * @param[in] server_name The server name to connect to + * @param[in] net_name The name of the share to query + * @param[in] level The level defining the SHARE_INFO_X structure + * @param[out] buffer The buffer containing a SHARE_INFO_X structure + * @return NET_API_STATUS + * + * example share/share_getinfo.c + ***************************************************************/ + +NET_API_STATUS NetShareGetInfo(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetShareSetInfo + * + * @brief Set Share Info + * + * @param[in] server_name The server name to connect to + * @param[in] net_name The name of the share to query + * @param[in] level The level defining the SHARE_INFO_X structure + * @param[in] buffer The buffer containing a SHARE_INFO_X structure + * @param[out] parm_err The returned parameter error number if any + * @return NET_API_STATUS + * + * example share/share_setinfo.c + ***************************************************************/ + +NET_API_STATUS NetShareSetInfo(const char * server_name /* [in] */, + const char * net_name /* [in] */, + uint32_t level /* [in] */, + uint8_t *buffer /* [in] [ref] */, + uint32_t *parm_err /* [out] [ref] */); + +/************************************************************//** + * + * NetFileClose + * + * @brief Close a file + * + * @param[in] server_name The server name to connect to + * @param[in] fileid The fileid of the file that is going to be closed + * @return NET_API_STATUS + * + * example file/file_close.c + ***************************************************************/ + +NET_API_STATUS NetFileClose(const char * server_name /* [in] */, + uint32_t fileid /* [in] */); + +/************************************************************//** + * + * NetFileGetInfo + * + * @brief Close a file + * + * @param[in] server_name The server name to connect to + * @param[in] fileid The fileid of the file that is going to be closed + * @param[in] level The level of the FILE_INFO_X buffer + * @param[out] buffer The buffer containing a FILE_INFO_X structure + * @return NET_API_STATUS + * + * example file/file_getinfo.c + ***************************************************************/ + +NET_API_STATUS NetFileGetInfo(const char * server_name /* [in] */, + uint32_t fileid /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * NetFileEnum + * + * @brief Enumerate Files + * + * @param[in] server_name The server name to connect to + * @param[in] base_path The + * @param[in] user_name The + * @param[in] level The level defining the FILE_INFO_X structure + * @param[out] buffer The buffer containing a FILE_INFO_X structure + * @param[in] prefmaxlen The requested maximal buffer size + * @param[out] entries_read The number of FILE_INFO_X entries in the buffer + * @param[out] total_entries The total number of FILE_INFO_X entries + * @param[in,out] resume_handle A handle passed in and returned for resuming + * operations + * @return NET_API_STATUS + * + * example file/file_enum.c + ***************************************************************/ + +NET_API_STATUS NetFileEnum(const char * server_name /* [in] */, + const char * base_path /* [in] */, + const char * user_name /* [in] */, + uint32_t level /* [in] */, + uint8_t **buffer /* [out] [ref] */, + uint32_t prefmaxlen /* [in] */, + uint32_t *entries_read /* [out] [ref] */, + uint32_t *total_entries /* [out] [ref] */, + uint32_t *resume_handle /* [in,out] [ref] */); + +/************************************************************//** + * + * NetShutdownInit + * + * @brief Start a machine shutdown + * + * @param[in] server_name The server name to connect to + * @param[in] message The message that is displayed before the shutdown + * @param[in] timeout The amount of seconds to wait until shutting down + * @param[in] force_apps Whether to close all applications before the shutdown + * @param[in] do_reboot Whether to reboot after the shutdown + * @return NET_API_STATUS + * + * example shutdown/shutdown_init.c + ***************************************************************/ + +NET_API_STATUS NetShutdownInit(const char * server_name /* [in] */, + const char * message /* [in] */, + uint32_t timeout /* [in] */, + uint8_t force_apps /* [in] */, + uint8_t do_reboot /* [in] */); + +/************************************************************//** + * + * NetShutdownAbort + * + * @brief Abort an initiated machine shutdown + * + * @param[in] server_name The server name to connect to + * @return NET_API_STATUS + * + * example shutdown/shutdown_abort.c + ***************************************************************/ + +NET_API_STATUS NetShutdownAbort(const char * server_name /* [in] */); + +/************************************************************//** + * + * I_NetLogonControl + * + * @brief Control various aspects of the NETLOGON service + * + * @param[in] server_name The server name to connect to + * @param[in] function_code The function code to call on the server + * @param[in] query_level The level of the NETLOGON_INFO structure returned + * @param[out] buffer The returned buffer containing the NETLOGON_INFO structure + * @return NET_API_STATUS + * + * example netlogon/netlogon_control.c + ***************************************************************/ + +NET_API_STATUS I_NetLogonControl(const char * server_name /* [in] */, + uint32_t function_code /* [in] */, + uint32_t query_level /* [in] */, + uint8_t **buffer /* [out] [ref] */); + +/************************************************************//** + * + * I_NetLogonControl2 + * + * @brief Control various aspects of the NETLOGON service + * + * @param[in] server_name The server name to connect to + * @param[in] function_code The function code to call on the server + * @param[in] query_level The level of the NETLOGON_INFO structure returned + * @param[in] data The buffer containing information related to the function code + * @param[out] buffer The returned buffer containing the NETLOGON_INFO structure + * @return NET_API_STATUS + * + * example netlogon/netlogon_control2.c + ***************************************************************/ + +NET_API_STATUS I_NetLogonControl2(const char * server_name /* [in] */, + uint32_t function_code /* [in] */, + uint32_t query_level /* [in] */, + uint8_t *data /* [in] [ref] */, + uint8_t **buffer /* [out] [ref] */); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __LIB_NETAPI_H__ */ diff --git a/source3/lib/netapi/netapi_net.h b/source3/lib/netapi/netapi_net.h new file mode 100644 index 0000000..8febc2a --- /dev/null +++ b/source3/lib/netapi/netapi_net.h @@ -0,0 +1,26 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Andrew Bartlett 2010 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* This API header is private between the 'net' binary and and libnet */ + +/* This function is to init the libnetapi subsystem, without + * re-reading config files or setting debug levels etc */ +NET_API_STATUS libnetapi_net_init(struct libnetapi_ctx **ctx, + struct loadparm_context *lp_ctx, + struct cli_credentials *creds); diff --git a/source3/lib/netapi/netapi_private.h b/source3/lib/netapi/netapi_private.h new file mode 100644 index 0000000..c0d0ea0 --- /dev/null +++ b/source3/lib/netapi/netapi_private.h @@ -0,0 +1,114 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_NETAPI_PRIVATE_H__ +#define __LIB_NETAPI_PRIVATE_H__ + +#include "lib/netapi/netapi_net.h" +#include "auth/credentials/credentials.h" + +#define LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, fn) \ + DEBUG(10,("redirecting call %s to localhost\n", #fn)); \ + if (!r->in.server_name) { \ + r->in.server_name = "localhost"; \ + } \ + return fn ## _r(ctx, r); + +struct dcerpc_binding_handle; +struct ndr_interface_table; + +struct libnetapi_private_ctx { + struct { + const char *domain_name; + struct dom_sid *domain_sid; + struct rpc_pipe_client *cli; + + uint32_t connect_mask; + struct policy_handle connect_handle; + + uint32_t domain_mask; + struct policy_handle domain_handle; + + uint32_t builtin_mask; + struct policy_handle builtin_handle; + } samr; + + struct client_ipc_connection *ipc_connections; + + struct messaging_context *msg_ctx; +}; + +struct libnetapi_ctx { + char *debuglevel; + char *logfile; + char *error_string; + int disable_policy_handle_cache; + + struct cli_credentials *creds; + + void *private_data; + struct loadparm_context *lp_ctx; +}; + + +NET_API_STATUS libnetapi_set_error_string(struct libnetapi_ctx *ctx, + const char *format, ...) + PRINTF_ATTRIBUTE(2,3); +NET_API_STATUS libnetapi_get_debuglevel(struct libnetapi_ctx *ctx, char **debuglevel); +NET_API_STATUS libnetapi_set_logfile(struct libnetapi_ctx *ctx, + const char *logfile); + +WERROR libnetapi_shutdown_cm(struct libnetapi_ctx *ctx); +WERROR libnetapi_open_pipe(struct libnetapi_ctx *ctx, + const char *server_name, + const struct ndr_interface_table *table, + struct rpc_pipe_client **presult); +WERROR libnetapi_get_binding_handle(struct libnetapi_ctx *ctx, + const char *server_name, + const struct ndr_interface_table *table, + struct dcerpc_binding_handle **binding_handle); +WERROR libnetapi_samr_open_domain(struct libnetapi_ctx *mem_ctx, + struct rpc_pipe_client *pipe_cli, + uint32_t connect_mask, + uint32_t domain_mask, + struct policy_handle *connect_handle, + struct policy_handle *domain_handle, + struct dom_sid2 **domain_sid); +WERROR libnetapi_samr_open_builtin_domain(struct libnetapi_ctx *mem_ctx, + struct rpc_pipe_client *pipe_cli, + uint32_t connect_mask, + uint32_t builtin_mask, + struct policy_handle *connect_handle, + struct policy_handle *builtin_handle); +void libnetapi_samr_close_domain_handle(struct libnetapi_ctx *ctx, + struct policy_handle *handle); +void libnetapi_samr_close_builtin_handle(struct libnetapi_ctx *ctx, + struct policy_handle *handle); +void libnetapi_samr_close_connect_handle(struct libnetapi_ctx *ctx, + struct policy_handle *handle); +void libnetapi_samr_free(struct libnetapi_ctx *ctx); + +NTSTATUS add_GROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + const char *group_name, + uint32_t attributes, + uint8_t **buffer, + uint32_t *num_entries); + +#endif diff --git a/source3/lib/netapi/netlogon.c b/source3/lib/netapi/netlogon.c new file mode 100644 index 0000000..717dd17 --- /dev/null +++ b/source3/lib/netapi/netlogon.c @@ -0,0 +1,248 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi LogonControl Support + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "../librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" + +static WERROR construct_data(enum netr_LogonControlCode function_code, + const uint8_t *data_in, + union netr_CONTROL_DATA_INFORMATION *data_out) +{ + switch (function_code) { + case NETLOGON_CONTROL_QUERY: + case NETLOGON_CONTROL_REDISCOVER: + case NETLOGON_CONTROL_TC_QUERY: + case NETLOGON_CONTROL_CHANGE_PASSWORD: + case NETLOGON_CONTROL_TC_VERIFY: + data_out->domain = (const char *)data_in; + break; + case NETLOGON_CONTROL_FIND_USER: + data_out->user = (const char *)data_in; + break; + case NETLOGON_CONTROL_SET_DBFLAG: + data_out->debug_level = atoi((const char *)data_in); + break; + case NETLOGON_CONTROL_FORCE_DNS_REG: + ZERO_STRUCTP(data_out); + break; + default: + return WERR_INVALID_PARAMETER; + } + + return WERR_OK; +} + +static WERROR construct_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + union netr_CONTROL_QUERY_INFORMATION *q, + uint8_t **buffer) +{ + struct NETLOGON_INFO_1 *i1; + struct NETLOGON_INFO_2 *i2; + struct NETLOGON_INFO_3 *i3; + struct NETLOGON_INFO_4 *i4; + + if (!q) { + return WERR_INVALID_PARAMETER; + } + + switch (level) { + case 1: + i1 = talloc(mem_ctx, struct NETLOGON_INFO_1); + W_ERROR_HAVE_NO_MEMORY(i1); + + i1->netlog1_flags = q->info1->flags; + i1->netlog1_pdc_connection_status = W_ERROR_V(q->info1->pdc_connection_status); + + *buffer = (uint8_t *)i1; + + break; + case 2: + i2 = talloc(mem_ctx, struct NETLOGON_INFO_2); + W_ERROR_HAVE_NO_MEMORY(i2); + + i2->netlog2_flags = q->info2->flags; + i2->netlog2_pdc_connection_status = W_ERROR_V(q->info2->pdc_connection_status); + i2->netlog2_trusted_dc_name = talloc_strdup(mem_ctx, q->info2->trusted_dc_name); + i2->netlog2_tc_connection_status = W_ERROR_V(q->info2->tc_connection_status); + + *buffer = (uint8_t *)i2; + + break; + case 3: + i3 = talloc(mem_ctx, struct NETLOGON_INFO_3); + W_ERROR_HAVE_NO_MEMORY(i3); + + i3->netlog1_flags = q->info3->flags; + i3->netlog3_logon_attempts = q->info3->logon_attempts; + i3->netlog3_reserved1 = q->info3->unknown1; + i3->netlog3_reserved2 = q->info3->unknown2; + i3->netlog3_reserved3 = q->info3->unknown3; + i3->netlog3_reserved4 = q->info3->unknown4; + i3->netlog3_reserved5 = q->info3->unknown5; + + *buffer = (uint8_t *)i3; + + break; + case 4: + i4 = talloc(mem_ctx, struct NETLOGON_INFO_4); + W_ERROR_HAVE_NO_MEMORY(i4); + + i4->netlog4_trusted_dc_name = talloc_strdup(mem_ctx, q->info4->trusted_dc_name); + i4->netlog4_trusted_domain_name = talloc_strdup(mem_ctx, q->info4->trusted_domain_name); + + *buffer = (uint8_t *)i4; + + break; + default: + return WERR_INVALID_LEVEL; + } + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR I_NetLogonControl_r(struct libnetapi_ctx *ctx, + struct I_NetLogonControl *r) +{ + WERROR werr; + NTSTATUS status; + union netr_CONTROL_QUERY_INFORMATION query; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_netlogon, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_netr_LogonControl(b, talloc_tos(), + r->in.server_name, + r->in.function_code, + r->in.query_level, + &query, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = construct_buffer(ctx, r->in.query_level, &query, + r->out.buffer); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR I_NetLogonControl_l(struct libnetapi_ctx *ctx, + struct I_NetLogonControl *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, I_NetLogonControl); +} + +/**************************************************************** +****************************************************************/ + +WERROR I_NetLogonControl2_r(struct libnetapi_ctx *ctx, + struct I_NetLogonControl2 *r) +{ + WERROR werr; + NTSTATUS status; + union netr_CONTROL_DATA_INFORMATION data; + union netr_CONTROL_QUERY_INFORMATION query; + struct dcerpc_binding_handle *b; + + werr = construct_data(r->in.function_code, r->in.data, &data); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_netlogon, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + switch (r->in.function_code) { + case NETLOGON_CONTROL_TC_VERIFY: + case NETLOGON_CONTROL_SET_DBFLAG: + case NETLOGON_CONTROL_FORCE_DNS_REG: + status = dcerpc_netr_LogonControl2Ex(b, talloc_tos(), + r->in.server_name, + r->in.function_code, + r->in.query_level, + &data, + &query, + &werr); + break; + default: + status = dcerpc_netr_LogonControl2(b, talloc_tos(), + r->in.server_name, + r->in.function_code, + r->in.query_level, + &data, + &query, + &werr); + break; + } + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = construct_buffer(ctx, r->in.query_level, &query, + r->out.buffer); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR I_NetLogonControl2_l(struct libnetapi_ctx *ctx, + struct I_NetLogonControl2 *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, I_NetLogonControl2); +} diff --git a/source3/lib/netapi/samr.c b/source3/lib/netapi/samr.c new file mode 100644 index 0000000..c1b7cfa --- /dev/null +++ b/source3/lib/netapi/samr.c @@ -0,0 +1,346 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Samr Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "rpc_client/cli_samr.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" + +/**************************************************************** +****************************************************************/ + +WERROR libnetapi_samr_open_domain(struct libnetapi_ctx *mem_ctx, + struct rpc_pipe_client *pipe_cli, + uint32_t connect_mask, + uint32_t domain_mask, + struct policy_handle *connect_handle, + struct policy_handle *domain_handle, + struct dom_sid2 **domain_sid) +{ + NTSTATUS status, result; + WERROR werr; + struct libnetapi_private_ctx *priv; + uint32_t resume_handle = 0; + uint32_t num_entries = 0; + struct samr_SamArray *sam = NULL; + const char *domain_name = NULL; + struct lsa_String lsa_domain_name; + bool domain_found = false; + int i; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + priv = talloc_get_type_abort(mem_ctx->private_data, + struct libnetapi_private_ctx); + + if (is_valid_policy_hnd(&priv->samr.connect_handle)) { + if ((priv->samr.connect_mask & connect_mask) == connect_mask) { + *connect_handle = priv->samr.connect_handle; + } else { + libnetapi_samr_close_connect_handle(mem_ctx, + &priv->samr.connect_handle); + } + } + + if (is_valid_policy_hnd(&priv->samr.domain_handle)) { + if ((priv->samr.domain_mask & domain_mask) == domain_mask) { + *domain_handle = priv->samr.domain_handle; + } else { + libnetapi_samr_close_domain_handle(mem_ctx, + &priv->samr.domain_handle); + } + } + + if (priv->samr.domain_sid) { + *domain_sid = priv->samr.domain_sid; + } + + if (is_valid_policy_hnd(&priv->samr.connect_handle) && + ((priv->samr.connect_mask & connect_mask) == connect_mask) && + is_valid_policy_hnd(&priv->samr.domain_handle) && + (priv->samr.domain_mask & domain_mask) == domain_mask) { + return WERR_OK; + } + + if (!is_valid_policy_hnd(connect_handle)) { + status = dcerpc_try_samr_connects(pipe_cli->binding_handle, mem_ctx, + pipe_cli->srv_name_slash, + connect_mask, + connect_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + status = dcerpc_samr_EnumDomains(b, mem_ctx, + connect_handle, + &resume_handle, + &sam, + 0xffffffff, + &num_entries, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + for (i=0; i<num_entries; i++) { + + domain_name = sam->entries[i].name.string; + + if (strequal(domain_name, builtin_domain_name())) { + continue; + } + + domain_found = true; + break; + } + + if (!domain_found) { + werr = WERR_NO_SUCH_DOMAIN; + goto done; + } + + init_lsa_String(&lsa_domain_name, domain_name); + + status = dcerpc_samr_LookupDomain(b, mem_ctx, + connect_handle, + &lsa_domain_name, + domain_sid, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + connect_handle, + domain_mask, + *domain_sid, + domain_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + priv->samr.cli = pipe_cli; + + priv->samr.domain_name = domain_name; + priv->samr.domain_sid = *domain_sid; + + priv->samr.connect_mask = connect_mask; + priv->samr.connect_handle = *connect_handle; + + priv->samr.domain_mask = domain_mask; + priv->samr.domain_handle = *domain_handle; + + werr = WERR_OK; + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR libnetapi_samr_open_builtin_domain(struct libnetapi_ctx *mem_ctx, + struct rpc_pipe_client *pipe_cli, + uint32_t connect_mask, + uint32_t builtin_mask, + struct policy_handle *connect_handle, + struct policy_handle *builtin_handle) +{ + NTSTATUS status, result; + WERROR werr; + struct libnetapi_private_ctx *priv; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + priv = talloc_get_type_abort(mem_ctx->private_data, + struct libnetapi_private_ctx); + + if (is_valid_policy_hnd(&priv->samr.connect_handle)) { + if ((priv->samr.connect_mask & connect_mask) == connect_mask) { + *connect_handle = priv->samr.connect_handle; + } else { + libnetapi_samr_close_connect_handle(mem_ctx, + &priv->samr.connect_handle); + } + } + + if (is_valid_policy_hnd(&priv->samr.builtin_handle)) { + if ((priv->samr.builtin_mask & builtin_mask) == builtin_mask) { + *builtin_handle = priv->samr.builtin_handle; + } else { + libnetapi_samr_close_builtin_handle(mem_ctx, + &priv->samr.builtin_handle); + } + } + + if (is_valid_policy_hnd(&priv->samr.connect_handle) && + ((priv->samr.connect_mask & connect_mask) == connect_mask) && + is_valid_policy_hnd(&priv->samr.builtin_handle) && + (priv->samr.builtin_mask & builtin_mask) == builtin_mask) { + return WERR_OK; + } + + if (!is_valid_policy_hnd(connect_handle)) { + status = dcerpc_try_samr_connects(pipe_cli->binding_handle, mem_ctx, + pipe_cli->srv_name_slash, + connect_mask, + connect_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + connect_handle, + builtin_mask, + discard_const_p(struct dom_sid, &global_sid_Builtin), + builtin_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + priv->samr.cli = pipe_cli; + + priv->samr.connect_mask = connect_mask; + priv->samr.connect_handle = *connect_handle; + + priv->samr.builtin_mask = builtin_mask; + priv->samr.builtin_handle = *builtin_handle; + + werr = WERR_OK; + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +void libnetapi_samr_close_domain_handle(struct libnetapi_ctx *ctx, + struct policy_handle *handle) +{ + struct libnetapi_private_ctx *priv; + struct dcerpc_binding_handle *b; + NTSTATUS result; + + if (!is_valid_policy_hnd(handle)) { + return; + } + + priv = talloc_get_type_abort(ctx->private_data, + struct libnetapi_private_ctx); + + if (!ndr_policy_handle_equal(handle, &priv->samr.domain_handle)) { + return; + } + + b = priv->samr.cli->binding_handle; + + dcerpc_samr_Close(b, ctx, handle, &result); + + ZERO_STRUCT(priv->samr.domain_handle); +} + +/**************************************************************** +****************************************************************/ + +void libnetapi_samr_close_builtin_handle(struct libnetapi_ctx *ctx, + struct policy_handle *handle) +{ + struct libnetapi_private_ctx *priv; + struct dcerpc_binding_handle *b; + NTSTATUS result; + + if (!is_valid_policy_hnd(handle)) { + return; + } + + priv = talloc_get_type_abort(ctx->private_data, + struct libnetapi_private_ctx); + + if (!ndr_policy_handle_equal(handle, &priv->samr.builtin_handle)) { + return; + } + + b = priv->samr.cli->binding_handle; + + dcerpc_samr_Close(b, ctx, handle, &result); + + ZERO_STRUCT(priv->samr.builtin_handle); +} + +/**************************************************************** +****************************************************************/ + +void libnetapi_samr_close_connect_handle(struct libnetapi_ctx *ctx, + struct policy_handle *handle) +{ + struct libnetapi_private_ctx *priv; + struct dcerpc_binding_handle *b; + NTSTATUS result; + + if (!is_valid_policy_hnd(handle)) { + return; + } + + priv = talloc_get_type_abort(ctx->private_data, + struct libnetapi_private_ctx); + + if (!ndr_policy_handle_equal(handle, &priv->samr.connect_handle)) { + return; + } + + b = priv->samr.cli->binding_handle; + + dcerpc_samr_Close(b, ctx, handle, &result); + + ZERO_STRUCT(priv->samr.connect_handle); +} + +/**************************************************************** +****************************************************************/ + +void libnetapi_samr_free(struct libnetapi_ctx *ctx) +{ + struct libnetapi_private_ctx *priv; + + if (!ctx->private_data) { + return; + } + + priv = talloc_get_type_abort(ctx->private_data, + struct libnetapi_private_ctx); + + libnetapi_samr_close_domain_handle(ctx, &priv->samr.domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &priv->samr.builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &priv->samr.connect_handle); +} diff --git a/source3/lib/netapi/serverinfo.c b/source3/lib/netapi/serverinfo.c new file mode 100644 index 0000000..cb5d9a2 --- /dev/null +++ b/source3/lib/netapi/serverinfo.c @@ -0,0 +1,701 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Server Support + * Copyright (C) Guenther Deschner 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "../librpc/gen_ndr/ndr_srvsvc_c.h" +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_reg.h" +#include "libsmb/dsgetdcname.h" + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerGetInfo_l_101(struct libnetapi_ctx *ctx, + uint8_t **buffer) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + struct SERVER_INFO_101 i; + + i.sv101_platform_id = PLATFORM_ID_NT; + i.sv101_name = lp_netbios_name(); + i.sv101_version_major = SAMBA_MAJOR_NBT_ANNOUNCE_VERSION; + i.sv101_version_minor = SAMBA_MINOR_NBT_ANNOUNCE_VERSION; + i.sv101_type = lp_default_server_announce(); + i.sv101_comment = lp_server_string(ctx, lp_sub); + + *buffer = (uint8_t *)talloc_memdup(ctx, &i, sizeof(i)); + if (!*buffer) { + return WERR_NOT_ENOUGH_MEMORY; + } + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerGetInfo_l_1005(struct libnetapi_ctx *ctx, + uint8_t **buffer) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + struct SERVER_INFO_1005 info1005; + + info1005.sv1005_comment = lp_server_string(ctx, lp_sub); + *buffer = (uint8_t *)talloc_memdup(ctx, &info1005, sizeof(info1005)); + if (!*buffer) { + return WERR_NOT_ENOUGH_MEMORY; + } + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetServerGetInfo_l(struct libnetapi_ctx *ctx, + struct NetServerGetInfo *r) +{ + switch (r->in.level) { + case 101: + return NetServerGetInfo_l_101(ctx, r->out.buffer); + case 1005: + return NetServerGetInfo_l_1005(ctx, r->out.buffer); + default: + break; + } + + return WERR_INVALID_LEVEL; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS map_server_info_to_SERVER_INFO_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + union srvsvc_NetSrvInfo *i, + uint8_t **buffer) +{ + struct SERVER_INFO_100 i100; + struct SERVER_INFO_101 i101; + struct SERVER_INFO_102 i102; + struct SERVER_INFO_402 i402; + struct SERVER_INFO_403 i403; + struct SERVER_INFO_502 i502; + struct SERVER_INFO_503 i503; + struct SERVER_INFO_599 i599; + struct SERVER_INFO_1005 i1005; +#if 0 + struct SERVER_INFO_1010 i1010; + struct SERVER_INFO_1016 i1016; + struct SERVER_INFO_1017 i1017; + struct SERVER_INFO_1018 i1018; + struct SERVER_INFO_1107 i1107; + struct SERVER_INFO_1501 i1501; + struct SERVER_INFO_1502 i1502; + struct SERVER_INFO_1503 i1503; + struct SERVER_INFO_1506 i1506; + struct SERVER_INFO_1509 i1509; + struct SERVER_INFO_1510 i1510; + struct SERVER_INFO_1511 i1511; + struct SERVER_INFO_1512 i1512; + struct SERVER_INFO_1513 i1513; + struct SERVER_INFO_1514 i1514; + struct SERVER_INFO_1515 i1515; + struct SERVER_INFO_1516 i1516; + struct SERVER_INFO_1518 i1518; + struct SERVER_INFO_1520 i1520; + struct SERVER_INFO_1521 i1521; + struct SERVER_INFO_1522 i1522; + struct SERVER_INFO_1523 i1523; + struct SERVER_INFO_1524 i1524; + struct SERVER_INFO_1525 i1525; + struct SERVER_INFO_1528 i1528; + struct SERVER_INFO_1529 i1529; + struct SERVER_INFO_1530 i1530; + struct SERVER_INFO_1533 i1533; + struct SERVER_INFO_1534 i1534; + struct SERVER_INFO_1535 i1535; + struct SERVER_INFO_1536 i1536; + struct SERVER_INFO_1537 i1537; + struct SERVER_INFO_1538 i1538; + struct SERVER_INFO_1539 i1539; + struct SERVER_INFO_1540 i1540; + struct SERVER_INFO_1541 i1541; + struct SERVER_INFO_1542 i1542; + struct SERVER_INFO_1543 i1543; + struct SERVER_INFO_1544 i1544; + struct SERVER_INFO_1545 i1545; + struct SERVER_INFO_1546 i1546; + struct SERVER_INFO_1547 i1547; + struct SERVER_INFO_1548 i1548; + struct SERVER_INFO_1549 i1549; + struct SERVER_INFO_1550 i1550; + struct SERVER_INFO_1552 i1552; + struct SERVER_INFO_1553 i1553; + struct SERVER_INFO_1554 i1554; + struct SERVER_INFO_1555 i1555; + struct SERVER_INFO_1556 i1556; + struct SERVER_INFO_1557 i1557; + struct SERVER_INFO_1560 i1560; + struct SERVER_INFO_1561 i1561; + struct SERVER_INFO_1562 i1562; + struct SERVER_INFO_1563 i1563; + struct SERVER_INFO_1564 i1564; + struct SERVER_INFO_1565 i1565; + struct SERVER_INFO_1566 i1566; + struct SERVER_INFO_1567 i1567; + struct SERVER_INFO_1568 i1568; + struct SERVER_INFO_1569 i1569; + struct SERVER_INFO_1570 i1570; + struct SERVER_INFO_1571 i1571; + struct SERVER_INFO_1572 i1572; + struct SERVER_INFO_1573 i1573; + struct SERVER_INFO_1574 i1574; + struct SERVER_INFO_1575 i1575; + struct SERVER_INFO_1576 i1576; + struct SERVER_INFO_1577 i1577; + struct SERVER_INFO_1578 i1578; + struct SERVER_INFO_1579 i1579; + struct SERVER_INFO_1580 i1580; + struct SERVER_INFO_1581 i1581; + struct SERVER_INFO_1582 i1582; + struct SERVER_INFO_1583 i1583; + struct SERVER_INFO_1584 i1584; + struct SERVER_INFO_1585 i1585; + struct SERVER_INFO_1586 i1586; + struct SERVER_INFO_1587 i1587; + struct SERVER_INFO_1588 i1588; + struct SERVER_INFO_1590 i1590; + struct SERVER_INFO_1591 i1591; + struct SERVER_INFO_1592 i1592; + struct SERVER_INFO_1593 i1593; + struct SERVER_INFO_1594 i1594; + struct SERVER_INFO_1595 i1595; + struct SERVER_INFO_1596 i1596; + struct SERVER_INFO_1597 i1597; + struct SERVER_INFO_1598 i1598; + struct SERVER_INFO_1599 i1599; + struct SERVER_INFO_1600 i1600; + struct SERVER_INFO_1601 i1601; + struct SERVER_INFO_1602 i1602; +#endif + uint32_t num_info = 0; + + switch (level) { + case 100: + i100.sv100_platform_id = i->info100->platform_id; + i100.sv100_name = talloc_strdup(mem_ctx, i->info100->server_name); + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_100, i100, + (struct SERVER_INFO_100 **)buffer, + &num_info); + break; + + case 101: + i101.sv101_platform_id = i->info101->platform_id; + i101.sv101_name = talloc_strdup(mem_ctx, i->info101->server_name); + i101.sv101_version_major = i->info101->version_major; + i101.sv101_version_minor = i->info101->version_minor; + i101.sv101_type = i->info101->server_type; + i101.sv101_comment = talloc_strdup(mem_ctx, i->info101->comment); + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_101, i101, + (struct SERVER_INFO_101 **)buffer, + &num_info); + break; + + case 102: + i102.sv102_platform_id = i->info102->platform_id; + i102.sv102_name = talloc_strdup(mem_ctx, i->info102->server_name); + i102.sv102_version_major = i->info102->version_major; + i102.sv102_version_minor = i->info102->version_minor; + i102.sv102_type = i->info102->server_type; + i102.sv102_comment = talloc_strdup(mem_ctx, i->info102->comment); + i102.sv102_users = i->info102->users; + i102.sv102_disc = i->info102->disc; + i102.sv102_hidden = i->info102->hidden; + i102.sv102_announce = i->info102->announce; + i102.sv102_anndelta = i->info102->anndelta; + i102.sv102_licenses = i->info102->licenses; + i102.sv102_userpath = talloc_strdup(mem_ctx, i->info102->userpath); + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_102, i102, + (struct SERVER_INFO_102 **)buffer, + &num_info); + break; + + case 402: + + i402.sv402_ulist_mtime = i->info402->ulist_mtime; + i402.sv402_glist_mtime = i->info402->glist_mtime; + i402.sv402_alist_mtime = i->info402->alist_mtime; + i402.sv402_alerts = talloc_strdup(mem_ctx, i->info402->alerts); + i402.sv402_security = i->info402->security; + i402.sv402_numadmin = i->info402->numadmin; + i402.sv402_lanmask = i->info402->lanmask; + i402.sv402_guestacct = talloc_strdup(mem_ctx, i->info402->guestaccount); + i402.sv402_chdevs = i->info402->chdevs; + i402.sv402_chdevq = i->info402->chdevqs; + i402.sv402_chdevjobs = i->info402->chdevjobs; + i402.sv402_connections = i->info402->connections; + i402.sv402_shares = i->info402->shares; + i402.sv402_openfiles = i->info402->openfiles; + i402.sv402_sessopens = i->info402->sessopen; + i402.sv402_sessvcs = i->info402->sesssvc; + i402.sv402_sessreqs = i->info402->sessreqs; + i402.sv402_opensearch = i->info402->opensearch; + i402.sv402_activelocks = i->info402->activelocks; + i402.sv402_numreqbuf = i->info402->numreqbufs; + i402.sv402_sizreqbuf = i->info402->sizereqbufs; + i402.sv402_numbigbuf = i->info402->numbigbufs; + i402.sv402_numfiletasks = i->info402->numfiletasks; + i402.sv402_alertsched = i->info402->alertsched; + i402.sv402_erroralert = i->info402->erroralert; + i402.sv402_logonalert = i->info402->logonalert; + i402.sv402_accessalert = i->info402->accessalert; + i402.sv402_diskalert = i->info402->diskalert; + i402.sv402_netioalert = i->info402->netioalert; + i402.sv402_maxauditsz = i->info402->maxaudits; + i402.sv402_srvheuristics = i->info402->srvheuristics; + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_402, i402, + (struct SERVER_INFO_402 **)buffer, + &num_info); + break; + + case 403: + + i403.sv403_ulist_mtime = i->info403->ulist_mtime; + i403.sv403_glist_mtime = i->info403->glist_mtime; + i403.sv403_alist_mtime = i->info403->alist_mtime; + i403.sv403_alerts = talloc_strdup(mem_ctx, i->info403->alerts); + i403.sv403_security = i->info403->security; + i403.sv403_numadmin = i->info403->numadmin; + i403.sv403_lanmask = i->info403->lanmask; + i403.sv403_guestacct = talloc_strdup(mem_ctx, i->info403->guestaccount); + i403.sv403_chdevs = i->info403->chdevs; + i403.sv403_chdevq = i->info403->chdevqs; + i403.sv403_chdevjobs = i->info403->chdevjobs; + i403.sv403_connections = i->info403->connections; + i403.sv403_shares = i->info403->shares; + i403.sv403_openfiles = i->info403->openfiles; + i403.sv403_sessopens = i->info403->sessopen; + i403.sv403_sessvcs = i->info403->sesssvc; + i403.sv403_sessreqs = i->info403->sessreqs; + i403.sv403_opensearch = i->info403->opensearch; + i403.sv403_activelocks = i->info403->activelocks; + i403.sv403_numreqbuf = i->info403->numreqbufs; + i403.sv403_sizreqbuf = i->info403->sizereqbufs; + i403.sv403_numbigbuf = i->info403->numbigbufs; + i403.sv403_numfiletasks = i->info403->numfiletasks; + i403.sv403_alertsched = i->info403->alertsched; + i403.sv403_erroralert = i->info403->erroralert; + i403.sv403_logonalert = i->info403->logonalert; + i403.sv403_accessalert = i->info403->accessalert; + i403.sv403_diskalert = i->info403->diskalert; + i403.sv403_netioalert = i->info403->netioalert; + i403.sv403_maxauditsz = i->info403->maxaudits; + i403.sv403_srvheuristics = i->info403->srvheuristics; + i403.sv403_auditedevents = i->info403->auditedevents; + i403.sv403_autoprofile = i->info403->auditprofile; + i403.sv403_autopath = talloc_strdup(mem_ctx, i->info403->autopath); + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_403, i403, + (struct SERVER_INFO_403 **)buffer, + &num_info); + break; + + case 502: + i502.sv502_sessopens = i->info502->sessopen; + i502.sv502_sessvcs = i->info502->sesssvc; + i502.sv502_opensearch = i->info502->opensearch; + i502.sv502_sizreqbuf = i->info502->sizereqbufs; + i502.sv502_initworkitems = i->info502->initworkitems; + i502.sv502_maxworkitems = i->info502->maxworkitems; + i502.sv502_rawworkitems = i->info502->rawworkitems; + i502.sv502_irpstacksize = i->info502->irpstacksize; + i502.sv502_maxrawbuflen = i->info502->maxrawbuflen; + i502.sv502_sessusers = i->info502->sessusers; + i502.sv502_sessconns = i->info502->sessconns; + i502.sv502_maxpagedmemoryusage = i->info502->maxpagedmemoryusage; + i502.sv502_maxnonpagedmemoryusage = i->info502->maxnonpagedmemoryusage; + i502.sv502_enablesoftcompat = i->info502->enablesoftcompat; + i502.sv502_enableforcedlogoff = i->info502->enableforcedlogoff; + i502.sv502_timesource = i->info502->timesource; + i502.sv502_acceptdownlevelapis = i->info502->acceptdownlevelapis; + i502.sv502_lmannounce = i->info502->lmannounce; + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_502, i502, + (struct SERVER_INFO_502 **)buffer, + &num_info); + break; + + case 503: + i503.sv503_sessopens = i->info503->sessopen; + i503.sv503_sessvcs = i->info503->sesssvc; + i503.sv503_opensearch = i->info503->opensearch; + i503.sv503_sizreqbuf = i->info503->sizereqbufs; + i503.sv503_initworkitems = i->info503->initworkitems; + i503.sv503_maxworkitems = i->info503->maxworkitems; + i503.sv503_rawworkitems = i->info503->rawworkitems; + i503.sv503_irpstacksize = i->info503->irpstacksize; + i503.sv503_maxrawbuflen = i->info503->maxrawbuflen; + i503.sv503_sessusers = i->info503->sessusers; + i503.sv503_sessconns = i->info503->sessconns; + i503.sv503_maxpagedmemoryusage = i->info503->maxpagedmemoryusage; + i503.sv503_maxnonpagedmemoryusage = i->info503->maxnonpagedmemoryusage; + i503.sv503_enablesoftcompat = i->info503->enablesoftcompat; + i503.sv503_enableforcedlogoff = i->info503->enableforcedlogoff; + i503.sv503_timesource = i->info503->timesource; + i503.sv503_acceptdownlevelapis = i->info503->acceptdownlevelapis; + i503.sv503_lmannounce = i->info503->lmannounce; + i503.sv503_domain = talloc_strdup(mem_ctx, i->info503->domain); + i503.sv503_maxcopyreadlen = i->info503->maxcopyreadlen; + i503.sv503_maxcopywritelen = i->info503->maxcopywritelen; + i503.sv503_minkeepsearch = i->info503->minkeepsearch; + i503.sv503_maxkeepsearch = i->info503->maxkeepsearch; + i503.sv503_minkeepcomplsearch = i->info503->minkeepcomplsearch; + i503.sv503_maxkeepcomplsearch = i->info503->maxkeepcomplsearch; + i503.sv503_threadcountadd = i->info503->threadcountadd; + i503.sv503_numblockthreads = i->info503->numlockthreads; + i503.sv503_scavtimeout = i->info503->scavtimeout; + i503.sv503_minrcvqueue = i->info503->minrcvqueue; + i503.sv503_minfreeworkitems = i->info503->minfreeworkitems; + i503.sv503_xactmemsize = i->info503->xactmemsize; + i503.sv503_threadpriority = i->info503->threadpriority; + i503.sv503_maxmpxct = i->info503->maxmpxct; + i503.sv503_oplockbreakwait = i->info503->oplockbreakwait; + i503.sv503_oplockbreakresponsewait = i->info503->oplockbreakresponsewait; + i503.sv503_enableoplocks = i->info503->enableoplocks; + i503.sv503_enableoplockforceclose = i->info503->enableoplockforceclose; + i503.sv503_enablefcbopens = i->info503->enablefcbopens; + i503.sv503_enableraw = i->info503->enableraw; + i503.sv503_enablesharednetdrives = i->info503->enablesharednetdrives; + i503.sv503_minfreeconnections = i->info503->minfreeconnections; + i503.sv503_maxfreeconnections = i->info503->maxfreeconnections; + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_503, i503, + (struct SERVER_INFO_503 **)buffer, + &num_info); + break; + + case 599: + i599.sv599_sessopens = i->info599->sessopen; + i599.sv599_sessvcs = i->info599->sesssvc; + i599.sv599_opensearch = i->info599->opensearch; + i599.sv599_sizreqbuf = i->info599->sizereqbufs; + i599.sv599_initworkitems = i->info599->initworkitems; + i599.sv599_maxworkitems = i->info599->maxworkitems; + i599.sv599_rawworkitems = i->info599->rawworkitems; + i599.sv599_irpstacksize = i->info599->irpstacksize; + i599.sv599_maxrawbuflen = i->info599->maxrawbuflen; + i599.sv599_sessusers = i->info599->sessusers; + i599.sv599_sessconns = i->info599->sessconns; + i599.sv599_maxpagedmemoryusage = i->info599->maxpagedmemoryusage; + i599.sv599_maxnonpagedmemoryusage = i->info599->maxnonpagedmemoryusage; + i599.sv599_enablesoftcompat = i->info599->enablesoftcompat; + i599.sv599_enableforcedlogoff = i->info599->enableforcedlogoff; + i599.sv599_timesource = i->info599->timesource; + i599.sv599_acceptdownlevelapis = i->info599->acceptdownlevelapis; + i599.sv599_lmannounce = i->info599->lmannounce; + i599.sv599_domain = talloc_strdup(mem_ctx, i->info599->domain); + i599.sv599_maxcopyreadlen = i->info599->maxcopyreadlen; + i599.sv599_maxcopywritelen = i->info599->maxcopywritelen; + i599.sv599_minkeepsearch = i->info599->minkeepsearch; + i599.sv599_maxkeepsearch = 0; /* ?? */ + i599.sv599_minkeepcomplsearch = i->info599->minkeepcomplsearch; + i599.sv599_maxkeepcomplsearch = i->info599->maxkeepcomplsearch; + i599.sv599_threadcountadd = i->info599->threadcountadd; + i599.sv599_numblockthreads = i->info599->numlockthreads; /* typo ? */ + i599.sv599_scavtimeout = i->info599->scavtimeout; + i599.sv599_minrcvqueue = i->info599->minrcvqueue; + i599.sv599_minfreeworkitems = i->info599->minfreeworkitems; + i599.sv599_xactmemsize = i->info599->xactmemsize; + i599.sv599_threadpriority = i->info599->threadpriority; + i599.sv599_maxmpxct = i->info599->maxmpxct; + i599.sv599_oplockbreakwait = i->info599->oplockbreakwait; + i599.sv599_oplockbreakresponsewait = i->info599->oplockbreakresponsewait; + i599.sv599_enableoplocks = i->info599->enableoplocks; + i599.sv599_enableoplockforceclose = i->info599->enableoplockforceclose; + i599.sv599_enablefcbopens = i->info599->enablefcbopens; + i599.sv599_enableraw = i->info599->enableraw; + i599.sv599_enablesharednetdrives = i->info599->enablesharednetdrives; + i599.sv599_minfreeconnections = i->info599->minfreeconnections; + i599.sv599_maxfreeconnections = i->info599->maxfreeconnections; + i599.sv599_initsesstable = i->info599->initsesstable; + i599.sv599_initconntable = i->info599->initconntable; + i599.sv599_initfiletable = i->info599->initfiletable; + i599.sv599_initsearchtable = i->info599->initsearchtable; + i599.sv599_alertschedule = i->info599->alertsched; + i599.sv599_errorthreshold = i->info599->errortreshold; + i599.sv599_networkerrorthreshold = i->info599->networkerrortreshold; + i599.sv599_diskspacethreshold = i->info599->diskspacetreshold; + i599.sv599_reserved = i->info599->reserved; + i599.sv599_maxlinkdelay = i->info599->maxlinkdelay; + i599.sv599_minlinkthroughput = i->info599->minlinkthroughput; + i599.sv599_linkinfovalidtime = i->info599->linkinfovalidtime; + i599.sv599_scavqosinfoupdatetime = i->info599->scavqosinfoupdatetime; + i599.sv599_maxworkitemidletime = i->info599->maxworkitemidletime; + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_599, i599, + (struct SERVER_INFO_599 **)buffer, + &num_info); + break; + + case 1005: + i1005.sv1005_comment = talloc_strdup(mem_ctx, i->info1005->comment); + + ADD_TO_ARRAY(mem_ctx, struct SERVER_INFO_1005, i1005, + (struct SERVER_INFO_1005 **)buffer, + &num_info); + break; + default: + return NT_STATUS_NOT_SUPPORTED; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetServerGetInfo_r(struct libnetapi_ctx *ctx, + struct NetServerGetInfo *r) +{ + NTSTATUS status; + WERROR werr; + union srvsvc_NetSrvInfo info; + struct dcerpc_binding_handle *b; + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 100: + case 101: + case 102: + case 402: + case 502: + case 503: + case 1005: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_srvsvc_NetSrvGetInfo(b, talloc_tos(), + r->in.server_name, + r->in.level, + &info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = map_server_info_to_SERVER_INFO_buffer(ctx, r->in.level, &info, + r->out.buffer); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerSetInfo_l_1005(struct libnetapi_ctx *ctx, + struct NetServerSetInfo *r) +{ + WERROR werr = WERR_OK; + sbcErr err; + struct smbconf_ctx *conf_ctx; + struct srvsvc_NetSrvInfo1005 *info1005; + + if (!r->in.buffer) { + *r->out.parm_error = 1005; /* sure here ? */ + return WERR_INVALID_PARAMETER; + } + + info1005 = (struct srvsvc_NetSrvInfo1005 *)r->in.buffer; + + if (!info1005->comment) { + *r->out.parm_error = 1005; + return WERR_INVALID_PARAMETER; + } + + if (!lp_config_backend_is_registry()) { + libnetapi_set_error_string(ctx, + "Configuration manipulation requested but not " + "supported by backend"); + return WERR_NOT_SUPPORTED; + } + + err = smbconf_init_reg(ctx, &conf_ctx, NULL); + if (!SBC_ERROR_IS_OK(err)) { + libnetapi_set_error_string(ctx, + "Could not initialize backend: %s", + sbcErrorString(err)); + werr = WERR_SERVICE_DOES_NOT_EXIST; + goto done; + } + + err = smbconf_set_global_parameter(conf_ctx, "server string", + info1005->comment); + if (!SBC_ERROR_IS_OK(err)) { + libnetapi_set_error_string(ctx, + "Could not set global parameter: %s", + sbcErrorString(err)); + werr = WERR_SERVICE_DOES_NOT_EXIST; + goto done; + } + + done: + smbconf_shutdown(conf_ctx); + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetServerSetInfo_l(struct libnetapi_ctx *ctx, + struct NetServerSetInfo *r) +{ + switch (r->in.level) { + case 1005: + return NetServerSetInfo_l_1005(ctx, r); + default: + break; + } + + return WERR_INVALID_LEVEL; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetServerSetInfo_r(struct libnetapi_ctx *ctx, + struct NetServerSetInfo *r) +{ + NTSTATUS status; + WERROR werr; + union srvsvc_NetSrvInfo info; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + switch (r->in.level) { + case 1005: + info.info1005 = (struct srvsvc_NetSrvInfo1005 *)r->in.buffer; + break; + default: + werr = WERR_NOT_SUPPORTED; + goto done; + } + + status = dcerpc_srvsvc_NetSrvSetInfo(b, talloc_tos(), + r->in.server_name, + r->in.level, + &info, + r->out.parm_error, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetRemoteTOD_r(struct libnetapi_ctx *ctx, + struct NetRemoteTOD *r) +{ + NTSTATUS status; + WERROR werr; + struct srvsvc_NetRemoteTODInfo *info = NULL; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_srvsvc_NetRemoteTOD(b, talloc_tos(), + r->in.server_name, + &info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + *r->out.buffer = (uint8_t *)talloc_memdup(ctx, info, + sizeof(struct srvsvc_NetRemoteTODInfo)); + W_ERROR_HAVE_NO_MEMORY(*r->out.buffer); + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetRemoteTOD_l(struct libnetapi_ctx *ctx, + struct NetRemoteTOD *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetRemoteTOD); +} + diff --git a/source3/lib/netapi/share.c b/source3/lib/netapi/share.c new file mode 100644 index 0000000..7f142fc --- /dev/null +++ b/source3/lib/netapi/share.c @@ -0,0 +1,573 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Share Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "../librpc/gen_ndr/ndr_srvsvc_c.h" +#include "librpc/gen_ndr/ndr_security.h" + +/**************************************************************** +****************************************************************/ + +static NTSTATUS map_srvsvc_share_info_to_SHARE_INFO_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + union srvsvc_NetShareInfo *info, + uint8_t **buffer, + uint32_t *num_shares) +{ + struct SHARE_INFO_0 i0; + struct SHARE_INFO_1 i1; + struct SHARE_INFO_2 i2; + struct SHARE_INFO_501 i501; + struct SHARE_INFO_1005 i1005; + + struct srvsvc_NetShareInfo0 *s0; + struct srvsvc_NetShareInfo1 *s1; + struct srvsvc_NetShareInfo2 *s2; + struct srvsvc_NetShareInfo501 *s501; + struct srvsvc_NetShareInfo1005 *s1005; + + if (!buffer) { + return NT_STATUS_INVALID_PARAMETER; + } + + switch (level) { + case 0: + s0 = info->info0; + + i0.shi0_netname = talloc_strdup(mem_ctx, s0->name); + + ADD_TO_ARRAY(mem_ctx, struct SHARE_INFO_0, i0, + (struct SHARE_INFO_0 **)buffer, + num_shares); + break; + + case 1: + s1 = info->info1; + + i1.shi1_netname = talloc_strdup(mem_ctx, s1->name); + i1.shi1_type = s1->type; + i1.shi1_remark = talloc_strdup(mem_ctx, s1->comment); + + ADD_TO_ARRAY(mem_ctx, struct SHARE_INFO_1, i1, + (struct SHARE_INFO_1 **)buffer, + num_shares); + break; + + case 2: + s2 = info->info2; + + i2.shi2_netname = talloc_strdup(mem_ctx, s2->name); + i2.shi2_type = s2->type; + i2.shi2_remark = talloc_strdup(mem_ctx, s2->comment); + i2.shi2_permissions = s2->permissions; + i2.shi2_max_uses = s2->max_users; + i2.shi2_current_uses = s2->current_users; + i2.shi2_path = talloc_strdup(mem_ctx, s2->path); + i2.shi2_passwd = talloc_strdup(mem_ctx, s2->password); + + ADD_TO_ARRAY(mem_ctx, struct SHARE_INFO_2, i2, + (struct SHARE_INFO_2 **)buffer, + num_shares); + break; + + case 501: + s501 = info->info501; + + i501.shi501_netname = talloc_strdup(mem_ctx, s501->name); + i501.shi501_type = s501->type; + i501.shi501_remark = talloc_strdup(mem_ctx, s501->comment); + i501.shi501_flags = s501->csc_policy; + + ADD_TO_ARRAY(mem_ctx, struct SHARE_INFO_501, i501, + (struct SHARE_INFO_501 **)buffer, + num_shares); + break; + + case 1005: + s1005 = info->info1005; + + i1005.shi1005_flags = s1005->dfs_flags; + + ADD_TO_ARRAY(mem_ctx, struct SHARE_INFO_1005, i1005, + (struct SHARE_INFO_1005 **)buffer, + num_shares); + break; + + default: + return NT_STATUS_INVALID_PARAMETER; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS map_SHARE_INFO_buffer_to_srvsvc_share_info(TALLOC_CTX *mem_ctx, + uint8_t *buffer, + uint32_t level, + union srvsvc_NetShareInfo *info) +{ + struct SHARE_INFO_2 *i2 = NULL; + struct SHARE_INFO_502 *i502 = NULL; + struct SHARE_INFO_1004 *i1004 = NULL; + struct srvsvc_NetShareInfo2 *s2 = NULL; + struct srvsvc_NetShareInfo502 *s502 = NULL; + struct srvsvc_NetShareInfo1004 *s1004 = NULL; + + if (!buffer) { + return NT_STATUS_INVALID_PARAMETER; + } + + switch (level) { + case 2: + i2 = (struct SHARE_INFO_2 *)buffer; + + s2 = talloc(mem_ctx, struct srvsvc_NetShareInfo2); + NT_STATUS_HAVE_NO_MEMORY(s2); + + s2->name = i2->shi2_netname; + s2->type = i2->shi2_type; + s2->comment = i2->shi2_remark; + s2->permissions = i2->shi2_permissions; + s2->max_users = i2->shi2_max_uses; + s2->current_users = i2->shi2_current_uses; + s2->path = i2->shi2_path; + s2->password = i2->shi2_passwd; + + info->info2 = s2; + + break; + + case 502: + i502 = (struct SHARE_INFO_502 *)buffer; + + s502 = talloc(mem_ctx, struct srvsvc_NetShareInfo502); + NT_STATUS_HAVE_NO_MEMORY(s502); + + s502->name = i502->shi502_netname; + s502->type = i502->shi502_type; + s502->comment = i502->shi502_remark; + s502->permissions = i502->shi502_permissions; + s502->max_users = i502->shi502_max_uses; + s502->current_users = i502->shi502_current_uses; + s502->path = i502->shi502_path; + s502->password = i502->shi502_passwd; + s502->sd_buf.sd_size = + ndr_size_security_descriptor(i502->shi502_security_descriptor, 0); + s502->sd_buf.sd = i502->shi502_security_descriptor; + + info->info502 = s502; + + break; + + case 1004: + i1004 = (struct SHARE_INFO_1004 *)buffer; + + s1004 = talloc(mem_ctx, struct srvsvc_NetShareInfo1004); + NT_STATUS_HAVE_NO_MEMORY(s1004); + + s1004->comment = i1004->shi1004_remark; + + info->info1004 = s1004; + + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareAdd_r(struct libnetapi_ctx *ctx, + struct NetShareAdd *r) +{ + WERROR werr; + NTSTATUS status; + union srvsvc_NetShareInfo info; + struct dcerpc_binding_handle *b; + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 2: + case 502: + break; + case 503: + return WERR_NOT_SUPPORTED; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = map_SHARE_INFO_buffer_to_srvsvc_share_info(ctx, + r->in.buffer, + r->in.level, + &info); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_srvsvc_NetShareAdd(b, talloc_tos(), + r->in.server_name, + r->in.level, + &info, + r->out.parm_err, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareAdd_l(struct libnetapi_ctx *ctx, + struct NetShareAdd *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShareAdd); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareDel_r(struct libnetapi_ctx *ctx, + struct NetShareDel *r) +{ + WERROR werr; + NTSTATUS status; + struct dcerpc_binding_handle *b; + + if (!r->in.net_name) { + return WERR_INVALID_PARAMETER; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_srvsvc_NetShareDel(b, talloc_tos(), + r->in.server_name, + r->in.net_name, + r->in.reserved, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareDel_l(struct libnetapi_ctx *ctx, + struct NetShareDel *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShareDel); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareEnum_r(struct libnetapi_ctx *ctx, + struct NetShareEnum *r) +{ + WERROR werr; + NTSTATUS status; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr0 ctr0; + struct srvsvc_NetShareCtr1 ctr1; + struct srvsvc_NetShareCtr2 ctr2; + uint32_t i; + struct dcerpc_binding_handle *b; + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + case 2: + break; + case 502: + case 503: + return WERR_NOT_SUPPORTED; + default: + return WERR_INVALID_LEVEL; + } + + ZERO_STRUCT(info_ctr); + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + info_ctr.level = r->in.level; + switch (r->in.level) { + case 0: + ZERO_STRUCT(ctr0); + info_ctr.ctr.ctr0 = &ctr0; + break; + case 1: + ZERO_STRUCT(ctr1); + info_ctr.ctr.ctr1 = &ctr1; + break; + case 2: + ZERO_STRUCT(ctr2); + info_ctr.ctr.ctr2 = &ctr2; + break; + } + + status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(), + r->in.server_name, + &info_ctr, + r->in.prefmaxlen, + r->out.total_entries, + r->out.resume_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr) && !W_ERROR_EQUAL(werr, WERR_MORE_DATA)) { + goto done; + } + + for (i=0; i < info_ctr.ctr.ctr1->count; i++) { + union srvsvc_NetShareInfo _i = {0}; + switch (r->in.level) { + case 0: + _i.info0 = &info_ctr.ctr.ctr0->array[i]; + break; + case 1: + _i.info1 = &info_ctr.ctr.ctr1->array[i]; + break; + case 2: + _i.info2 = &info_ctr.ctr.ctr2->array[i]; + break; + } + + status = map_srvsvc_share_info_to_SHARE_INFO_buffer(ctx, + r->in.level, + &_i, + r->out.buffer, + r->out.entries_read); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareEnum_l(struct libnetapi_ctx *ctx, + struct NetShareEnum *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShareEnum); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareGetInfo_r(struct libnetapi_ctx *ctx, + struct NetShareGetInfo *r) +{ + WERROR werr; + NTSTATUS status; + union srvsvc_NetShareInfo info; + uint32_t num_entries = 0; + struct dcerpc_binding_handle *b; + + if (!r->in.net_name || !r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + case 2: + case 501: + case 1005: + break; + case 502: + case 503: + return WERR_NOT_SUPPORTED; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_srvsvc_NetShareGetInfo(b, talloc_tos(), + r->in.server_name, + r->in.net_name, + r->in.level, + &info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = map_srvsvc_share_info_to_SHARE_INFO_buffer(ctx, + r->in.level, + &info, + r->out.buffer, + &num_entries); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareGetInfo_l(struct libnetapi_ctx *ctx, + struct NetShareGetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShareGetInfo); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareSetInfo_r(struct libnetapi_ctx *ctx, + struct NetShareSetInfo *r) +{ + WERROR werr; + NTSTATUS status; + union srvsvc_NetShareInfo info; + struct dcerpc_binding_handle *b; + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 2: + case 1004: + break; + case 1: + case 502: + case 503: + case 1005: + case 1006: + case 1501: + return WERR_NOT_SUPPORTED; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_srvsvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = map_SHARE_INFO_buffer_to_srvsvc_share_info(ctx, + r->in.buffer, + r->in.level, + &info); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_srvsvc_NetShareSetInfo(b, talloc_tos(), + r->in.server_name, + r->in.net_name, + r->in.level, + &info, + r->out.parm_err, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShareSetInfo_l(struct libnetapi_ctx *ctx, + struct NetShareSetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShareSetInfo); +} diff --git a/source3/lib/netapi/shutdown.c b/source3/lib/netapi/shutdown.c new file mode 100644 index 0000000..9e1e8e1 --- /dev/null +++ b/source3/lib/netapi/shutdown.c @@ -0,0 +1,110 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Shutdown Support + * Copyright (C) Guenther Deschner 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "../librpc/gen_ndr/ndr_initshutdown_c.h" +#include "rpc_client/init_lsa.h" + +/**************************************************************** +****************************************************************/ + +WERROR NetShutdownInit_r(struct libnetapi_ctx *ctx, + struct NetShutdownInit *r) +{ + WERROR werr; + NTSTATUS status; + struct lsa_StringLarge message; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_initshutdown, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_StringLarge(&message, r->in.message); + + status = dcerpc_initshutdown_Init(b, talloc_tos(), + NULL, + &message, + r->in.timeout, + r->in.force_apps, + r->in.do_reboot, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShutdownInit_l(struct libnetapi_ctx *ctx, + struct NetShutdownInit *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShutdownInit); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShutdownAbort_r(struct libnetapi_ctx *ctx, + struct NetShutdownAbort *r) +{ + WERROR werr; + NTSTATUS status; + struct dcerpc_binding_handle *b; + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_initshutdown, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_initshutdown_Abort(b, talloc_tos(), + NULL, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetShutdownAbort_l(struct libnetapi_ctx *ctx, + struct NetShutdownAbort *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetShutdownAbort); +} diff --git a/source3/lib/netapi/sid.c b/source3/lib/netapi/sid.c new file mode 100644 index 0000000..ba22374 --- /dev/null +++ b/source3/lib/netapi/sid.c @@ -0,0 +1,71 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "lib/netapi/netapi.h" +#include "../libcli/security/security.h" + +/**************************************************************** +****************************************************************/ + +int ConvertSidToStringSid(const struct domsid *sid, + char **sid_string) +{ + struct dom_sid_buf buf; + + if (!sid || !sid_string) { + return false; + } + + *sid_string = SMB_STRDUP( + dom_sid_str_buf((const struct dom_sid *)sid, &buf)); + + if (!*sid_string) { + return false; + } + + return true; +} + +/**************************************************************** +****************************************************************/ + +int ConvertStringSidToSid(const char *sid_string, + struct domsid **sid) +{ + struct dom_sid _sid; + + if (!sid_string || !sid) { + return false; + } + + if (!string_to_sid(&_sid, sid_string)) { + return false; + } + + *sid = (struct domsid *)SMB_MALLOC(sizeof(struct domsid)); + if (!*sid) { + return false; + } + + sid_copy((struct dom_sid*)*sid, &_sid); + + return true; +} diff --git a/source3/lib/netapi/tests/common.c b/source3/lib/netapi/tests/common.c new file mode 100644 index 0000000..a01a870 --- /dev/null +++ b/source3/lib/netapi/tests/common.c @@ -0,0 +1,116 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <inttypes.h> + +#include <popt.h> +#include <netapi.h> + +#include "common.h" + +void popt_common_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + struct libnetapi_ctx *ctx = NULL; + + libnetapi_getctx(&ctx); + + if (reason == POPT_CALLBACK_REASON_PRE) { + } + + if (reason == POPT_CALLBACK_REASON_POST) { + } + + if (!opt) { + return; + } + switch (opt->val) { + case 'U': { + char *puser = strdup(arg); + char *p = NULL; + + if ((p = strchr(puser,'%'))) { + size_t len; + *p = 0; + libnetapi_set_username(ctx, puser); + libnetapi_set_password(ctx, p+1); + len = strlen(p+1); + memset(strchr(arg,'%')+1,'X',len); + } else { + libnetapi_set_username(ctx, puser); + } + free(puser); + break; + } + case 'd': + libnetapi_set_debuglevel(ctx, arg); + break; + case 'p': + libnetapi_set_password(ctx, arg); + break; + case 'k': + libnetapi_set_use_kerberos(ctx); + break; + } +} + +struct poptOption popt_common_netapi_examples[] = { + { + .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, + .arg = (void *)popt_common_callback, + }, + { + .longName = "user", + .shortName = 'U', + .argInfo = POPT_ARG_STRING, + .val = 'U', + .descrip = "Username used for connection", + .argDescrip = "USERNAME", + }, + { + .longName = "password", + .shortName = 'p', + .argInfo = POPT_ARG_STRING, + .val = 'p', + .descrip = "Password used for connection", + .argDescrip = "PASSWORD", + }, + { + .longName = "debuglevel", + .shortName = 'd', + .argInfo = POPT_ARG_STRING, + .val = 'd', + .descrip = "Debuglevel", + .argDescrip = "DEBUGLEVEL", + }, + { + .longName = "kerberos", + .shortName = 'k', + .argInfo = POPT_ARG_NONE, + .val = 'k', + .descrip = "Use Kerberos", + }, + POPT_TABLEEND +}; + diff --git a/source3/lib/netapi/tests/common.h b/source3/lib/netapi/tests/common.h new file mode 100644 index 0000000..be1a2e4 --- /dev/null +++ b/source3/lib/netapi/tests/common.h @@ -0,0 +1,67 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <popt.h> + +void popt_common_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data); + +extern struct poptOption popt_common_netapi_examples[]; + +#define POPT_COMMON_LIBNETAPI_EXAMPLES { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_netapi_examples, 0, "Common samba netapi example options:", NULL }, + +#ifndef POPT_TABLEEND +#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL } +#endif + +NET_API_STATUS test_netuseradd(const char *hostname, + const char *username); + +NET_API_STATUS netapitest_localgroup(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_user(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_group(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_display(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_share(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_file(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_server(struct libnetapi_ctx *ctx, + const char *hostname); +NET_API_STATUS netapitest_wksta(struct libnetapi_ctx *ctx, + const char *hostname); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +#define NETAPI_STATUS(x,y,fn) \ + printf("FAILURE: line %d: %s failed with status: %s (%d)\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y); + +#define NETAPI_STATUS_MSG(x,y,fn,z) \ + printf("FAILURE: line %d: %s failed with status: %s (%d), %s\n", \ + __LINE__, fn, libnetapi_get_error_string(x,y), y, z); + +#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) diff --git a/source3/lib/netapi/tests/netapitest.c b/source3/lib/netapi/tests/netapitest.c new file mode 100644 index 0000000..fd367bd --- /dev/null +++ b/source3/lib/netapi/tests/netapitest.c @@ -0,0 +1,112 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +int main(int argc, const char **argv) +{ + NET_API_STATUS status = 0; + struct libnetapi_ctx *ctx = NULL; + const char *hostname = NULL; + + poptContext pc; + int opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_LIBNETAPI_EXAMPLES + POPT_TABLEEND + }; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + pc = poptGetContext("netapitest", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "hostname"); + while((opt = poptGetNextOpt(pc)) != -1) { + } + + if (!poptPeekArg(pc)) { + poptPrintHelp(pc, stderr, 0); + goto out; + } + hostname = poptGetArg(pc); + + status = netapitest_localgroup(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_user(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_group(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_display(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_share(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_file(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_server(ctx, hostname); + if (status) { + goto out; + } + + status = netapitest_wksta(ctx, hostname); + if (status) { + goto out; + } + + out: + if (status != 0) { + printf("testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + libnetapi_free(ctx); + poptFreeContext(pc); + + return status; +} diff --git a/source3/lib/netapi/tests/netdisplay.c b/source3/lib/netapi/tests/netdisplay.c new file mode 100644 index 0000000..d7967fa --- /dev/null +++ b/source3/lib/netapi/tests/netdisplay.c @@ -0,0 +1,150 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroup testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +static NET_API_STATUS test_netquerydisplayinformation(const char *hostname, + uint32_t level, + const char *name) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + int found_name = 0; + const char *current_name; + uint8_t *buffer = NULL; + uint32_t idx = 0; + int i; + + struct NET_DISPLAY_USER *user = NULL; + struct NET_DISPLAY_GROUP *group = NULL; + struct NET_DISPLAY_MACHINE *machine = NULL; + + printf("testing NetQueryDisplayInformation level %d\n", level); + + do { + status = NetQueryDisplayInformation(hostname, + level, + idx, + 1000, + (uint32_t)-1, + &entries_read, + (void **)&buffer); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 1: + user = (struct NET_DISPLAY_USER *)buffer; + break; + case 2: + machine = (struct NET_DISPLAY_MACHINE *)buffer; + break; + case 3: + group = (struct NET_DISPLAY_GROUP *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 1: + current_name = user->usri1_name; + break; + case 2: + current_name = machine->usri2_name; + break; + case 3: + current_name = group->grpi3_name; + break; + default: + break; + } + + if (name && strcasecmp(current_name, name) == 0) { + found_name = 1; + } + + switch (level) { + case 1: + user++; + break; + case 2: + machine++; + break; + case 3: + group++; + break; + } + } + NetApiBufferFree(buffer); + } + idx += entries_read; + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (name && !found_name) { + printf("failed to get name\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS netapitest_display(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + uint32_t levels[] = { 1, 2, 3}; + int i; + + printf("NetDisplay tests\n"); + + /* test enum */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + status = test_netquerydisplayinformation(hostname, levels[i], NULL); + if (status) { + NETAPI_STATUS(ctx, status, "NetQueryDisplayInformation"); + goto out; + } + } + + status = 0; + + printf("NetDisplay tests succeeded\n"); + out: + if (status != 0) { + printf("NetDisplay testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netfile.c b/source3/lib/netapi/tests/netfile.c new file mode 100644 index 0000000..87aff02 --- /dev/null +++ b/source3/lib/netapi/tests/netfile.c @@ -0,0 +1,138 @@ +/* + * Unix SMB/CIFS implementation. + * NetFile testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +static NET_API_STATUS test_netfileenum(const char *hostname, + uint32_t level) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + uint8_t *buffer = NULL; + int i; + + printf("testing NetFileEnum level %d\n", level); + + do { + status = NetFileEnum(hostname, + NULL, + NULL, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 2: + break; + case 3: + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 2: + case 3: + break; + default: + break; + } + + switch (level) { + case 2: + break; + case 3: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + return 0; +} + +NET_API_STATUS netapitest_file(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + uint32_t enum_levels[] = { 2, 3 }; + int i; + + printf("NetFile tests\n"); + + /* test enum */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netfileenum(hostname, enum_levels[i]); + if (status) { + NETAPI_STATUS(ctx, status, "NetFileEnum"); + goto out; + } + } + + /* basic queries */ +#if 0 + { + uint32_t levels[] = { 2, 3 }; + for (i=0; i<ARRAY_SIZE(levels); i++) { + uint8_t *buffer = NULL; + + printf("testing NetFileGetInfo level %d\n", levels[i]); + + status = NetFileGetInfo(hostname, fid, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetFileGetInfo"); + goto out; + } + } + } +#endif + + status = 0; + + printf("NetFile tests succeeded\n"); + out: + if (status != 0) { + printf("NetFile testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netgroup.c b/source3/lib/netapi/tests/netgroup.c new file mode 100644 index 0000000..5180c49 --- /dev/null +++ b/source3/lib/netapi/tests/netgroup.c @@ -0,0 +1,503 @@ +/* + * Unix SMB/CIFS implementation. + * NetGroup testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +static NET_API_STATUS test_netgroupenum(const char *hostname, + uint32_t level, + const char *groupname) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_group = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct GROUP_INFO_0 *info0 = NULL; + struct GROUP_INFO_1 *info1 = NULL; + struct GROUP_INFO_2 *info2 = NULL; + struct GROUP_INFO_3 *info3 = NULL; + + printf("testing NetGroupEnum level %d\n", level); + + do { + status = NetGroupEnum(hostname, + level, + &buffer, + 120, /*(uint32_t)-1, */ + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct GROUP_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_INFO_1 *)buffer; + break; + case 2: + info2 = (struct GROUP_INFO_2 *)buffer; + break; + case 3: + info3 = (struct GROUP_INFO_3 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = info0->grpi0_name; + break; + case 1: + current_name = info1->grpi1_name; + break; + case 2: + current_name = info2->grpi2_name; + break; + case 3: + current_name = info3->grpi3_name; + break; + default: + break; + } + + if (strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + case 2: + info2++; + break; + case 3: + info3++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_group) { + printf("failed to get group\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netgroupgetusers(const char *hostname, + uint32_t level, + const char *groupname, + const char *username) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_user = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct GROUP_USERS_INFO_0 *info0 = NULL; + struct GROUP_USERS_INFO_1 *info1 = NULL; + + printf("testing NetGroupGetUsers level %d\n", level); + + do { + status = NetGroupGetUsers(hostname, + groupname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + + switch (level) { + case 0: + info0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + info1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + break; + } + for (i=0; i<entries_read; i++) { + switch (level) { + case 0: + current_name = info0->grui0_name; + break; + case 1: + current_name = info1->grui1_name; + break; + default: + break; + } + + if (username && strcasecmp(current_name, username) == 0) { + found_user = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (username && !found_user) { + printf("failed to get user\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netgroupsetusers(const char *hostname, + const char *groupname, + uint32_t level, + size_t num_entries, + const char **names) +{ + NET_API_STATUS status; + uint8_t *buffer = NULL; + int i = 0; + size_t buf_size = 0; + + struct GROUP_USERS_INFO_0 *g0 = NULL; + struct GROUP_USERS_INFO_1 *g1 = NULL; + + printf("testing NetGroupSetUsers level %d\n", level); + + switch (level) { + case 0: + buf_size = sizeof(struct GROUP_USERS_INFO_0) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g0); + if (status) { + goto out; + } + + for (i=0; i<num_entries; i++) { + g0[i].grui0_name = names[i]; + } + + buffer = (uint8_t *)g0; + break; + case 1: + buf_size = sizeof(struct GROUP_USERS_INFO_1) * num_entries; + + status = NetApiBufferAllocate(buf_size, (void **)&g1); + if (status) { + goto out; + } + + for (i=0; i<num_entries; i++) { + g1[i].grui1_name = names[i]; + } + + buffer = (uint8_t *)g1; + break; + default: + break; + } + + /* NetGroupSetUsers */ + + status = NetGroupSetUsers(hostname, + groupname, + level, + buffer, + num_entries); + if (status) { + goto out; + } + + out: + NetApiBufferFree(buffer); + return status; +} + +NET_API_STATUS netapitest_group(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + const char *username, *groupname, *groupname2; + uint8_t *buffer = NULL; + struct GROUP_INFO_0 g0; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 2, 3}; + uint32_t enum_levels[] = { 0, 1, 2, 3}; + uint32_t getmem_levels[] = { 0, 1}; + int i; + + printf("NetGroup tests\n"); + + username = "torture_test_user"; + groupname = "torture_test_group"; + groupname2 = "torture_test_group2"; + + /* cleanup */ + NetGroupDel(hostname, groupname); + NetGroupDel(hostname, groupname2); + NetUserDel(hostname, username); + + /* add a group */ + + g0.grpi0_name = groupname; + + printf("testing NetGroupAdd\n"); + + status = NetGroupAdd(hostname, 0, (uint8_t *)&g0, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupAdd"); + goto out; + } + + /* 2nd add must fail */ + + status = NetGroupAdd(hostname, 0, (uint8_t *)&g0, &parm_err); + if (status == 0) { + NETAPI_STATUS(ctx, status, "NetGroupAdd"); + goto out; + } + + /* test enum */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netgroupenum(hostname, enum_levels[i], groupname); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupEnum"); + goto out; + } + } + + /* basic queries */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + printf("testing NetGroupGetInfo level %d\n", levels[i]); + + status = NetGroupGetInfo(hostname, groupname, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetGroupGetInfo"); + goto out; + } + } + + /* group rename */ + + g0.grpi0_name = groupname2; + + printf("testing NetGroupSetInfo level 0\n"); + + status = NetGroupSetInfo(hostname, groupname, 0, (uint8_t *)&g0, &parm_err); + switch ((int)status) { + case 0: + break; + case 50: /* not supported */ + case 124: /* not implemented */ + groupname2 = groupname; + goto skip_rename; + default: + NETAPI_STATUS(ctx, status, "NetGroupSetInfo"); + goto out; + } + + /* should not exist anymore */ + + status = NetGroupDel(hostname, groupname); + if (status == 0) { + NETAPI_STATUS(ctx, status, "NetGroupDel"); + goto out; + } + + skip_rename: + /* query info */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + status = NetGroupGetInfo(hostname, groupname2, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetGroupGetInfo"); + goto out; + } + } + + /* add user to group */ + + status = test_netuseradd(hostname, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserAdd"); + goto out; + } + + /* should not be member */ + + for (i=0; i<ARRAY_SIZE(getmem_levels); i++) { + + status = test_netgroupgetusers(hostname, getmem_levels[i], groupname2, NULL); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupGetUsers"); + goto out; + } + } + + printf("testing NetGroupAddUser\n"); + + status = NetGroupAddUser(hostname, groupname2, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupAddUser"); + goto out; + } + + /* should be member */ + + for (i=0; i<ARRAY_SIZE(getmem_levels); i++) { + + status = test_netgroupgetusers(hostname, getmem_levels[i], groupname2, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupGetUsers"); + goto out; + } + } + + printf("testing NetGroupDelUser\n"); + + status = NetGroupDelUser(hostname, groupname2, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupDelUser"); + goto out; + } + + /* should not be member */ + + status = test_netgroupgetusers(hostname, 0, groupname2, NULL); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupGetUsers"); + goto out; + } + + /* set it again via explicit member set */ + + status = test_netgroupsetusers(hostname, groupname2, 0, 1, &username); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupSetUsers"); + goto out; + } + + /* should be member */ + + status = test_netgroupgetusers(hostname, 0, groupname2, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupGetUsers"); + goto out; + } +#if 0 + /* wipe out member list */ + + status = test_netgroupsetusers(hostname, groupname2, 0, 0, NULL); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupSetUsers"); + goto out; + } + + /* should not be member */ + + status = test_netgroupgetusers(hostname, 0, groupname2, NULL); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupGetUsers"); + goto out; + } +#endif + status = NetUserDel(hostname, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserDel"); + goto out; + } + + /* delete */ + + printf("testing NetGroupDel\n"); + + status = NetGroupDel(hostname, groupname2); + if (status) { + NETAPI_STATUS(ctx, status, "NetGroupDel"); + goto out; + }; + + /* should not exist anymore */ + + status = NetGroupGetInfo(hostname, groupname2, 0, &buffer); + if (status == 0) { + NETAPI_STATUS_MSG(ctx, status, "NetGroupGetInfo", "expected failure and error code"); + goto out; + }; + + status = 0; + + printf("NetGroup tests succeeded\n"); + out: + if (status != 0) { + printf("NetGroup testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netlocalgroup.c b/source3/lib/netapi/tests/netlocalgroup.c new file mode 100644 index 0000000..76c59c8 --- /dev/null +++ b/source3/lib/netapi/tests/netlocalgroup.c @@ -0,0 +1,226 @@ +/* + * Unix SMB/CIFS implementation. + * NetLocalGroup testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +static NET_API_STATUS test_netlocalgroupenum(const char *hostname, + uint32_t level, + const char *groupname) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_group = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct LOCALGROUP_INFO_0 *info0 = NULL; + struct LOCALGROUP_INFO_1 *info1 = NULL; + + printf("testing NetLocalGroupEnum level %d\n", level); + + do { + status = NetLocalGroupEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct LOCALGROUP_INFO_0 *)buffer; + break; + case 1: + info1 = (struct LOCALGROUP_INFO_1 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = info0->lgrpi0_name; + break; + case 1: + current_name = info1->lgrpi1_name; + break; + default: + break; + } + + if (strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_group) { + printf("failed to get group\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS netapitest_localgroup(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + const char *groupname, *groupname2; + uint8_t *buffer = NULL; + struct LOCALGROUP_INFO_0 g0; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 1002 }; + uint32_t enum_levels[] = { 0, 1 }; + int i; + + printf("NetLocalgroup tests\n"); + + groupname = "torture_test_localgroup"; + groupname2 = "torture_test_localgroup2"; + + /* cleanup */ + NetLocalGroupDel(hostname, groupname); + NetLocalGroupDel(hostname, groupname2); + + /* add a localgroup */ + + printf("testing NetLocalGroupAdd\n"); + + g0.lgrpi0_name = groupname; + + status = NetLocalGroupAdd(hostname, 0, (uint8_t *)&g0, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetLocalGroupAdd"); + goto out; + }; + + /* test enum */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netlocalgroupenum(hostname, enum_levels[i], groupname); + if (status) { + NETAPI_STATUS(ctx, status, "NetLocalGroupEnum"); + goto out; + } + } + + + /* basic queries */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + printf("testing NetLocalGroupGetInfo level %d\n", levels[i]); + + status = NetLocalGroupGetInfo(hostname, groupname, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetLocalGroupGetInfo"); + goto out; + }; + } + + /* alias rename */ + + printf("testing NetLocalGroupSetInfo level 0\n"); + + g0.lgrpi0_name = groupname2; + + status = NetLocalGroupSetInfo(hostname, groupname, 0, (uint8_t *)&g0, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetLocalGroupSetInfo"); + goto out; + }; + + /* should not exist anymore */ + + status = NetLocalGroupDel(hostname, groupname); + if (status == 0) { + NETAPI_STATUS(ctx, status, "NetLocalGroupDel"); + goto out; + }; + + /* query info */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + status = NetLocalGroupGetInfo(hostname, groupname2, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetLocalGroupGetInfo"); + goto out; + }; + } + + /* delete */ + + printf("testing NetLocalGroupDel\n"); + + status = NetLocalGroupDel(hostname, groupname2); + if (status) { + NETAPI_STATUS(ctx, status, "NetLocalGroupDel"); + goto out; + }; + + /* should not exist anymore */ + + status = NetLocalGroupGetInfo(hostname, groupname2, 0, &buffer); + if (status == 0) { + NETAPI_STATUS(ctx, status, "NetLocalGroupGetInfo"); + goto out; + }; + + status = 0; + + printf("NetLocalgroup tests succeeded\n"); + out: + if (status != 0) { + printf("NetLocalGroup testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netserver.c b/source3/lib/netapi/tests/netserver.c new file mode 100644 index 0000000..f7b9286 --- /dev/null +++ b/source3/lib/netapi/tests/netserver.c @@ -0,0 +1,61 @@ +/* + * Unix SMB/CIFS implementation. + * NetServer testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +NET_API_STATUS netapitest_server(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + uint32_t levels[] = { 100, 101, 102, 402, 403, 502, 503, 1005 }; + int i; + + printf("NetServer tests\n"); + + /* basic queries */ + for (i=0; i<ARRAY_SIZE(levels); i++) { + uint8_t *buffer = NULL; + printf("testing NetServerGetInfo level %d\n", levels[i]); + + status = NetServerGetInfo(hostname, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetServerGetInfo"); + goto out; + } + } + + status = 0; + + printf("NetServer tests succeeded\n"); + out: + if (status != 0) { + printf("NetServer testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netshare.c b/source3/lib/netapi/tests/netshare.c new file mode 100644 index 0000000..a518ce9 --- /dev/null +++ b/source3/lib/netapi/tests/netshare.c @@ -0,0 +1,250 @@ +/* + * Unix SMB/CIFS implementation. + * NetShare testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +static NET_API_STATUS test_netshareenum(const char *hostname, + uint32_t level, + const char *sharename) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + int found_share = 0; + const char *current_name = NULL; + uint8_t *buffer = NULL; + int i; + + struct SHARE_INFO_0 *i0 = NULL; + struct SHARE_INFO_1 *i1 = NULL; + struct SHARE_INFO_2 *i2 = NULL; + + printf("testing NetShareEnum level %d\n", level); + + do { + status = NetShareEnum(hostname, + level, + &buffer, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + i0 = (struct SHARE_INFO_0 *)buffer; + break; + case 1: + i1 = (struct SHARE_INFO_1 *)buffer; + break; + case 2: + i2 = (struct SHARE_INFO_2 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = i0->shi0_netname; + break; + case 1: + current_name = i1->shi1_netname; + break; + case 2: + current_name = i2->shi2_netname; + break; + default: + break; + } + + if (strcasecmp(current_name, sharename) == 0) { + found_share = 1; + } + + switch (level) { + case 0: + i0++; + break; + case 1: + i1++; + break; + case 2: + i2++; + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_share) { + printf("failed to get share\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS netapitest_share(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + const char *sharename, *comment; + uint8_t *buffer = NULL; + struct SHARE_INFO_2 i2; + struct SHARE_INFO_502 i502; + struct SHARE_INFO_1004 i1004; + struct SHARE_INFO_501 *i501 = NULL; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 2, 501, 1005 }; + uint32_t enum_levels[] = { 0, 1, 2 }; + int i; + + printf("NetShare tests\n"); + + sharename = "torture_test_share"; + + /* cleanup */ + NetShareDel(hostname, sharename, 0); + + /* add a share */ + + printf("testing NetShareAdd\n"); + + ZERO_STRUCT(i502); + + i502.shi502_netname = sharename; + i502.shi502_path = "c:\\"; + + status = NetShareAdd(hostname, 502, (uint8_t *)&i502, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareAdd"); + goto out; + }; + + status = NetShareDel(hostname, sharename, 0); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareDel"); + goto out; + }; + + ZERO_STRUCT(i2); + + i2.shi2_netname = sharename; + i2.shi2_path = "c:\\"; + + status = NetShareAdd(hostname, 2, (uint8_t *)&i2, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareAdd"); + goto out; + }; + + /* test enum */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netshareenum(hostname, enum_levels[i], sharename); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareEnum"); + goto out; + } + } + + /* basic queries */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + printf("testing NetShareGetInfo level %d\n", levels[i]); + + status = NetShareGetInfo(hostname, sharename, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetShareGetInfo"); + goto out; + } + } + + + comment = "NetApi generated comment"; + + i1004.shi1004_remark = comment; + + printf("testing NetShareSetInfo level 1004\n"); + + status = NetShareSetInfo(hostname, sharename, 1004, (uint8_t *)&i1004, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareSetInfo"); + goto out; + } + + status = NetShareGetInfo(hostname, sharename, 501, (uint8_t **)&i501); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareGetInfo"); + goto out; + } + + if (strcasecmp(i501->shi501_remark, comment) != 0) { + NETAPI_STATUS(ctx, status, "NetShareGetInfo"); + goto out; + } + + /* delete */ + + printf("testing NetShareDel\n"); + + status = NetShareDel(hostname, sharename, 0); + if (status) { + NETAPI_STATUS(ctx, status, "NetShareDel"); + goto out; + }; + + /* should not exist anymore */ + + status = NetShareGetInfo(hostname, sharename, 0, &buffer); + if (status == 0) { + NETAPI_STATUS(ctx, status, "NetShareGetInfo"); + goto out; + }; + + status = 0; + + printf("NetShare tests succeeded\n"); + out: + if (status != 0) { + printf("NetShare testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netuser.c b/source3/lib/netapi/tests/netuser.c new file mode 100644 index 0000000..ad2bb53 --- /dev/null +++ b/source3/lib/netapi/tests/netuser.c @@ -0,0 +1,462 @@ +/* + * Unix SMB/CIFS implementation. + * NetUser testsuite + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +static NET_API_STATUS test_netuserenum(const char *hostname, + uint32_t level, + const char *username) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + const char *current_name = NULL; + int found_user = 0; + uint8_t *buffer = NULL; + int i; + + struct USER_INFO_0 *info0 = NULL; + struct USER_INFO_1 *info1 = NULL; + struct USER_INFO_2 *info2 = NULL; + struct USER_INFO_3 *info3 = NULL; + struct USER_INFO_4 *info4 = NULL; + struct USER_INFO_10 *info10 = NULL; + struct USER_INFO_11 *info11 = NULL; + struct USER_INFO_20 *info20 = NULL; + struct USER_INFO_23 *info23 = NULL; + + printf("testing NetUserEnum level %d\n", level); + + do { + status = NetUserEnum(hostname, + level, + FILTER_NORMAL_ACCOUNT, + &buffer, + 120, /*(uint32_t)-1, */ + &entries_read, + &total_entries, + &resume_handle); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + info0 = (struct USER_INFO_0 *)buffer; + break; + case 1: + info1 = (struct USER_INFO_1 *)buffer; + break; + case 2: + info2 = (struct USER_INFO_2 *)buffer; + break; + case 3: + info3 = (struct USER_INFO_3 *)buffer; + break; + case 4: + info4 = (struct USER_INFO_4 *)buffer; + break; + case 10: + info10 = (struct USER_INFO_10 *)buffer; + break; + case 11: + info11 = (struct USER_INFO_11 *)buffer; + break; + case 20: + info20 = (struct USER_INFO_20 *)buffer; + break; + case 23: + info23 = (struct USER_INFO_23 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = info0->usri0_name; + break; + case 1: + current_name = info1->usri1_name; + break; + case 2: + current_name = info2->usri2_name; + break; + case 3: + current_name = info3->usri3_name; + break; + case 4: + current_name = info4->usri4_name; + break; + case 10: + current_name = info10->usri10_name; + break; + case 11: + current_name = info11->usri11_name; + break; + case 20: + current_name = info20->usri20_name; + break; + case 23: + current_name = info23->usri23_name; + break; + default: + return -1; + } + + if (strcasecmp(current_name, username) == 0) { + found_user = 1; + } + + switch (level) { + case 0: + info0++; + break; + case 1: + info1++; + break; + case 2: + info2++; + break; + case 3: + info3++; + break; + case 4: + info4++; + break; + case 10: + info10++; + break; + case 11: + info11++; + break; + case 20: + info20++; + break; + case 23: + info23++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (!found_user) { + printf("failed to get user\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS test_netuseradd(const char *hostname, + const char *username) +{ + struct USER_INFO_1 u1; + uint32_t parm_err = 0; + + ZERO_STRUCT(u1); + + printf("testing NetUserAdd\n"); + + u1.usri1_name = username; + u1.usri1_password = "W297!832jD8J"; + u1.usri1_password_age = 0; + u1.usri1_priv = 0; + u1.usri1_home_dir = NULL; + u1.usri1_comment = "User created using Samba NetApi Example code"; + u1.usri1_flags = 0; + u1.usri1_script_path = NULL; + + return NetUserAdd(hostname, 1, (uint8_t *)&u1, &parm_err); +} + +static NET_API_STATUS test_netusermodals(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status; + struct USER_MODALS_INFO_0 *u0 = NULL; + struct USER_MODALS_INFO_0 *_u0 = NULL; + uint8_t *buffer = NULL; + uint32_t parm_err = 0; + uint32_t levels[] = { 0, 1, 2, 3 }; + int i = 0; + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + printf("testing NetUserModalsGet level %d\n", levels[i]); + + status = NetUserModalsGet(hostname, levels[i], &buffer); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserModalsGet"); + return status; + } + } + + status = NetUserModalsGet(hostname, 0, (uint8_t **)&u0); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserModalsGet"); + return status; + } + + printf("testing NetUserModalsSet\n"); + + status = NetUserModalsSet(hostname, 0, (uint8_t *)u0, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserModalsSet"); + return status; + } + + status = NetUserModalsGet(hostname, 0, (uint8_t **)&_u0); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserModalsGet"); + return status; + } + + if (memcmp(u0, _u0, sizeof(*u0)) != 0) { + printf("USER_MODALS_INFO_0 struct has changed!!!!\n"); + return -1; + } + + return 0; +} + +static NET_API_STATUS test_netusergetgroups(const char *hostname, + uint32_t level, + const char *username, + const char *groupname) +{ + NET_API_STATUS status; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + const char *current_name; + int found_group = 0; + uint8_t *buffer = NULL; + int i; + + struct GROUP_USERS_INFO_0 *i0 = NULL; + struct GROUP_USERS_INFO_1 *i1 = NULL; + + printf("testing NetUserGetGroups level %d\n", level); + + do { + status = NetUserGetGroups(hostname, + username, + level, + &buffer, + 120, /*(uint32_t)-1, */ + &entries_read, + &total_entries); + if (status == 0 || status == ERROR_MORE_DATA) { + switch (level) { + case 0: + i0 = (struct GROUP_USERS_INFO_0 *)buffer; + break; + case 1: + i1 = (struct GROUP_USERS_INFO_1 *)buffer; + break; + default: + return -1; + } + + for (i=0; i<entries_read; i++) { + + switch (level) { + case 0: + current_name = i0->grui0_name; + break; + case 1: + current_name = i1->grui1_name; + break; + default: + return -1; + } + + if (groupname && strcasecmp(current_name, groupname) == 0) { + found_group = 1; + } + + switch (level) { + case 0: + i0++; + break; + case 1: + i1++; + break; + default: + break; + } + } + NetApiBufferFree(buffer); + } + } while (status == ERROR_MORE_DATA); + + if (status) { + return status; + } + + if (groupname && !found_group) { + printf("failed to get membership\n"); + return -1; + } + + return 0; +} + +NET_API_STATUS netapitest_user(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + const char *username, *username2; + uint8_t *buffer = NULL; + uint32_t levels[] = { 0, 1, 2, 3, 4, 10, 11, 20, 23 }; + uint32_t enum_levels[] = { 0, 1, 2, 3, 4, 10, 11, 20, 23 }; + uint32_t getgr_levels[] = { 0, 1 }; + int i; + + struct USER_INFO_1007 u1007; + uint32_t parm_err = 0; + + printf("NetUser tests\n"); + + username = "torture_test_user"; + username2 = "torture_test_user2"; + + /* cleanup */ + NetUserDel(hostname, username); + NetUserDel(hostname, username2); + + /* add a user */ + + status = test_netuseradd(hostname, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserAdd"); + goto out; + } + + /* enum the new user */ + + for (i=0; i<ARRAY_SIZE(enum_levels); i++) { + + status = test_netuserenum(hostname, enum_levels[i], username); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserEnum"); + goto out; + } + } + + /* basic queries */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + printf("testing NetUserGetInfo level %d\n", levels[i]); + + status = NetUserGetInfo(hostname, username, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetUserGetInfo"); + goto out; + } + } + + /* testing getgroups */ + + for (i=0; i<ARRAY_SIZE(getgr_levels); i++) { + + status = test_netusergetgroups(hostname, getgr_levels[i], username, NULL); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserGetGroups"); + goto out; + } + } + + /* modify description */ + + printf("testing NetUserSetInfo level %d\n", 1007); + + u1007.usri1007_comment = "NetApi modified user"; + + status = NetUserSetInfo(hostname, username, 1007, (uint8_t *)&u1007, &parm_err); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserSetInfo"); + goto out; + } + + /* query info */ + + for (i=0; i<ARRAY_SIZE(levels); i++) { + status = NetUserGetInfo(hostname, username, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetUserGetInfo"); + goto out; + } + } + + /* delete */ + + printf("testing NetUserDel\n"); + + status = NetUserDel(hostname, username); + if (status) { + NETAPI_STATUS(ctx, status, "NetUserDel"); + goto out; + } + + /* should not exist anymore */ + + status = NetUserGetInfo(hostname, username, 0, &buffer); + if (status == 0) { + NETAPI_STATUS(ctx, status, "NetUserGetInfo"); + goto out; + } + + status = test_netusermodals(ctx, hostname); + if (status) { + goto out; + } + + status = 0; + + printf("NetUser tests succeeded\n"); + out: + /* cleanup */ + NetUserDel(hostname, username); + NetUserDel(hostname, username2); + + if (status != 0) { + printf("NetUser testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/netwksta.c b/source3/lib/netapi/tests/netwksta.c new file mode 100644 index 0000000..2c3f57f --- /dev/null +++ b/source3/lib/netapi/tests/netwksta.c @@ -0,0 +1,62 @@ +/* + * Unix SMB/CIFS implementation. + * NetWorkstation testsuite + * Copyright (C) Guenther Deschner 2008 + * Copyright (C) Hans Leidekker 2013 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +#include "common.h" + +NET_API_STATUS netapitest_wksta(struct libnetapi_ctx *ctx, + const char *hostname) +{ + NET_API_STATUS status = 0; + uint32_t levels[] = { 100, 101, 102 }; + int i; + + printf("NetWorkstation tests\n"); + + /* basic queries */ + for (i=0; i<ARRAY_SIZE(levels); i++) { + uint8_t *buffer = NULL; + printf("testing NetWkstaGetInfo level %d\n", levels[i]); + + status = NetWkstaGetInfo(hostname, levels[i], &buffer); + if (status && status != 124) { + NETAPI_STATUS(ctx, status, "NetWkstaGetInfo"); + goto out; + } + } + + status = 0; + + printf("NetWorkstation tests succeeded\n"); + out: + if (status != 0) { + printf("NetWorkstation testsuite failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } + + return status; +} diff --git a/source3/lib/netapi/tests/wscript_build b/source3/lib/netapi/tests/wscript_build new file mode 100644 index 0000000..5e37297 --- /dev/null +++ b/source3/lib/netapi/tests/wscript_build @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +bld.SAMBA_BINARY('netapitest', + source='netapitest.c netlocalgroup.c netuser.c netgroup.c netdisplay.c netshare.c netfile.c netserver.c netwksta.c common.c', + deps='netapi popt', + install=False) diff --git a/source3/lib/netapi/user.c b/source3/lib/netapi/user.c new file mode 100644 index 0000000..a7f4c9d --- /dev/null +++ b/source3/lib/netapi/user.c @@ -0,0 +1,3638 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi User Support + * Copyright (C) Guenther Deschner 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "rpc_client/init_samr.h" +#include "../libds/common/flags.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" +#include "../libds/common/flag_mapping.h" +#include "rpc_client/cli_pipe.h" + +/**************************************************************** +****************************************************************/ + +static void convert_USER_INFO_X_to_samr_user_info21(struct USER_INFO_X *infoX, + struct samr_UserInfo21 *info21) +{ + uint32_t fields_present = 0; + struct samr_LogonHours zero_logon_hours; + struct lsa_BinaryString zero_parameters; + NTTIME password_age; + + ZERO_STRUCTP(info21); + ZERO_STRUCT(zero_logon_hours); + ZERO_STRUCT(zero_parameters); + + if (infoX->usriX_flags) { + fields_present |= SAMR_FIELD_ACCT_FLAGS; + } + if (infoX->usriX_name) { + fields_present |= SAMR_FIELD_ACCOUNT_NAME; + } + if (infoX->usriX_password) { + fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT; + } + if (infoX->usriX_flags) { + fields_present |= SAMR_FIELD_ACCT_FLAGS; + } + if (infoX->usriX_home_dir) { + fields_present |= SAMR_FIELD_HOME_DIRECTORY; + } + if (infoX->usriX_script_path) { + fields_present |= SAMR_FIELD_LOGON_SCRIPT; + } + if (infoX->usriX_comment) { + fields_present |= SAMR_FIELD_DESCRIPTION; + } + if (infoX->usriX_password_age) { + fields_present |= SAMR_FIELD_EXPIRED_FLAG; + } + if (infoX->usriX_full_name) { + fields_present |= SAMR_FIELD_FULL_NAME; + } + if (infoX->usriX_usr_comment) { + fields_present |= SAMR_FIELD_COMMENT; + } + if (infoX->usriX_profile) { + fields_present |= SAMR_FIELD_PROFILE_PATH; + } + if (infoX->usriX_home_dir_drive) { + fields_present |= SAMR_FIELD_HOME_DRIVE; + } + if (infoX->usriX_primary_group_id) { + fields_present |= SAMR_FIELD_PRIMARY_GID; + } + if (infoX->usriX_country_code) { + fields_present |= SAMR_FIELD_COUNTRY_CODE; + } + if (infoX->usriX_workstations) { + fields_present |= SAMR_FIELD_WORKSTATIONS; + } + + unix_to_nt_time_abs(&password_age, infoX->usriX_password_age); + + /* TODO: infoX->usriX_priv */ + + info21->last_logon = 0; + info21->last_logoff = 0; + info21->last_password_change = 0; + info21->acct_expiry = 0; + info21->allow_password_change = 0; + info21->force_password_change = 0; + info21->account_name.string = infoX->usriX_name; + info21->full_name.string = infoX->usriX_full_name; + info21->home_directory.string = infoX->usriX_home_dir; + info21->home_drive.string = infoX->usriX_home_dir_drive; + info21->logon_script.string = infoX->usriX_script_path; + info21->profile_path.string = infoX->usriX_profile; + info21->description.string = infoX->usriX_comment; + info21->workstations.string = infoX->usriX_workstations; + info21->comment.string = infoX->usriX_usr_comment; + info21->parameters = zero_parameters; + info21->lm_owf_password = zero_parameters; + info21->nt_owf_password = zero_parameters; + info21->private_data.string = NULL; + info21->buf_count = 0; + info21->buffer = NULL; + info21->rid = infoX->usriX_user_id; + info21->primary_gid = infoX->usriX_primary_group_id; + info21->acct_flags = infoX->usriX_flags; + info21->fields_present = fields_present; + info21->logon_hours = zero_logon_hours; + info21->bad_password_count = infoX->usriX_bad_pw_count; + info21->logon_count = infoX->usriX_num_logons; + info21->country_code = infoX->usriX_country_code; + info21->code_page = infoX->usriX_code_page; + info21->lm_password_set = 0; + info21->nt_password_set = 0; + info21->password_expired = infoX->usriX_password_expired; + info21->private_data_sensitive = 0; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS construct_USER_INFO_X(uint32_t level, + uint8_t *buffer, + struct USER_INFO_X *uX) +{ + struct USER_INFO_0 *u0 = NULL; + struct USER_INFO_1 *u1 = NULL; + struct USER_INFO_2 *u2 = NULL; + struct USER_INFO_3 *u3 = NULL; + struct USER_INFO_1003 *u1003 = NULL; + struct USER_INFO_1006 *u1006 = NULL; + struct USER_INFO_1007 *u1007 = NULL; + struct USER_INFO_1009 *u1009 = NULL; + struct USER_INFO_1011 *u1011 = NULL; + struct USER_INFO_1012 *u1012 = NULL; + struct USER_INFO_1014 *u1014 = NULL; + struct USER_INFO_1024 *u1024 = NULL; + struct USER_INFO_1051 *u1051 = NULL; + struct USER_INFO_1052 *u1052 = NULL; + struct USER_INFO_1053 *u1053 = NULL; + + if (!buffer || !uX) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCTP(uX); + + switch (level) { + case 0: + u0 = (struct USER_INFO_0 *)buffer; + uX->usriX_name = u0->usri0_name; + break; + case 1: + u1 = (struct USER_INFO_1 *)buffer; + uX->usriX_name = u1->usri1_name; + uX->usriX_password = u1->usri1_password; + uX->usriX_password_age = u1->usri1_password_age; + uX->usriX_priv = u1->usri1_priv; + uX->usriX_home_dir = u1->usri1_home_dir; + uX->usriX_comment = u1->usri1_comment; + uX->usriX_flags = u1->usri1_flags; + uX->usriX_script_path = u1->usri1_script_path; + break; + case 2: + u2 = (struct USER_INFO_2 *)buffer; + uX->usriX_name = u2->usri2_name; + uX->usriX_password = u2->usri2_password; + uX->usriX_password_age = u2->usri2_password_age; + uX->usriX_priv = u2->usri2_priv; + uX->usriX_home_dir = u2->usri2_home_dir; + uX->usriX_comment = u2->usri2_comment; + uX->usriX_flags = u2->usri2_flags; + uX->usriX_script_path = u2->usri2_script_path; + uX->usriX_auth_flags = u2->usri2_auth_flags; + uX->usriX_full_name = u2->usri2_full_name; + uX->usriX_usr_comment = u2->usri2_usr_comment; + uX->usriX_parms = u2->usri2_parms; + uX->usriX_workstations = u2->usri2_workstations; + uX->usriX_last_logon = u2->usri2_last_logon; + uX->usriX_last_logoff = u2->usri2_last_logoff; + uX->usriX_acct_expires = u2->usri2_acct_expires; + uX->usriX_max_storage = u2->usri2_max_storage; + uX->usriX_units_per_week= u2->usri2_units_per_week; + uX->usriX_logon_hours = u2->usri2_logon_hours; + uX->usriX_bad_pw_count = u2->usri2_bad_pw_count; + uX->usriX_num_logons = u2->usri2_num_logons; + uX->usriX_logon_server = u2->usri2_logon_server; + uX->usriX_country_code = u2->usri2_country_code; + uX->usriX_code_page = u2->usri2_code_page; + break; + case 3: + u3 = (struct USER_INFO_3 *)buffer; + uX->usriX_name = u3->usri3_name; + uX->usriX_password_age = u3->usri3_password_age; + uX->usriX_priv = u3->usri3_priv; + uX->usriX_home_dir = u3->usri3_home_dir; + uX->usriX_comment = u3->usri3_comment; + uX->usriX_flags = u3->usri3_flags; + uX->usriX_script_path = u3->usri3_script_path; + uX->usriX_auth_flags = u3->usri3_auth_flags; + uX->usriX_full_name = u3->usri3_full_name; + uX->usriX_usr_comment = u3->usri3_usr_comment; + uX->usriX_parms = u3->usri3_parms; + uX->usriX_workstations = u3->usri3_workstations; + uX->usriX_last_logon = u3->usri3_last_logon; + uX->usriX_last_logoff = u3->usri3_last_logoff; + uX->usriX_acct_expires = u3->usri3_acct_expires; + uX->usriX_max_storage = u3->usri3_max_storage; + uX->usriX_units_per_week= u3->usri3_units_per_week; + uX->usriX_logon_hours = u3->usri3_logon_hours; + uX->usriX_bad_pw_count = u3->usri3_bad_pw_count; + uX->usriX_num_logons = u3->usri3_num_logons; + uX->usriX_logon_server = u3->usri3_logon_server; + uX->usriX_country_code = u3->usri3_country_code; + uX->usriX_code_page = u3->usri3_code_page; + uX->usriX_user_id = u3->usri3_user_id; + uX->usriX_primary_group_id = u3->usri3_primary_group_id; + uX->usriX_profile = u3->usri3_profile; + uX->usriX_home_dir_drive = u3->usri3_home_dir_drive; + uX->usriX_password_expired = u3->usri3_password_expired; + break; + case 1003: + u1003 = (struct USER_INFO_1003 *)buffer; + uX->usriX_password = u1003->usri1003_password; + break; + case 1006: + u1006 = (struct USER_INFO_1006 *)buffer; + uX->usriX_home_dir = u1006->usri1006_home_dir; + break; + case 1007: + u1007 = (struct USER_INFO_1007 *)buffer; + uX->usriX_comment = u1007->usri1007_comment; + break; + case 1009: + u1009 = (struct USER_INFO_1009 *)buffer; + uX->usriX_script_path = u1009->usri1009_script_path; + break; + case 1011: + u1011 = (struct USER_INFO_1011 *)buffer; + uX->usriX_full_name = u1011->usri1011_full_name; + break; + case 1012: + u1012 = (struct USER_INFO_1012 *)buffer; + uX->usriX_usr_comment = u1012->usri1012_usr_comment; + break; + case 1014: + u1014 = (struct USER_INFO_1014 *)buffer; + uX->usriX_workstations = u1014->usri1014_workstations; + break; + case 1024: + u1024 = (struct USER_INFO_1024 *)buffer; + uX->usriX_country_code = u1024->usri1024_country_code; + break; + case 1051: + u1051 = (struct USER_INFO_1051 *)buffer; + uX->usriX_primary_group_id = u1051->usri1051_primary_group_id; + break; + case 1052: + u1052 = (struct USER_INFO_1052 *)buffer; + uX->usriX_profile = u1052->usri1052_profile; + break; + case 1053: + u1053 = (struct USER_INFO_1053 *)buffer; + uX->usriX_home_dir_drive = u1053->usri1053_home_dir_drive; + break; + case 4: + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_user_info_USER_INFO_X(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + DATA_BLOB *session_key, + struct policy_handle *user_handle, + struct USER_INFO_X *uX) +{ + union samr_UserInfo user_info; + struct samr_UserInfo21 info21; + NTSTATUS status, result; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + if (!uX) { + return NT_STATUS_INVALID_PARAMETER; + } + + convert_USER_INFO_X_to_samr_user_info21(uX, &info21); + + ZERO_STRUCT(user_info); + + if (uX->usriX_password) { + + user_info.info25.info = info21; + + status = init_samr_CryptPasswordEx(uX->usriX_password, + session_key, + &user_info.info25.password); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dcerpc_samr_SetUserInfo2(b, mem_ctx, + user_handle, + 25, + &user_info, + &result); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) { + + user_info.info23.info = info21; + + status = init_samr_CryptPassword(uX->usriX_password, + session_key, + &user_info.info23.password); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dcerpc_samr_SetUserInfo2(b, mem_ctx, + user_handle, + 23, + &user_info, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } else { + + user_info.info21 = info21; + + status = dcerpc_samr_SetUserInfo(b, mem_ctx, + user_handle, + 21, + &user_info, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return result; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserAdd_r(struct libnetapi_ctx *ctx, + struct NetUserAdd *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, domain_handle, user_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + union samr_UserInfo *user_info = NULL; + struct samr_PwInfo pw_info; + uint32_t access_granted = 0; + uint32_t rid = 0; + struct USER_INFO_X uX; + struct dcerpc_binding_handle *b = NULL; + DATA_BLOB session_key; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(user_handle); + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 1: + break; + case 2: + case 3: + case 4: + default: + werr = WERR_NOT_SUPPORTED; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + status = construct_USER_INFO_X(r->in.level, r->in.buffer, &uX); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 | + SAMR_DOMAIN_ACCESS_CREATE_USER | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, uX.usriX_name); + + status = dcerpc_samr_CreateUser2(b, talloc_tos(), + &domain_handle, + &lsa_account_name, + ACB_NORMAL, + SEC_STD_WRITE_DAC | + SEC_STD_DELETE | + SAMR_USER_ACCESS_SET_PASSWORD | + SAMR_USER_ACCESS_SET_ATTRIBUTES | + SAMR_USER_ACCESS_GET_ATTRIBUTES, + &user_handle, + &access_granted, + &rid, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_QueryUserInfo(b, talloc_tos(), + &user_handle, + 16, + &user_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!(user_info->info16.acct_flags & ACB_NORMAL)) { + werr = WERR_INVALID_PARAMETER; + goto done; + } + + status = dcerpc_samr_GetUserPwInfo(b, talloc_tos(), + &user_handle, + &pw_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = cli_get_session_key(talloc_tos(), pipe_cli, &session_key); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + uX.usriX_flags |= ACB_NORMAL; + + status = set_user_info_USER_INFO_X(ctx, pipe_cli, + &session_key, + &user_handle, + &uX); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto failed; + } + + werr = WERR_OK; + goto done; + + failed: + dcerpc_samr_DeleteUser(b, talloc_tos(), + &user_handle, + &result); + + done: + if (is_valid_policy_hnd(&user_handle) && b) { + dcerpc_samr_Close(b, talloc_tos(), &user_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserAdd_l(struct libnetapi_ctx *ctx, + struct NetUserAdd *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserAdd); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserDel_r(struct libnetapi_ctx *ctx, + struct NetUserDel *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + struct policy_handle connect_handle, builtin_handle, domain_handle, user_handle; + struct lsa_String lsa_account_name; + struct samr_Ids user_rids, name_types; + struct dom_sid2 *domain_sid = NULL; + struct dom_sid2 user_sid; + struct dcerpc_binding_handle *b = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(user_handle); + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_samr_OpenDomain(b, talloc_tos(), + &connect_handle, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + discard_const_p(struct dom_sid, &global_sid_Builtin), + &builtin_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenUser(b, talloc_tos(), + &domain_handle, + SEC_STD_DELETE, + user_rids.ids[0], + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + sid_compose(&user_sid, domain_sid, user_rids.ids[0]); + + status = dcerpc_samr_RemoveMemberFromForeignDomain(b, talloc_tos(), + &builtin_handle, + &user_sid, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_DeleteUser(b, talloc_tos(), + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&user_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &user_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserDel_l(struct libnetapi_ctx *ctx, + struct NetUserDel *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserDel); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS libnetapi_samr_lookup_user(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct policy_handle *builtin_handle, + const char *user_name, + const struct dom_sid *domain_sid, + uint32_t rid, + uint32_t level, + struct samr_UserInfo21 **info21, + struct sec_desc_buf **sec_desc, + uint32_t *auth_flag_p) +{ + NTSTATUS status, result; + + struct policy_handle user_handle; + union samr_UserInfo *user_info = NULL; + struct samr_RidWithAttributeArray *rid_array = NULL; + uint32_t access_mask = SEC_STD_READ_CONTROL | + SAMR_USER_ACCESS_GET_ATTRIBUTES | + SAMR_USER_ACCESS_GET_NAME_ETC; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + ZERO_STRUCT(user_handle); + + switch (level) { + case 0: + break; + case 1: + access_mask |= SAMR_USER_ACCESS_GET_LOGONINFO | + SAMR_USER_ACCESS_GET_GROUPS; + break; + case 2: + case 3: + case 4: + case 11: + access_mask |= SAMR_USER_ACCESS_GET_LOGONINFO | + SAMR_USER_ACCESS_GET_GROUPS | + SAMR_USER_ACCESS_GET_LOCALE; + break; + case 10: + case 20: + case 23: + break; + default: + return NT_STATUS_INVALID_LEVEL; + } + + if (level == 0) { + return NT_STATUS_OK; + } + + status = dcerpc_samr_OpenUser(b, mem_ctx, + domain_handle, + access_mask, + rid, + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + status = dcerpc_samr_QueryUserInfo(b, mem_ctx, + &user_handle, + 21, + &user_info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + status = dcerpc_samr_QuerySecurity(b, mem_ctx, + &user_handle, + SECINFO_DACL, + sec_desc, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + if (access_mask & SAMR_USER_ACCESS_GET_GROUPS) { + + struct lsa_SidArray sid_array; + struct samr_Ids alias_rids; + int i; + uint32_t auth_flag = 0; + struct dom_sid sid; + + status = dcerpc_samr_GetGroupsForUser(b, mem_ctx, + &user_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + sid_array.num_sids = rid_array->count + 1; + sid_array.sids = talloc_array(mem_ctx, struct lsa_SidPtr, + sid_array.num_sids); + NT_STATUS_HAVE_NO_MEMORY(sid_array.sids); + + for (i=0; i<rid_array->count; i++) { + sid_compose(&sid, domain_sid, rid_array->rids[i].rid); + sid_array.sids[i].sid = dom_sid_dup(mem_ctx, &sid); + NT_STATUS_HAVE_NO_MEMORY(sid_array.sids[i].sid); + } + + sid_compose(&sid, domain_sid, rid); + sid_array.sids[i].sid = dom_sid_dup(mem_ctx, &sid); + NT_STATUS_HAVE_NO_MEMORY(sid_array.sids[i].sid); + + status = dcerpc_samr_GetAliasMembership(b, mem_ctx, + builtin_handle, + &sid_array, + &alias_rids, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + for (i=0; i<alias_rids.count; i++) { + switch (alias_rids.ids[i]) { + case 550: /* Print Operators */ + auth_flag |= AF_OP_PRINT; + break; + case 549: /* Server Operators */ + auth_flag |= AF_OP_SERVER; + break; + case 548: /* Account Operators */ + auth_flag |= AF_OP_ACCOUNTS; + break; + default: + break; + } + } + + if (auth_flag_p) { + *auth_flag_p = auth_flag; + } + } + + *info21 = &user_info->info21; + + done: + if (is_valid_policy_hnd(&user_handle)) { + dcerpc_samr_Close(b, mem_ctx, &user_handle, &result); + } + + return status; +} + +/**************************************************************** +****************************************************************/ + +static uint32_t samr_rid_to_priv_level(uint32_t rid) +{ + switch (rid) { + case DOMAIN_RID_ADMINISTRATOR: + return USER_PRIV_ADMIN; + case DOMAIN_RID_GUEST: + return USER_PRIV_GUEST; + default: + return USER_PRIV_USER; + } +} + +/**************************************************************** +****************************************************************/ + +static uint32_t samr_acb_flags_to_netapi_flags(uint32_t acb) +{ + uint32_t fl = UF_SCRIPT; /* god knows why */ + + fl |= ds_acb2uf(acb); + + return fl; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_1(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + struct USER_INFO_1 *i) +{ + ZERO_STRUCTP(i); + i->usri1_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri1_name); + i->usri1_password = NULL; + i->usri1_password_age = time(NULL) - nt_time_to_unix(i21->last_password_change); + i->usri1_priv = samr_rid_to_priv_level(i21->rid); + i->usri1_home_dir = talloc_strdup(mem_ctx, i21->home_directory.string); + i->usri1_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri1_flags = samr_acb_flags_to_netapi_flags(i21->acct_flags); + i->usri1_script_path = talloc_strdup(mem_ctx, i21->logon_script.string); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_2(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + uint32_t auth_flag, + struct USER_INFO_2 *i) +{ + ZERO_STRUCTP(i); + + i->usri2_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri2_name); + i->usri2_password = NULL; + i->usri2_password_age = time(NULL) - nt_time_to_unix(i21->last_password_change); + i->usri2_priv = samr_rid_to_priv_level(i21->rid); + i->usri2_home_dir = talloc_strdup(mem_ctx, i21->home_directory.string); + i->usri2_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri2_flags = samr_acb_flags_to_netapi_flags(i21->acct_flags); + i->usri2_script_path = talloc_strdup(mem_ctx, i21->logon_script.string); + i->usri2_auth_flags = auth_flag; + i->usri2_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri2_usr_comment = talloc_strdup(mem_ctx, i21->comment.string); + i->usri2_parms = talloc_strndup(mem_ctx, (const char *)i21->parameters.array, i21->parameters.size/2); + i->usri2_workstations = talloc_strdup(mem_ctx, i21->workstations.string); + i->usri2_last_logon = nt_time_to_unix(i21->last_logon); + i->usri2_last_logoff = nt_time_to_unix(i21->last_logoff); + i->usri2_acct_expires = nt_time_to_unix(i21->acct_expiry); + i->usri2_max_storage = USER_MAXSTORAGE_UNLIMITED; /* FIXME */ + i->usri2_units_per_week = i21->logon_hours.units_per_week; + i->usri2_logon_hours = (uint8_t *)talloc_memdup(mem_ctx, i21->logon_hours.bits, 21); + i->usri2_bad_pw_count = i21->bad_password_count; + i->usri2_num_logons = i21->logon_count; + i->usri2_logon_server = talloc_strdup(mem_ctx, "\\\\*"); + i->usri2_country_code = i21->country_code; + i->usri2_code_page = i21->code_page; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_3(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + uint32_t auth_flag, + struct USER_INFO_3 *i) +{ + ZERO_STRUCTP(i); + + i->usri3_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri3_name); + i->usri3_password_age = time(NULL) - nt_time_to_unix(i21->last_password_change); + i->usri3_priv = samr_rid_to_priv_level(i21->rid); + i->usri3_home_dir = talloc_strdup(mem_ctx, i21->home_directory.string); + i->usri3_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri3_flags = samr_acb_flags_to_netapi_flags(i21->acct_flags); + i->usri3_script_path = talloc_strdup(mem_ctx, i21->logon_script.string); + i->usri3_auth_flags = auth_flag; + i->usri3_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri3_usr_comment = talloc_strdup(mem_ctx, i21->comment.string); + i->usri3_parms = talloc_strndup(mem_ctx, (const char *)i21->parameters.array, i21->parameters.size/2); + i->usri3_workstations = talloc_strdup(mem_ctx, i21->workstations.string); + i->usri3_last_logon = nt_time_to_unix(i21->last_logon); + i->usri3_last_logoff = nt_time_to_unix(i21->last_logoff); + i->usri3_acct_expires = nt_time_to_unix(i21->acct_expiry); + i->usri3_max_storage = USER_MAXSTORAGE_UNLIMITED; /* FIXME */ + i->usri3_units_per_week = i21->logon_hours.units_per_week; + i->usri3_logon_hours = (uint8_t *)talloc_memdup(mem_ctx, i21->logon_hours.bits, 21); + i->usri3_bad_pw_count = i21->bad_password_count; + i->usri3_num_logons = i21->logon_count; + i->usri3_logon_server = talloc_strdup(mem_ctx, "\\\\*"); + i->usri3_country_code = i21->country_code; + i->usri3_code_page = i21->code_page; + i->usri3_user_id = i21->rid; + i->usri3_primary_group_id = i21->primary_gid; + i->usri3_profile = talloc_strdup(mem_ctx, i21->profile_path.string); + i->usri3_home_dir_drive = talloc_strdup(mem_ctx, i21->home_drive.string); + i->usri3_password_expired = i21->password_expired; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_4(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + uint32_t auth_flag, + struct dom_sid *domain_sid, + struct USER_INFO_4 *i) +{ + struct dom_sid sid; + + ZERO_STRUCTP(i); + + i->usri4_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri4_name); + i->usri4_password_age = time(NULL) - nt_time_to_unix(i21->last_password_change); + i->usri4_password = NULL; + i->usri4_priv = samr_rid_to_priv_level(i21->rid); + i->usri4_home_dir = talloc_strdup(mem_ctx, i21->home_directory.string); + i->usri4_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri4_flags = samr_acb_flags_to_netapi_flags(i21->acct_flags); + i->usri4_script_path = talloc_strdup(mem_ctx, i21->logon_script.string); + i->usri4_auth_flags = auth_flag; + i->usri4_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri4_usr_comment = talloc_strdup(mem_ctx, i21->comment.string); + i->usri4_parms = talloc_strndup(mem_ctx, (const char *)i21->parameters.array, i21->parameters.size/2); + i->usri4_workstations = talloc_strdup(mem_ctx, i21->workstations.string); + i->usri4_last_logon = nt_time_to_unix(i21->last_logon); + i->usri4_last_logoff = nt_time_to_unix(i21->last_logoff); + i->usri4_acct_expires = nt_time_to_unix(i21->acct_expiry); + i->usri4_max_storage = USER_MAXSTORAGE_UNLIMITED; /* FIXME */ + i->usri4_units_per_week = i21->logon_hours.units_per_week; + i->usri4_logon_hours = (uint8_t *)talloc_memdup(mem_ctx, i21->logon_hours.bits, 21); + i->usri4_bad_pw_count = i21->bad_password_count; + i->usri4_num_logons = i21->logon_count; + i->usri4_logon_server = talloc_strdup(mem_ctx, "\\\\*"); + i->usri4_country_code = i21->country_code; + i->usri4_code_page = i21->code_page; + if (!sid_compose(&sid, domain_sid, i21->rid)) { + return NT_STATUS_NO_MEMORY; + } + i->usri4_user_sid = (struct domsid *)dom_sid_dup(mem_ctx, &sid); + i->usri4_primary_group_id = i21->primary_gid; + i->usri4_profile = talloc_strdup(mem_ctx, i21->profile_path.string); + i->usri4_home_dir_drive = talloc_strdup(mem_ctx, i21->home_drive.string); + i->usri4_password_expired = i21->password_expired; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_10(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + struct USER_INFO_10 *i) +{ + ZERO_STRUCTP(i); + + i->usri10_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri10_name); + i->usri10_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri10_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri10_usr_comment = talloc_strdup(mem_ctx, i21->comment.string); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_11(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + uint32_t auth_flag, + struct USER_INFO_11 *i) +{ + ZERO_STRUCTP(i); + + i->usri11_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri11_name); + i->usri11_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri11_usr_comment = talloc_strdup(mem_ctx, i21->comment.string); + i->usri11_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri11_priv = samr_rid_to_priv_level(i21->rid); + i->usri11_auth_flags = auth_flag; + i->usri11_password_age = time(NULL) - nt_time_to_unix(i21->last_password_change); + i->usri11_home_dir = talloc_strdup(mem_ctx, i21->home_directory.string); + i->usri11_parms = talloc_strndup(mem_ctx, (const char *)i21->parameters.array, i21->parameters.size/2); + i->usri11_last_logon = nt_time_to_unix(i21->last_logon); + i->usri11_last_logoff = nt_time_to_unix(i21->last_logoff); + i->usri11_bad_pw_count = i21->bad_password_count; + i->usri11_num_logons = i21->logon_count; + i->usri11_logon_server = talloc_strdup(mem_ctx, "\\\\*"); + i->usri11_country_code = i21->country_code; + i->usri11_workstations = talloc_strdup(mem_ctx, i21->workstations.string); + i->usri11_max_storage = USER_MAXSTORAGE_UNLIMITED; /* FIXME */ + i->usri11_units_per_week = i21->logon_hours.units_per_week; + i->usri11_logon_hours = (uint8_t *)talloc_memdup(mem_ctx, i21->logon_hours.bits, 21); + i->usri11_code_page = i21->code_page; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_20(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + struct USER_INFO_20 *i) +{ + ZERO_STRUCTP(i); + + i->usri20_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri20_name); + i->usri20_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri20_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri20_flags = samr_acb_flags_to_netapi_flags(i21->acct_flags); + i->usri20_user_id = i21->rid; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS info21_to_USER_INFO_23(TALLOC_CTX *mem_ctx, + const struct samr_UserInfo21 *i21, + struct dom_sid *domain_sid, + struct USER_INFO_23 *i) +{ + struct dom_sid sid; + + ZERO_STRUCTP(i); + + i->usri23_name = talloc_strdup(mem_ctx, i21->account_name.string); + NT_STATUS_HAVE_NO_MEMORY(i->usri23_name); + i->usri23_comment = talloc_strdup(mem_ctx, i21->description.string); + i->usri23_full_name = talloc_strdup(mem_ctx, i21->full_name.string); + i->usri23_flags = samr_acb_flags_to_netapi_flags(i21->acct_flags); + if (!sid_compose(&sid, domain_sid, i21->rid)) { + return NT_STATUS_NO_MEMORY; + } + i->usri23_user_sid = (struct domsid *)dom_sid_dup(mem_ctx, &sid); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS libnetapi_samr_lookup_user_map_USER_INFO(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct dom_sid *domain_sid, + struct policy_handle *domain_handle, + struct policy_handle *builtin_handle, + const char *user_name, + uint32_t rid, + uint32_t level, + uint8_t **buffer, + uint32_t *num_entries) +{ + NTSTATUS status; + + struct samr_UserInfo21 *info21 = NULL; + struct sec_desc_buf *sec_desc = NULL; + uint32_t auth_flag = 0; + + struct USER_INFO_0 info0; + struct USER_INFO_1 info1; + struct USER_INFO_2 info2; + struct USER_INFO_3 info3; + struct USER_INFO_4 info4; + struct USER_INFO_10 info10; + struct USER_INFO_11 info11; + struct USER_INFO_20 info20; + struct USER_INFO_23 info23; + + switch (level) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 10: + case 11: + case 20: + case 23: + break; + default: + return NT_STATUS_INVALID_LEVEL; + } + + if (level == 0) { + info0.usri0_name = talloc_strdup(mem_ctx, user_name); + NT_STATUS_HAVE_NO_MEMORY(info0.usri0_name); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_0, info0, + (struct USER_INFO_0 **)buffer, num_entries); + + return NT_STATUS_OK; + } + + status = libnetapi_samr_lookup_user(mem_ctx, pipe_cli, + domain_handle, + builtin_handle, + user_name, + domain_sid, + rid, + level, + &info21, + &sec_desc, + &auth_flag); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + switch (level) { + case 1: + status = info21_to_USER_INFO_1(mem_ctx, info21, &info1); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_1, info1, + (struct USER_INFO_1 **)buffer, num_entries); + + break; + case 2: + status = info21_to_USER_INFO_2(mem_ctx, info21, auth_flag, &info2); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_2, info2, + (struct USER_INFO_2 **)buffer, num_entries); + + break; + case 3: + status = info21_to_USER_INFO_3(mem_ctx, info21, auth_flag, &info3); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_3, info3, + (struct USER_INFO_3 **)buffer, num_entries); + + break; + case 4: + status = info21_to_USER_INFO_4(mem_ctx, info21, auth_flag, domain_sid, &info4); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_4, info4, + (struct USER_INFO_4 **)buffer, num_entries); + + break; + case 10: + status = info21_to_USER_INFO_10(mem_ctx, info21, &info10); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_10, info10, + (struct USER_INFO_10 **)buffer, num_entries); + + break; + case 11: + status = info21_to_USER_INFO_11(mem_ctx, info21, auth_flag, &info11); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_11, info11, + (struct USER_INFO_11 **)buffer, num_entries); + + break; + case 20: + status = info21_to_USER_INFO_20(mem_ctx, info21, &info20); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_20, info20, + (struct USER_INFO_20 **)buffer, num_entries); + + break; + case 23: + status = info21_to_USER_INFO_23(mem_ctx, info21, domain_sid, &info23); + NT_STATUS_NOT_OK_RETURN(status); + + ADD_TO_ARRAY(mem_ctx, struct USER_INFO_23, info23, + (struct USER_INFO_23 **)buffer, num_entries); + break; + default: + return NT_STATUS_INVALID_LEVEL; + } + + done: + return status; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserEnum_r(struct libnetapi_ctx *ctx, + struct NetUserEnum *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle; + struct dom_sid2 *domain_sid = NULL; + struct policy_handle domain_handle, builtin_handle; + struct samr_SamArray *sam = NULL; + uint32_t filter = ACB_NORMAL; + int i; + uint32_t entries_read = 0; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + struct dcerpc_binding_handle *b = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(builtin_handle); + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + *r->out.buffer = NULL; + *r->out.entries_read = 0; + + switch (r->in.level) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 10: + case 11: + case 20: + case 23: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT | + SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + switch (r->in.filter) { + case FILTER_NORMAL_ACCOUNT: + filter = ACB_NORMAL; + break; + case FILTER_TEMP_DUPLICATE_ACCOUNT: + filter = ACB_TEMPDUP; + break; + case FILTER_INTERDOMAIN_TRUST_ACCOUNT: + filter = ACB_DOMTRUST; + break; + case FILTER_WORKSTATION_TRUST_ACCOUNT: + filter = ACB_WSTRUST; + break; + case FILTER_SERVER_TRUST_ACCOUNT: + filter = ACB_SVRTRUST; + break; + default: + break; + } + + status = dcerpc_samr_EnumDomainUsers(b, + ctx, + &domain_handle, + r->in.resume_handle, + filter, + &sam, + r->in.prefmaxlen, + &entries_read, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + werr = ntstatus_to_werror(result); + if (NT_STATUS_IS_ERR(result)) { + goto done; + } + + for (i=0; i < sam->count; i++) { + + status = libnetapi_samr_lookup_user_map_USER_INFO(ctx, pipe_cli, + domain_sid, + &domain_handle, + &builtin_handle, + sam->entries[i].name.string, + sam->entries[i].idx, + r->in.level, + r->out.buffer, + r->out.entries_read); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + done: + /* if last query */ + if (NT_STATUS_IS_OK(result) || + NT_STATUS_IS_ERR(result)) { + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserEnum_l(struct libnetapi_ctx *ctx, + struct NetUserEnum *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserEnum); +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_dispinfo_to_NET_DISPLAY_USER(TALLOC_CTX *mem_ctx, + struct samr_DispInfoGeneral *info, + uint32_t *entries_read, + void **buffer) +{ + struct NET_DISPLAY_USER *user = NULL; + int i; + + user = talloc_zero_array(mem_ctx, + struct NET_DISPLAY_USER, + info->count); + W_ERROR_HAVE_NO_MEMORY(user); + + for (i = 0; i < info->count; i++) { + user[i].usri1_name = talloc_strdup(mem_ctx, + info->entries[i].account_name.string); + user[i].usri1_comment = talloc_strdup(mem_ctx, + info->entries[i].description.string); + user[i].usri1_flags = + info->entries[i].acct_flags; + user[i].usri1_full_name = talloc_strdup(mem_ctx, + info->entries[i].full_name.string); + user[i].usri1_user_id = + info->entries[i].rid; + user[i].usri1_next_index = + info->entries[i].idx; + + if (!user[i].usri1_name) { + return WERR_NOT_ENOUGH_MEMORY; + } + } + + *buffer = talloc_memdup(mem_ctx, user, + sizeof(struct NET_DISPLAY_USER) * info->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + *entries_read = info->count; + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_dispinfo_to_NET_DISPLAY_MACHINE(TALLOC_CTX *mem_ctx, + struct samr_DispInfoFull *info, + uint32_t *entries_read, + void **buffer) +{ + struct NET_DISPLAY_MACHINE *machine = NULL; + int i; + + machine = talloc_zero_array(mem_ctx, + struct NET_DISPLAY_MACHINE, + info->count); + W_ERROR_HAVE_NO_MEMORY(machine); + + for (i = 0; i < info->count; i++) { + machine[i].usri2_name = talloc_strdup(mem_ctx, + info->entries[i].account_name.string); + machine[i].usri2_comment = talloc_strdup(mem_ctx, + info->entries[i].description.string); + machine[i].usri2_flags = + info->entries[i].acct_flags; + machine[i].usri2_user_id = + info->entries[i].rid; + machine[i].usri2_next_index = + info->entries[i].idx; + + if (!machine[i].usri2_name) { + return WERR_NOT_ENOUGH_MEMORY; + } + } + + *buffer = talloc_memdup(mem_ctx, machine, + sizeof(struct NET_DISPLAY_MACHINE) * info->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + *entries_read = info->count; + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_dispinfo_to_NET_DISPLAY_GROUP(TALLOC_CTX *mem_ctx, + struct samr_DispInfoFullGroups *info, + uint32_t *entries_read, + void **buffer) +{ + struct NET_DISPLAY_GROUP *group = NULL; + int i; + + group = talloc_zero_array(mem_ctx, + struct NET_DISPLAY_GROUP, + info->count); + W_ERROR_HAVE_NO_MEMORY(group); + + for (i = 0; i < info->count; i++) { + group[i].grpi3_name = talloc_strdup(mem_ctx, + info->entries[i].account_name.string); + group[i].grpi3_comment = talloc_strdup(mem_ctx, + info->entries[i].description.string); + group[i].grpi3_group_id = + info->entries[i].rid; + group[i].grpi3_attributes = + info->entries[i].acct_flags; + group[i].grpi3_next_index = + info->entries[i].idx; + + if (!group[i].grpi3_name) { + return WERR_NOT_ENOUGH_MEMORY; + } + } + + *buffer = talloc_memdup(mem_ctx, group, + sizeof(struct NET_DISPLAY_GROUP) * info->count); + W_ERROR_HAVE_NO_MEMORY(*buffer); + + *entries_read = info->count; + + return WERR_OK; + +} + +/**************************************************************** +****************************************************************/ + +static WERROR convert_samr_dispinfo_to_NET_DISPLAY(TALLOC_CTX *mem_ctx, + union samr_DispInfo *info, + uint32_t level, + uint32_t *entries_read, + void **buffer) +{ + switch (level) { + case 1: + return convert_samr_dispinfo_to_NET_DISPLAY_USER(mem_ctx, + &info->info1, + entries_read, + buffer); + case 2: + return convert_samr_dispinfo_to_NET_DISPLAY_MACHINE(mem_ctx, + &info->info2, + entries_read, + buffer); + case 3: + return convert_samr_dispinfo_to_NET_DISPLAY_GROUP(mem_ctx, + &info->info3, + entries_read, + buffer); + default: + break; + } + + return WERR_INVALID_LEVEL; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetQueryDisplayInformation_r(struct libnetapi_ctx *ctx, + struct NetQueryDisplayInformation *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle; + struct dom_sid2 *domain_sid = NULL; + struct policy_handle domain_handle; + union samr_DispInfo info; + struct dcerpc_binding_handle *b = NULL; + + uint32_t total_size = 0; + uint32_t returned_size = 0; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + WERROR werr_tmp; + + *r->out.entries_read = 0; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + + switch (r->in.level) { + case 1: + case 2: + case 3: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_samr_QueryDisplayInfo2(b, + ctx, + &domain_handle, + r->in.level, + r->in.idx, + r->in.entries_requested, + r->in.prefmaxlen, + &total_size, + &returned_size, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + werr = ntstatus_to_werror(result); + if (NT_STATUS_IS_ERR(result)) { + goto done; + } + + werr_tmp = convert_samr_dispinfo_to_NET_DISPLAY(ctx, &info, + r->in.level, + r->out.entries_read, + r->out.buffer); + if (!W_ERROR_IS_OK(werr_tmp)) { + werr = werr_tmp; + } + done: + /* if last query */ + if (NT_STATUS_IS_OK(result) || + NT_STATUS_IS_ERR(result)) { + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + } + + return werr; + +} + +/**************************************************************** +****************************************************************/ + + +WERROR NetQueryDisplayInformation_l(struct libnetapi_ctx *ctx, + struct NetQueryDisplayInformation *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetQueryDisplayInformation); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserChangePassword_r(struct libnetapi_ctx *ctx, + struct NetUserChangePassword *r) +{ + return WERR_NOT_SUPPORTED; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserChangePassword_l(struct libnetapi_ctx *ctx, + struct NetUserChangePassword *r) +{ + return WERR_NOT_SUPPORTED; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx, + struct NetUserGetInfo *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + + struct policy_handle connect_handle, domain_handle, builtin_handle, user_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + struct samr_Ids user_rids, name_types; + uint32_t num_entries = 0; + struct dcerpc_binding_handle *b = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(user_handle); + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 10: + case 11: + case 20: + case 23: + break; + default: + werr = WERR_INVALID_LEVEL; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT | + SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = libnetapi_samr_lookup_user_map_USER_INFO(ctx, pipe_cli, + domain_sid, + &domain_handle, + &builtin_handle, + r->in.user_name, + user_rids.ids[0], + r->in.level, + r->out.buffer, + &num_entries); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + if (is_valid_policy_hnd(&user_handle) && b) { + dcerpc_samr_Close(b, talloc_tos(), &user_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserGetInfo_l(struct libnetapi_ctx *ctx, + struct NetUserGetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetInfo); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx, + struct NetUserSetInfo *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status, result; + WERROR werr; + + struct policy_handle connect_handle, domain_handle, builtin_handle, user_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + struct samr_Ids user_rids, name_types; + uint32_t user_mask = 0; + + struct USER_INFO_X uX; + struct dcerpc_binding_handle *b = NULL; + DATA_BLOB session_key; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(builtin_handle); + ZERO_STRUCT(user_handle); + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES; + break; + case 1003: + user_mask = SAMR_USER_ACCESS_SET_PASSWORD; + break; + case 1006: + case 1007: + case 1009: + case 1011: + case 1014: + case 1052: + case 1053: + user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES; + break; + case 1012: + case 1024: + user_mask = SAMR_USER_ACCESS_SET_LOC_COM; + break; + case 1051: + user_mask = SAMR_USER_ACCESS_SET_ATTRIBUTES | + SAMR_USER_ACCESS_GET_GROUPS; + break; + case 3: + user_mask = SEC_STD_READ_CONTROL | + SEC_STD_WRITE_DAC | + SAMR_USER_ACCESS_GET_GROUPS | + SAMR_USER_ACCESS_SET_PASSWORD | + SAMR_USER_ACCESS_SET_ATTRIBUTES | + SAMR_USER_ACCESS_GET_ATTRIBUTES | + SAMR_USER_ACCESS_SET_LOC_COM; + break; + case 1: + case 2: + case 4: + case 21: + case 22: + case 1005: + case 1008: + case 1010: + case 1017: + case 1020: + werr = WERR_NOT_SUPPORTED; + goto done; + default: + werr = WERR_INVALID_LEVEL; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 | + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT | + SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenUser(b, talloc_tos(), + &domain_handle, + user_mask, + user_rids.ids[0], + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = construct_USER_INFO_X(r->in.level, r->in.buffer, &uX); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = cli_get_session_key(talloc_tos(), pipe_cli, &session_key); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = set_user_info_USER_INFO_X(ctx, pipe_cli, + &session_key, + &user_handle, + &uX); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&user_handle) && b) { + dcerpc_samr_Close(b, talloc_tos(), &user_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_builtin_handle(ctx, &builtin_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserSetInfo_l(struct libnetapi_ctx *ctx, + struct NetUserSetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserSetInfo); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS query_USER_MODALS_INFO_rpc(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct samr_DomInfo1 *info1, + struct samr_DomInfo3 *info3, + struct samr_DomInfo5 *info5, + struct samr_DomInfo6 *info6, + struct samr_DomInfo7 *info7, + struct samr_DomInfo12 *info12) +{ + NTSTATUS status, result; + union samr_DomainInfo *dom_info = NULL; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + if (info1) { + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + domain_handle, + 1, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + *info1 = dom_info->info1; + } + + if (info3) { + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + domain_handle, + 3, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + *info3 = dom_info->info3; + } + + if (info5) { + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + domain_handle, + 5, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + *info5 = dom_info->info5; + } + + if (info6) { + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + domain_handle, + 6, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + *info6 = dom_info->info6; + } + + if (info7) { + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + domain_handle, + 7, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + *info7 = dom_info->info7; + } + + if (info12) { + status = dcerpc_samr_QueryDomainInfo2(b, mem_ctx, + domain_handle, + 12, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + + *info12 = dom_info->info12; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS query_USER_MODALS_INFO_0(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_0 *info0) +{ + NTSTATUS status; + struct samr_DomInfo1 dom_info1; + struct samr_DomInfo3 dom_info3; + + ZERO_STRUCTP(info0); + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info1, + &dom_info3, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + info0->usrmod0_min_passwd_len = + dom_info1.min_password_length; + info0->usrmod0_max_passwd_age = + nt_time_to_unix_abs((NTTIME *)&dom_info1.max_password_age); + info0->usrmod0_min_passwd_age = + nt_time_to_unix_abs((NTTIME *)&dom_info1.min_password_age); + info0->usrmod0_password_hist_len = + dom_info1.password_history_length; + + info0->usrmod0_force_logoff = + nt_time_to_unix_abs(&dom_info3.force_logoff_time); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS query_USER_MODALS_INFO_1(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_1 *info1) +{ + NTSTATUS status; + struct samr_DomInfo6 dom_info6; + struct samr_DomInfo7 dom_info7; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + NULL, + NULL, + &dom_info6, + &dom_info7, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + info1->usrmod1_primary = + talloc_strdup(mem_ctx, dom_info6.primary.string); + + info1->usrmod1_role = dom_info7.role; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS query_USER_MODALS_INFO_2(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + struct USER_MODALS_INFO_2 *info2) +{ + NTSTATUS status; + struct samr_DomInfo5 dom_info5; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + NULL, + &dom_info5, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + info2->usrmod2_domain_name = + talloc_strdup(mem_ctx, dom_info5.domain_name.string); + info2->usrmod2_domain_id = + (struct domsid *)dom_sid_dup(mem_ctx, domain_sid); + + NT_STATUS_HAVE_NO_MEMORY(info2->usrmod2_domain_name); + NT_STATUS_HAVE_NO_MEMORY(info2->usrmod2_domain_id); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS query_USER_MODALS_INFO_3(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_3 *info3) +{ + NTSTATUS status; + struct samr_DomInfo12 dom_info12; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + NULL, + NULL, + NULL, + NULL, + &dom_info12); + NT_STATUS_NOT_OK_RETURN(status); + + info3->usrmod3_lockout_duration = + nt_time_to_unix_abs(&dom_info12.lockout_duration); + info3->usrmod3_lockout_observation_window = + nt_time_to_unix_abs(&dom_info12.lockout_window); + info3->usrmod3_lockout_threshold = + dom_info12.lockout_threshold; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS query_USER_MODALS_INFO_to_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + uint32_t level, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + uint8_t **buffer) +{ + NTSTATUS status; + + struct USER_MODALS_INFO_0 info0; + struct USER_MODALS_INFO_1 info1; + struct USER_MODALS_INFO_2 info2; + struct USER_MODALS_INFO_3 info3; + + if (!buffer) { + return ERROR_INSUFFICIENT_BUFFER; + } + + switch (level) { + case 0: + status = query_USER_MODALS_INFO_0(mem_ctx, + pipe_cli, + domain_handle, + &info0); + NT_STATUS_NOT_OK_RETURN(status); + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info0, + sizeof(info0)); + break; + + case 1: + status = query_USER_MODALS_INFO_1(mem_ctx, + pipe_cli, + domain_handle, + &info1); + NT_STATUS_NOT_OK_RETURN(status); + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info1, + sizeof(info1)); + break; + case 2: + status = query_USER_MODALS_INFO_2(mem_ctx, + pipe_cli, + domain_handle, + domain_sid, + &info2); + NT_STATUS_NOT_OK_RETURN(status); + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info2, + sizeof(info2)); + break; + case 3: + status = query_USER_MODALS_INFO_3(mem_ctx, + pipe_cli, + domain_handle, + &info3); + NT_STATUS_NOT_OK_RETURN(status); + + *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info3, + sizeof(info3)); + break; + default: + break; + } + + NT_STATUS_HAVE_NO_MEMORY(*buffer); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserModalsGet_r(struct libnetapi_ctx *ctx, + struct NetUserModalsGet *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + + struct policy_handle connect_handle, domain_handle; + struct dom_sid2 *domain_sid = NULL; + uint32_t access_mask = SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 | + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2; + break; + case 1: + case 2: + access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2; + break; + case 3: + access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1; + break; + default: + werr = WERR_INVALID_LEVEL; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + access_mask, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + /* 0: 1 + 3 */ + /* 1: 6 + 7 */ + /* 2: 5 */ + /* 3: 12 (DomainInfo2) */ + + status = query_USER_MODALS_INFO_to_buffer(ctx, + pipe_cli, + r->in.level, + &domain_handle, + domain_sid, + r->out.buffer); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserModalsGet_l(struct libnetapi_ctx *ctx, + struct NetUserModalsGet *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserModalsGet); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_rpc(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct samr_DomInfo1 *info1, + struct samr_DomInfo3 *info3, + struct samr_DomInfo12 *info12) +{ + NTSTATUS status, result; + union samr_DomainInfo dom_info; + struct dcerpc_binding_handle *b = pipe_cli->binding_handle; + + if (info1) { + + ZERO_STRUCT(dom_info); + + dom_info.info1 = *info1; + + status = dcerpc_samr_SetDomainInfo(b, mem_ctx, + domain_handle, + 1, + &dom_info, + &result); + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + } + + if (info3) { + + ZERO_STRUCT(dom_info); + + dom_info.info3 = *info3; + + status = dcerpc_samr_SetDomainInfo(b, mem_ctx, + domain_handle, + 3, + &dom_info, + &result); + + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + } + + if (info12) { + + ZERO_STRUCT(dom_info); + + dom_info.info12 = *info12; + + status = dcerpc_samr_SetDomainInfo(b, mem_ctx, + domain_handle, + 12, + &dom_info, + &result); + + NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN(result); + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_0_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_0 *info0) +{ + NTSTATUS status; + struct samr_DomInfo1 dom_info_1; + struct samr_DomInfo3 dom_info_3; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + &dom_info_3, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + dom_info_1.min_password_length = + info0->usrmod0_min_passwd_len; + dom_info_1.password_history_length = + info0->usrmod0_password_hist_len; + + unix_to_nt_time_abs((NTTIME *)&dom_info_1.max_password_age, + info0->usrmod0_max_passwd_age); + unix_to_nt_time_abs((NTTIME *)&dom_info_1.min_password_age, + info0->usrmod0_min_passwd_age); + + unix_to_nt_time_abs(&dom_info_3.force_logoff_time, + info0->usrmod0_force_logoff); + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + &dom_info_3, + NULL); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_3_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_3 *info3) +{ + NTSTATUS status; + struct samr_DomInfo12 dom_info_12; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + NULL, + NULL, + NULL, + NULL, + &dom_info_12); + NT_STATUS_NOT_OK_RETURN(status); + + unix_to_nt_time_abs((NTTIME *)&dom_info_12.lockout_duration, + info3->usrmod3_lockout_duration); + unix_to_nt_time_abs((NTTIME *)&dom_info_12.lockout_window, + info3->usrmod3_lockout_observation_window); + dom_info_12.lockout_threshold = info3->usrmod3_lockout_threshold; + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + NULL, + &dom_info_12); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_1001_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_1001 *info1001) +{ + NTSTATUS status; + struct samr_DomInfo1 dom_info_1; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + dom_info_1.min_password_length = + info1001->usrmod1001_min_passwd_len; + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_1002_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_1002 *info1002) +{ + NTSTATUS status; + struct samr_DomInfo1 dom_info_1; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + unix_to_nt_time_abs((NTTIME *)&dom_info_1.max_password_age, + info1002->usrmod1002_max_passwd_age); + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_1003_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_1003 *info1003) +{ + NTSTATUS status; + struct samr_DomInfo1 dom_info_1; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + unix_to_nt_time_abs((NTTIME *)&dom_info_1.min_password_age, + info1003->usrmod1003_min_passwd_age); + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_1004_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_1004 *info1004) +{ + NTSTATUS status; + struct samr_DomInfo3 dom_info_3; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + &dom_info_3, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + unix_to_nt_time_abs(&dom_info_3.force_logoff_time, + info1004->usrmod1004_force_logoff); + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + NULL, + &dom_info_3, + NULL); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_1005_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + struct policy_handle *domain_handle, + struct USER_MODALS_INFO_1005 *info1005) +{ + NTSTATUS status; + struct samr_DomInfo1 dom_info_1; + + status = query_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL, + NULL, + NULL, + NULL); + NT_STATUS_NOT_OK_RETURN(status); + + dom_info_1.password_history_length = + info1005->usrmod1005_password_hist_len; + + return set_USER_MODALS_INFO_rpc(mem_ctx, + pipe_cli, + domain_handle, + &dom_info_1, + NULL, + NULL); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS set_USER_MODALS_INFO_buffer(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_cli, + uint32_t level, + struct policy_handle *domain_handle, + struct dom_sid *domain_sid, + uint8_t *buffer) +{ + struct USER_MODALS_INFO_0 *info0; + struct USER_MODALS_INFO_3 *info3; + struct USER_MODALS_INFO_1001 *info1001; + struct USER_MODALS_INFO_1002 *info1002; + struct USER_MODALS_INFO_1003 *info1003; + struct USER_MODALS_INFO_1004 *info1004; + struct USER_MODALS_INFO_1005 *info1005; + + if (!buffer) { + return ERROR_INSUFFICIENT_BUFFER; + } + + switch (level) { + case 0: + info0 = (struct USER_MODALS_INFO_0 *)buffer; + return set_USER_MODALS_INFO_0_buffer(mem_ctx, + pipe_cli, + domain_handle, + info0); + case 3: + info3 = (struct USER_MODALS_INFO_3 *)buffer; + return set_USER_MODALS_INFO_3_buffer(mem_ctx, + pipe_cli, + domain_handle, + info3); + case 1001: + info1001 = (struct USER_MODALS_INFO_1001 *)buffer; + return set_USER_MODALS_INFO_1001_buffer(mem_ctx, + pipe_cli, + domain_handle, + info1001); + case 1002: + info1002 = (struct USER_MODALS_INFO_1002 *)buffer; + return set_USER_MODALS_INFO_1002_buffer(mem_ctx, + pipe_cli, + domain_handle, + info1002); + case 1003: + info1003 = (struct USER_MODALS_INFO_1003 *)buffer; + return set_USER_MODALS_INFO_1003_buffer(mem_ctx, + pipe_cli, + domain_handle, + info1003); + case 1004: + info1004 = (struct USER_MODALS_INFO_1004 *)buffer; + return set_USER_MODALS_INFO_1004_buffer(mem_ctx, + pipe_cli, + domain_handle, + info1004); + case 1005: + info1005 = (struct USER_MODALS_INFO_1005 *)buffer; + return set_USER_MODALS_INFO_1005_buffer(mem_ctx, + pipe_cli, + domain_handle, + info1005); + + default: + break; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserModalsSet_r(struct libnetapi_ctx *ctx, + struct NetUserModalsSet *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + + struct policy_handle connect_handle, domain_handle; + struct dom_sid2 *domain_sid = NULL; + uint32_t access_mask = SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 | + SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_SET_INFO_1 | + SAMR_DOMAIN_ACCESS_SET_INFO_2; + break; + case 3: + case 1001: + case 1002: + case 1003: + case 1005: + access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 | + SAMR_DOMAIN_ACCESS_SET_INFO_1; + break; + case 1004: + access_mask |= SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 | + SAMR_DOMAIN_ACCESS_SET_INFO_2; + break; + case 1: + case 2: + case 1006: + case 1007: + werr = WERR_NOT_SUPPORTED; + break; + default: + werr = WERR_INVALID_LEVEL; + goto done; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + access_mask, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = set_USER_MODALS_INFO_buffer(ctx, + pipe_cli, + r->in.level, + &domain_handle, + domain_sid, + r->in.buffer); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserModalsSet_l(struct libnetapi_ctx *ctx, + struct NetUserModalsSet *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserModalsSet); +} + +/**************************************************************** +****************************************************************/ + +NTSTATUS add_GROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + const char *group_name, + uint32_t attributes, + uint8_t **buffer, + uint32_t *num_entries) +{ + struct GROUP_USERS_INFO_0 u0; + struct GROUP_USERS_INFO_1 u1; + + switch (level) { + case 0: + if (group_name) { + u0.grui0_name = talloc_strdup(mem_ctx, group_name); + NT_STATUS_HAVE_NO_MEMORY(u0.grui0_name); + } else { + u0.grui0_name = NULL; + } + + ADD_TO_ARRAY(mem_ctx, struct GROUP_USERS_INFO_0, u0, + (struct GROUP_USERS_INFO_0 **)buffer, num_entries); + break; + case 1: + if (group_name) { + u1.grui1_name = talloc_strdup(mem_ctx, group_name); + NT_STATUS_HAVE_NO_MEMORY(u1.grui1_name); + } else { + u1.grui1_name = NULL; + } + + u1.grui1_attributes = attributes; + + ADD_TO_ARRAY(mem_ctx, struct GROUP_USERS_INFO_1, u1, + (struct GROUP_USERS_INFO_1 **)buffer, num_entries); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx, + struct NetUserGetGroups *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle, domain_handle, user_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + struct samr_Ids user_rids, name_types; + struct samr_RidWithAttributeArray *rid_array = NULL; + struct lsa_Strings names; + struct samr_Ids types; + uint32_t *rids = NULL; + + int i; + uint32_t entries_read = 0; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + struct dcerpc_binding_handle *b = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + *r->out.buffer = NULL; + *r->out.entries_read = 0; + *r->out.total_entries = 0; + + switch (r->in.level) { + case 0: + case 1: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenUser(b, talloc_tos(), + &domain_handle, + SAMR_USER_ACCESS_GET_GROUPS, + user_rids.ids[0], + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_GetGroupsForUser(b, talloc_tos(), + &user_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + rids = talloc_array(ctx, uint32_t, rid_array->count); + if (!rids) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + for (i=0; i < rid_array->count; i++) { + rids[i] = rid_array->rids[i].rid; + } + + status = dcerpc_samr_LookupRids(b, talloc_tos(), + &domain_handle, + rid_array->count, + rids, + &names, + &types, + &result); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) { + werr = ntstatus_to_werror(result); + goto done; + } + if (names.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + for (i=0; i < names.count; i++) { + status = add_GROUP_USERS_INFO_X_buffer(ctx, + r->in.level, + names.names[i].string, + rid_array->rids[i].attributes, + r->out.buffer, + &entries_read); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + *r->out.entries_read = entries_read; + *r->out.total_entries = entries_read; + + done: + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserGetGroups_l(struct libnetapi_ctx *ctx, + struct NetUserGetGroups *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetGroups); +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserSetGroups_r(struct libnetapi_ctx *ctx, + struct NetUserSetGroups *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle, domain_handle, user_handle, group_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + struct samr_Ids user_rids, name_types; + struct samr_Ids group_rids; + struct samr_RidWithAttributeArray *rid_array = NULL; + struct lsa_String *lsa_names = NULL; + + uint32_t *add_rids = NULL; + uint32_t *del_rids = NULL; + size_t num_add_rids = 0; + size_t num_del_rids = 0; + + uint32_t *member_rids = NULL; + + struct GROUP_USERS_INFO_0 *i0 = NULL; + struct GROUP_USERS_INFO_1 *i1 = NULL; + + int i, k; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + struct dcerpc_binding_handle *b = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + ZERO_STRUCT(group_handle); + + if (!r->in.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 0: + case 1: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenUser(b, talloc_tos(), + &domain_handle, + SAMR_USER_ACCESS_GET_GROUPS, + user_rids.ids[0], + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + switch (r->in.level) { + case 0: + i0 = (struct GROUP_USERS_INFO_0 *)r->in.buffer; + break; + case 1: + i1 = (struct GROUP_USERS_INFO_1 *)r->in.buffer; + break; + } + + lsa_names = talloc_array(ctx, struct lsa_String, r->in.num_entries); + if (!lsa_names) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + for (i=0; i < r->in.num_entries; i++) { + + switch (r->in.level) { + case 0: + init_lsa_String(&lsa_names[i], i0->grui0_name); + i0++; + break; + case 1: + init_lsa_String(&lsa_names[i], i1->grui1_name); + i1++; + break; + } + } + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + r->in.num_entries, + lsa_names, + &group_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (group_rids.count != r->in.num_entries) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != r->in.num_entries) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + member_rids = group_rids.ids; + + status = dcerpc_samr_GetGroupsForUser(b, talloc_tos(), + &user_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + /* add list */ + + for (i=0; i < r->in.num_entries; i++) { + bool already_member = false; + for (k=0; k < rid_array->count; k++) { + if (member_rids[i] == rid_array->rids[k].rid) { + already_member = true; + break; + } + } + if (!already_member) { + if (!add_rid_to_array_unique(ctx, + member_rids[i], + &add_rids, &num_add_rids)) { + werr = WERR_GEN_FAILURE; + goto done; + } + } + } + + /* del list */ + + for (k=0; k < rid_array->count; k++) { + bool keep_member = false; + for (i=0; i < r->in.num_entries; i++) { + if (member_rids[i] == rid_array->rids[k].rid) { + keep_member = true; + break; + } + } + if (!keep_member) { + if (!add_rid_to_array_unique(ctx, + rid_array->rids[k].rid, + &del_rids, &num_del_rids)) { + werr = WERR_GEN_FAILURE; + goto done; + } + } + } + + /* add list */ + + for (i=0; i < num_add_rids; i++) { + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_ADD_MEMBER, + add_rids[i], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_AddGroupMember(b, talloc_tos(), + &group_handle, + user_rids.ids[0], + 7 /* ? */, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + } + + /* del list */ + + for (i=0; i < num_del_rids; i++) { + status = dcerpc_samr_OpenGroup(b, talloc_tos(), + &domain_handle, + SAMR_GROUP_ACCESS_REMOVE_MEMBER, + del_rids[i], + &group_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_DeleteGroupMember(b, talloc_tos(), + &group_handle, + user_rids.ids[0], + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + } + + werr = WERR_OK; + + done: + if (is_valid_policy_hnd(&group_handle)) { + dcerpc_samr_Close(b, talloc_tos(), &group_handle, &result); + } + + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserSetGroups_l(struct libnetapi_ctx *ctx, + struct NetUserSetGroups *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserSetGroups); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS add_LOCALGROUP_USERS_INFO_X_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + const char *group_name, + uint8_t **buffer, + uint32_t *num_entries) +{ + struct LOCALGROUP_USERS_INFO_0 u0; + + switch (level) { + case 0: + u0.lgrui0_name = talloc_strdup(mem_ctx, group_name); + NT_STATUS_HAVE_NO_MEMORY(u0.lgrui0_name); + + ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_USERS_INFO_0, u0, + (struct LOCALGROUP_USERS_INFO_0 **)buffer, num_entries); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserGetLocalGroups_r(struct libnetapi_ctx *ctx, + struct NetUserGetLocalGroups *r) +{ + struct rpc_pipe_client *pipe_cli = NULL; + struct policy_handle connect_handle, domain_handle, user_handle, + builtin_handle; + struct lsa_String lsa_account_name; + struct dom_sid2 *domain_sid = NULL; + struct samr_Ids user_rids, name_types; + struct samr_RidWithAttributeArray *rid_array = NULL; + struct lsa_Strings names; + struct samr_Ids types; + uint32_t *rids = NULL; + size_t num_rids = 0; + struct dom_sid user_sid; + struct lsa_SidArray sid_array; + struct samr_Ids domain_rids; + struct samr_Ids builtin_rids; + + int i; + uint32_t entries_read = 0; + + NTSTATUS status; + NTSTATUS result = NT_STATUS_OK; + WERROR werr; + struct dcerpc_binding_handle *b = NULL; + + ZERO_STRUCT(connect_handle); + ZERO_STRUCT(domain_handle); + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + *r->out.buffer = NULL; + *r->out.entries_read = 0; + *r->out.total_entries = 0; + + switch (r->in.level) { + case 0: + case 1: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_open_pipe(ctx, r->in.server_name, + &ndr_table_samr, + &pipe_cli); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + b = pipe_cli->binding_handle; + + werr = libnetapi_samr_open_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT | + SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS, + &connect_handle, + &domain_handle, + &domain_sid); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli, + SAMR_ACCESS_ENUM_DOMAINS | + SAMR_ACCESS_LOOKUP_DOMAIN, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT | + SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS, + &connect_handle, + &builtin_handle); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + init_lsa_String(&lsa_account_name, r->in.user_name); + + status = dcerpc_samr_LookupNames(b, talloc_tos(), + &domain_handle, + 1, + &lsa_account_name, + &user_rids, + &name_types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + status = dcerpc_samr_OpenUser(b, talloc_tos(), + &domain_handle, + SAMR_USER_ACCESS_GET_GROUPS, + user_rids.ids[0], + &user_handle, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = dcerpc_samr_GetGroupsForUser(b, talloc_tos(), + &user_handle, + &rid_array, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!sid_compose(&user_sid, domain_sid, user_rids.ids[0])) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + sid_array.num_sids = rid_array->count + 1; + sid_array.sids = talloc_array(ctx, struct lsa_SidPtr, sid_array.num_sids); + if (!sid_array.sids) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + sid_array.sids[0].sid = dom_sid_dup(ctx, &user_sid); + if (!sid_array.sids[0].sid) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + for (i=0; i < rid_array->count; i++) { + struct dom_sid sid; + + if (!sid_compose(&sid, domain_sid, rid_array->rids[i].rid)) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + sid_array.sids[i+1].sid = dom_sid_dup(ctx, &sid); + if (!sid_array.sids[i+1].sid) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + } + + status = dcerpc_samr_GetAliasMembership(b, talloc_tos(), + &domain_handle, + &sid_array, + &domain_rids, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + for (i=0; i < domain_rids.count; i++) { + if (!add_rid_to_array_unique(ctx, domain_rids.ids[i], + &rids, &num_rids)) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + } + + status = dcerpc_samr_GetAliasMembership(b, talloc_tos(), + &builtin_handle, + &sid_array, + &builtin_rids, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + for (i=0; i < builtin_rids.count; i++) { + if (!add_rid_to_array_unique(ctx, builtin_rids.ids[i], + &rids, &num_rids)) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + } + + status = dcerpc_samr_LookupRids(b, talloc_tos(), + &builtin_handle, + num_rids, + rids, + &names, + &types, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + werr = ntstatus_to_werror(status); + goto done; + } + if (names.count != num_rids) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != num_rids) { + werr = WERR_BAD_NET_RESP; + goto done; + } + + for (i=0; i < names.count; i++) { + status = add_LOCALGROUP_USERS_INFO_X_buffer(ctx, + r->in.level, + names.names[i].string, + r->out.buffer, + &entries_read); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + } + + *r->out.entries_read = entries_read; + *r->out.total_entries = entries_read; + + done: + if (ctx->disable_policy_handle_cache) { + libnetapi_samr_close_domain_handle(ctx, &domain_handle); + libnetapi_samr_close_connect_handle(ctx, &connect_handle); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetUserGetLocalGroups_l(struct libnetapi_ctx *ctx, + struct NetUserGetLocalGroups *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetUserGetLocalGroups); +} diff --git a/source3/lib/netapi/wkstainfo.c b/source3/lib/netapi/wkstainfo.c new file mode 100644 index 0000000..9cfcff4 --- /dev/null +++ b/source3/lib/netapi/wkstainfo.c @@ -0,0 +1,154 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Workstation Support + * Copyright (C) Guenther Deschner 2007 + * Copyright (C) Hans Leidekker 2013 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "librpc/gen_ndr/libnetapi.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_private.h" +#include "lib/netapi/libnetapi.h" +#include "../librpc/gen_ndr/ndr_wkssvc_c.h" +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_reg.h" + +/**************************************************************** +****************************************************************/ + +WERROR NetWkstaGetInfo_l(struct libnetapi_ctx *ctx, + struct NetWkstaGetInfo *r) +{ + LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetWkstaGetInfo); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS map_wksta_info_to_WKSTA_INFO_buffer(TALLOC_CTX *mem_ctx, + uint32_t level, + union wkssvc_NetWkstaInfo *i, + uint8_t **buffer) +{ + struct WKSTA_INFO_100 i100; + struct WKSTA_INFO_101 i101; + struct WKSTA_INFO_102 i102; + uint32_t num_info = 0; + + switch (level) { + case 100: + i100.wki100_platform_id = i->info100->platform_id; + i100.wki100_computername = talloc_strdup(mem_ctx, i->info100->server_name); + i100.wki100_langroup = talloc_strdup(mem_ctx, i->info100->domain_name); + i100.wki100_ver_major = i->info100->version_major; + i100.wki100_ver_minor = i->info100->version_minor; + + ADD_TO_ARRAY(mem_ctx, struct WKSTA_INFO_100, i100, + (struct WKSTA_INFO_100 **)buffer, + &num_info); + break; + + case 101: + i101.wki101_platform_id = i->info101->platform_id; + i101.wki101_computername = talloc_strdup(mem_ctx, i->info101->server_name); + i101.wki101_langroup = talloc_strdup(mem_ctx, i->info101->domain_name); + i101.wki101_ver_major = i->info101->version_major; + i101.wki101_ver_minor = i->info101->version_minor; + i101.wki101_lanroot = talloc_strdup(mem_ctx, i->info101->lan_root); + + ADD_TO_ARRAY(mem_ctx, struct WKSTA_INFO_101, i101, + (struct WKSTA_INFO_101 **)buffer, + &num_info); + break; + + case 102: + i102.wki102_platform_id = i->info102->platform_id; + i102.wki102_computername = talloc_strdup(mem_ctx, i->info102->server_name); + i102.wki102_langroup = talloc_strdup(mem_ctx, i->info102->domain_name); + i102.wki102_ver_major = i->info102->version_major; + i102.wki102_ver_minor = i->info102->version_minor; + i102.wki102_lanroot = talloc_strdup(mem_ctx, i->info102->lan_root); + i102.wki102_logged_on_users = i->info102->logged_on_users; + + ADD_TO_ARRAY(mem_ctx, struct WKSTA_INFO_102, i102, + (struct WKSTA_INFO_102 **)buffer, + &num_info); + break; + + default: + return NT_STATUS_NOT_SUPPORTED; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetWkstaGetInfo_r(struct libnetapi_ctx *ctx, + struct NetWkstaGetInfo *r) +{ + NTSTATUS status; + WERROR werr; + union wkssvc_NetWkstaInfo info; + struct dcerpc_binding_handle *b; + + if (!r->out.buffer) { + return WERR_INVALID_PARAMETER; + } + + switch (r->in.level) { + case 100: + case 101: + case 102: + break; + default: + return WERR_INVALID_LEVEL; + } + + werr = libnetapi_get_binding_handle(ctx, r->in.server_name, + &ndr_table_wkssvc, + &b); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = dcerpc_wkssvc_NetWkstaGetInfo(b, talloc_tos(), + r->in.server_name, + r->in.level, + &info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + status = map_wksta_info_to_WKSTA_INFO_buffer(ctx, r->in.level, &info, + r->out.buffer); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + return werr; +} diff --git a/source3/lib/per_thread_cwd.c b/source3/lib/per_thread_cwd.c new file mode 100644 index 0000000..b71d4c6 --- /dev/null +++ b/source3/lib/per_thread_cwd.c @@ -0,0 +1,156 @@ +/* + Unix SMB/Netbios implementation. + + Copyright (C) Ralph Boehme 2019 + Copyright (C) Stefan Metzmacher 2019 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/threads.h" +#ifdef HAVE_UNSHARE_CLONE_FS +#include <sched.h> +#endif /* HAVE_UNSHARE_CLONE_FS */ + +static bool _per_thread_cwd_checked; +static bool _per_thread_cwd_supported; +#ifdef HAVE_UNSHARE_CLONE_FS +static __thread bool _per_thread_cwd_disabled; +static __thread bool _per_thread_cwd_activated; +#endif /* HAVE_UNSHARE_CLONE_FS */ + +/* + * This is the first function to be called! + * Typically in the main() function before + * any threads are created. + * + * This can be called multiple times + * as the result is cached the first time. + */ +void per_thread_cwd_check(void) +{ + if (_per_thread_cwd_checked) { + return; + } + +#ifdef HAVE_UNSHARE_CLONE_FS + /* + * While unshare(CLONE_FS) is available on + * Linux for ages, unshare() is also + * used to implement containers with various + * per container namespaces. + * + * It's possible that the whole unshare() + * is blocked in order to disallow nested + * containers. + * + * That's why we sadly need a runtime check + * for this. + */ + { + int res; + + res = unshare(CLONE_FS); + if (res == 0) { + _per_thread_cwd_supported = true; + } + } + + /* + * We're in the main thread, so we should disallow + * per_thread_cwd_activate() here. + */ + _per_thread_cwd_disabled = true; +#endif /* HAVE_UNSHARE_CLONE_FS */ + + _per_thread_cwd_checked = true; +} + +/* + * In order to use per_thread_cwd_supported() + * per_thread_cwd_check() needs to be called first! + * Otherwise an assert will be triggered! + */ +bool per_thread_cwd_supported(void) +{ + SMB_ASSERT(_per_thread_cwd_checked); + return _per_thread_cwd_supported; +} + +/* + * In order to use per_thread_cwd_disable() + * should be called after any fork() in order + * to mark the main thread of the process, + * which should disallow per_thread_cwd_activate(). + * + * This can be called without calling + * per_thread_cwd_check() first. + * + * And it can't be called after calling + * per_thread_cwd_activate()! + * Otherwise an assert will be triggered! + * + * This can be called multiple times + * as the result is cached the first time. + */ +void per_thread_cwd_disable(void) +{ +#ifdef HAVE_UNSHARE_CLONE_FS + SMB_ASSERT(!_per_thread_cwd_activated); + if (_per_thread_cwd_disabled) { + return; + } + _per_thread_cwd_disabled = true; +#endif /* HAVE_UNSHARE_CLONE_FS */ +} + +/* + * In order to use per_thread_cwd_activate() + * per_thread_cwd_supported() needs to be checked first! + * Otherwise an assert will be triggered! + * + * This MUST only be called within helper threads! + * + * That means it can't be called after calling + * per_thread_cwd_disable()! + * Otherwise an assert will be triggered! + * + * This can be called multiple times + * as the result is cached the first time. + */ +void per_thread_cwd_activate(void) +{ + SMB_ASSERT(_per_thread_cwd_checked); + SMB_ASSERT(_per_thread_cwd_supported); + +#ifdef HAVE_UNSHARE_CLONE_FS + if (_per_thread_cwd_activated) { + return; + } + + SMB_ASSERT(!_per_thread_cwd_disabled); + + { + int ret; + ret = unshare(CLONE_FS); + SMB_ASSERT(ret == 0); + } + + _per_thread_cwd_activated = true; +#else /* not HAVE_UNSHARE_CLONE_FS */ + smb_panic(__location__); +#endif /* not HAVE_UNSHARE_CLONE_FS */ +} diff --git a/source3/lib/privileges.c b/source3/lib/privileges.c new file mode 100644 index 0000000..05a4c9a --- /dev/null +++ b/source3/lib/privileges.c @@ -0,0 +1,514 @@ +/* + Unix SMB/CIFS implementation. + Privileges handling functions + Copyright (C) Jean François Micouleau 1998-2001 + Copyright (C) Simo Sorce 2002-2003 + Copyright (C) Gerald (Jerry) Carter 2005 + Copyright (C) Michael Adam 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#include "lib/privileges.h" +#include "dbwrap/dbwrap.h" +#include "libcli/security/privileges_private.h" +#include "../libcli/security/security.h" +#include "passdb.h" +#include "lib/util/string_wrappers.h" + +#define PRIVPREFIX "PRIV_" + +typedef struct { + uint32_t count; + struct dom_sid *list; +} SID_LIST; + +typedef struct { + TALLOC_CTX *mem_ctx; + uint64_t privilege; + SID_LIST sids; +} PRIV_SID_LIST; + +/* + interpret an old style SE_PRIV structure + */ +static uint64_t map_old_SE_PRIV(unsigned char *dptr) +{ + uint32_t *old_masks = (uint32_t *)dptr; + /* + * the old privileges code only ever used up to 0x800, except + * for a special case of 'SE_ALL_PRIVS' which was 0xFFFFFFFF + */ + if (old_masks[0] == 0xFFFFFFFF) { + /* they set all privileges */ + return SE_ALL_PRIVS; + } + + /* the old code used the machine byte order, but we don't know + * the byte order of the machine that wrote it. However we can + * tell what byte order it was by taking advantage of the fact + * that it only ever use up to 0x800 + */ + if (dptr[0] || dptr[1]) { + /* it was little endian */ + return IVAL(dptr, 0); + } + + /* it was either zero or big-endian */ + return RIVAL(dptr, 0); +} + + +static bool get_privileges( const struct dom_sid *sid, uint64_t *mask ) +{ + struct db_context *db = get_account_pol_db(); + struct dom_sid_buf tmp; + fstring keystr; + TDB_DATA data; + NTSTATUS status; + + /* Fail if the admin has not enable privileges */ + + if ( !lp_enable_privileges() ) { + return False; + } + + if ( db == NULL ) + return False; + + /* PRIV_<SID> (NULL terminated) as the key */ + + fstr_sprintf(keystr, "%s%s", PRIVPREFIX, dom_sid_str_buf(sid, &tmp)); + + status = dbwrap_fetch_bystring(db, talloc_tos(), keystr, &data); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(4, ("get_privileges: No privileges assigned to SID " + "[%s]\n", tmp.buf)); + return False; + } + + if (data.dsize == 4*4) { + /* it's an old style SE_PRIV structure. */ + *mask = map_old_SE_PRIV(data.dptr); + } else { + if (data.dsize != sizeof( uint64_t ) ) { + DEBUG(3, ("get_privileges: Invalid privileges record assigned to SID " + "[%s]\n", tmp.buf)); + return False; + } + + *mask = BVAL(data.dptr, 0); + } + + TALLOC_FREE(data.dptr); + + return True; +} + +/*************************************************************************** + Store the privilege mask (set) for a given SID +****************************************************************************/ + +static bool set_privileges( const struct dom_sid *sid, uint64_t mask ) +{ + struct db_context *db = get_account_pol_db(); + uint8_t privbuf[8]; + struct dom_sid_buf tmp; + fstring keystr; + TDB_DATA data = { .dptr = privbuf, .dsize = sizeof(privbuf), }; + + if ( !lp_enable_privileges() ) + return False; + + if ( db == NULL ) + return False; + + if ( !sid || (sid->num_auths == 0) ) { + DEBUG(0,("set_privileges: Refusing to store empty SID!\n")); + return False; + } + + /* PRIV_<SID> (NULL terminated) as the key */ + + fstr_sprintf(keystr, "%s%s", PRIVPREFIX, dom_sid_str_buf(sid, &tmp)); + + /* This writes the 64 bit bitmask out in little endian format */ + SBVAL(privbuf,0,mask); + + return NT_STATUS_IS_OK(dbwrap_store_bystring(db, keystr, data, + TDB_REPLACE)); +} + +/********************************************************************* + get a list of all privileges for all sids in the list +*********************************************************************/ + +bool get_privileges_for_sids(uint64_t *privileges, struct dom_sid *slist, int scount) +{ + uint64_t mask; + int i; + bool found = False; + + *privileges = 0; + + for ( i=0; i<scount; i++ ) { + struct dom_sid_buf buf; + + /* don't add unless we actually have a privilege assigned */ + + if ( !get_privileges( &slist[i], &mask ) ) + continue; + + DBG_INFO("sid = %s\nPrivilege set: 0x%"PRIx64"\n", + dom_sid_str_buf(&slist[i], &buf), + mask); + + *privileges |= mask; + found = True; + } + + return found; +} + +NTSTATUS get_privileges_for_sid_as_set(TALLOC_CTX *mem_ctx, PRIVILEGE_SET **privileges, struct dom_sid *sid) +{ + uint64_t mask; + if (!get_privileges(sid, &mask)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + *privileges = talloc_zero(mem_ctx, PRIVILEGE_SET); + if (!*privileges) { + return NT_STATUS_NO_MEMORY; + } + + if (!se_priv_to_privilege_set(*privileges, mask)) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/********************************************************************* + traversal functions for privilege_enumerate_accounts +*********************************************************************/ + +static int priv_traverse_fn(struct db_record *rec, void *state) +{ + PRIV_SID_LIST *priv = (PRIV_SID_LIST *)state; + int prefixlen = strlen(PRIVPREFIX); + struct dom_sid sid; + fstring sid_string; + TDB_DATA key; + + key = dbwrap_record_get_key(rec); + + /* check we have a PRIV_+SID entry */ + + if (strncmp((char *)key.dptr, PRIVPREFIX, prefixlen) != 0) + return 0; + + /* check to see if we are looking for a particular privilege */ + + fstrcpy( sid_string, (char *)&(key.dptr[strlen(PRIVPREFIX)]) ); + + if (priv->privilege != 0) { + uint64_t mask; + TDB_DATA value; + + value = dbwrap_record_get_value(rec); + + if (value.dsize == 4*4) { + mask = map_old_SE_PRIV(value.dptr); + } else { + if (value.dsize != sizeof( uint64_t ) ) { + DEBUG(3, ("get_privileges: Invalid privileges record assigned to SID " + "[%s]\n", sid_string)); + return 0; + } + mask = BVAL(value.dptr, 0); + } + + /* if the SID does not have the specified privilege + then just return */ + + if ((mask & priv->privilege) == 0) { + return 0; + } + } + + /* this is a last ditch safety check to preventing returning + and invalid SID (i've somehow run into this on development branches) */ + + if ( strcmp( "S-0-0", sid_string ) == 0 ) + return 0; + + if ( !string_to_sid(&sid, sid_string) ) { + DBG_WARNING("Could not convert SID [%s]\n", sid_string); + return 0; + } + + if (!NT_STATUS_IS_OK(add_sid_to_array(priv->mem_ctx, &sid, + &priv->sids.list, + &priv->sids.count))) + { + return 0; + } + + return 0; +} + +/********************************************************************* + Retrieve list of privileged SIDs (for _lsa_enumerate_accounts() +*********************************************************************/ + +NTSTATUS privilege_enumerate_accounts(struct dom_sid **sids, int *num_sids) +{ + struct db_context *db = get_account_pol_db(); + PRIV_SID_LIST priv; + NTSTATUS status; + + if (db == NULL) { + return NT_STATUS_ACCESS_DENIED; + } + + ZERO_STRUCT(priv); + + status = dbwrap_traverse_read(db, priv_traverse_fn, &priv, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* give the memory away; caller will free */ + + *sids = priv.sids.list; + *num_sids = priv.sids.count; + + return NT_STATUS_OK; +} + +/********************************************************************* + Retrieve list of SIDs granted a particular privilege +*********************************************************************/ + +NTSTATUS privilege_enum_sids(enum sec_privilege privilege, TALLOC_CTX *mem_ctx, + struct dom_sid **sids, int *num_sids) +{ + struct db_context *db = get_account_pol_db(); + PRIV_SID_LIST priv; + NTSTATUS status; + + if (db == NULL) { + return NT_STATUS_ACCESS_DENIED; + } + + ZERO_STRUCT(priv); + + priv.privilege = sec_privilege_mask(privilege); + priv.mem_ctx = mem_ctx; + + status = dbwrap_traverse_read(db, priv_traverse_fn, &priv, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* give the memory away; caller will free */ + + *sids = priv.sids.list; + *num_sids = priv.sids.count; + + return NT_STATUS_OK; +} + +/*************************************************************************** + Add privilege to sid +****************************************************************************/ + +static bool grant_privilege_bitmap(const struct dom_sid *sid, const uint64_t priv_mask) +{ + uint64_t old_mask = 0, new_mask = 0; + struct dom_sid_buf buf; + + if ( get_privileges( sid, &old_mask ) ) { + new_mask = old_mask; + } + + new_mask |= priv_mask; + + DBG_DEBUG("%s\n" + "original privilege mask: 0x%"PRIx64"\n" + "new privilege mask: 0x%"PRIx64"\n", + dom_sid_str_buf(sid, &buf), + old_mask, + new_mask); + + return set_privileges( sid, new_mask ); +} + +/********************************************************************* + Add a privilege based on its name +*********************************************************************/ + +bool grant_privilege_by_name(const struct dom_sid *sid, const char *name) +{ + uint64_t mask; + + if (! se_priv_from_name(name, &mask)) { + DEBUG(3, ("grant_privilege_by_name: " + "No Such Privilege Found (%s)\n", name)); + return False; + } + + return grant_privilege_bitmap( sid, mask ); +} + +/*************************************************************************** + Grant a privilege set (list of LUID values) from a sid +****************************************************************************/ + +bool grant_privilege_set(const struct dom_sid *sid, struct lsa_PrivilegeSet *set) +{ + uint64_t privilege_mask; + if (!privilege_set_to_se_priv(&privilege_mask, set)) { + return false; + } + return grant_privilege_bitmap(sid, privilege_mask); +} + +/*************************************************************************** + Remove privilege from sid +****************************************************************************/ + +static bool revoke_privilege_bitmap(const struct dom_sid *sid, const uint64_t priv_mask) +{ + uint64_t mask; + struct dom_sid_buf buf; + + /* if the user has no privileges, then we can't revoke any */ + + if ( !get_privileges( sid, &mask ) ) + return True; + + DEBUG(10,("revoke_privilege: %s\n", dom_sid_str_buf(sid, &buf))); + + DEBUGADD( 10, ("original privilege mask: 0x%llx\n", (unsigned long long)mask)); + + mask &= ~priv_mask; + + DEBUGADD( 10, ("new privilege mask: 0x%llx\n", (unsigned long long)mask)); + + return set_privileges( sid, mask ); +} + +/*************************************************************************** + Remove a privilege set (list of LUID values) from a sid +****************************************************************************/ + +bool revoke_privilege_set(const struct dom_sid *sid, struct lsa_PrivilegeSet *set) +{ + uint64_t privilege_mask; + if (!privilege_set_to_se_priv(&privilege_mask, set)) { + return false; + } + return revoke_privilege_bitmap(sid, privilege_mask); +} + +/********************************************************************* + Revoke all privileges +*********************************************************************/ + +bool revoke_all_privileges( const struct dom_sid *sid ) +{ + return revoke_privilege_bitmap( sid, SE_ALL_PRIVS); +} + +/********************************************************************* + Add a privilege based on its name +*********************************************************************/ + +bool revoke_privilege_by_name(const struct dom_sid *sid, const char *name) +{ + uint64_t mask; + + if (! se_priv_from_name(name, &mask)) { + DEBUG(3, ("revoke_privilege_by_name: " + "No Such Privilege Found (%s)\n", name)); + return False; + } + + return revoke_privilege_bitmap(sid, mask); + +} + +/*************************************************************************** + Retrieve the SIDs assigned to a given privilege +****************************************************************************/ + +NTSTATUS privilege_create_account(const struct dom_sid *sid ) +{ + return ( grant_privilege_bitmap(sid, 0) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL); +} + +/*************************************************************************** + Delete a privileged account +****************************************************************************/ + +NTSTATUS privilege_delete_account(const struct dom_sid *sid) +{ + struct db_context *db = get_account_pol_db(); + struct dom_sid_buf tmp; + fstring keystr; + + if (!lp_enable_privileges()) { + return NT_STATUS_OK; + } + + if (!db) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!sid || (sid->num_auths == 0)) { + return NT_STATUS_INVALID_SID; + } + + /* PRIV_<SID> (NULL terminated) as the key */ + + fstr_sprintf(keystr, "%s%s", PRIVPREFIX, dom_sid_str_buf(sid, &tmp)); + + return dbwrap_delete_bystring(db, keystr); +} + +/******************************************************************* +*******************************************************************/ + +bool is_privileged_sid( const struct dom_sid *sid ) +{ + uint64_t mask; + + return get_privileges( sid, &mask ); +} + +/******************************************************************* +*******************************************************************/ + +bool grant_all_privileges( const struct dom_sid *sid ) +{ + uint64_t mask; + + se_priv_put_all_privileges(&mask); + + return grant_privilege_bitmap( sid, mask ); +} diff --git a/source3/lib/privileges.h b/source3/lib/privileges.h new file mode 100644 index 0000000..ca2a7c9 --- /dev/null +++ b/source3/lib/privileges.h @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Privileges handling functions + Copyright (C) Jean François Micouleau 1998-2001 + Copyright (C) Simo Sorce 2002-2003 + Copyright (C) Gerald (Jerry) Carter 2005 + Copyright (C) Michael Adam 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIB_PRIVILEGES_H_ +#define _LIB_PRIVILEGES_H_ + +#include "../libcli/security/privileges.h" + +/* The following definitions come from lib/privileges.c */ + +bool get_privileges_for_sids(uint64_t *privileges, struct dom_sid *slist, int scount); +NTSTATUS get_privileges_for_sid_as_set(TALLOC_CTX *mem_ctx, PRIVILEGE_SET **privileges, struct dom_sid *sid); +NTSTATUS privilege_enumerate_accounts(struct dom_sid **sids, int *num_sids); +NTSTATUS privilege_enum_sids(enum sec_privilege privilege, TALLOC_CTX *mem_ctx, + struct dom_sid **sids, int *num_sids); +bool grant_privilege_set(const struct dom_sid *sid, struct lsa_PrivilegeSet *set); +bool grant_privilege_by_name( const struct dom_sid *sid, const char *name); +bool revoke_all_privileges( const struct dom_sid *sid ); +bool revoke_privilege_set(const struct dom_sid *sid, struct lsa_PrivilegeSet *set); +bool revoke_privilege_by_name(const struct dom_sid *sid, const char *name); +NTSTATUS privilege_create_account(const struct dom_sid *sid ); +NTSTATUS privilege_delete_account(const struct dom_sid *sid); +bool is_privileged_sid( const struct dom_sid *sid ); +bool grant_all_privileges( const struct dom_sid *sid ); + +#endif /* _LIB_PRIVILEGES_H_ */ diff --git a/source3/lib/readdir_attr.h b/source3/lib/readdir_attr.h new file mode 100644 index 0000000..d2a814d --- /dev/null +++ b/source3/lib/readdir_attr.h @@ -0,0 +1,37 @@ +/* + * Fetch filesystem metadata in readdir/marshall context + * + * Copyright (C) Ralph Boehme 2014 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _READDIR_ATTR_H +#define _READDIR_ATTR_H + +enum readdir_attr_type {RDATTR_NONE, RDATTR_AAPL}; + +struct readdir_attr_data { + enum readdir_attr_type type; + union attr_data { + struct aapl { + uint64_t rfork_size; + char finder_info[16]; + uint32_t max_access; + mode_t unix_mode; + } aapl; + } attr_data; +}; + +#endif /* _READDIR_ATTR_H */ diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c new file mode 100644 index 0000000..e1eb241 --- /dev/null +++ b/source3/lib/recvfile.c @@ -0,0 +1,310 @@ +/* + Unix SMB/Netbios implementation. + Version 3.2.x + recvfile implementations. + Copyright (C) Jeremy Allison 2007. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * This file handles the OS dependent recvfile implementations. + * The API is such that it returns -1 on error, else returns the + * number of bytes written. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/util/sys_rw.h" + +/* Do this on our own in TRANSFER_BUF_SIZE chunks. + * It's safe to make direct syscalls to lseek/write here + * as we're below the Samba vfs layer. + * + * Returns -1 on short reads from fromfd (read error) + * and sets errno. + * + * Returns number of bytes written to 'tofd' + * return != count then sets errno. + * Returns count if complete success. + */ + +#ifndef TRANSFER_BUF_SIZE +#define TRANSFER_BUF_SIZE (128*1024) +#endif + +static ssize_t default_sys_recvfile(int fromfd, + int tofd, + off_t offset, + size_t count) +{ + int saved_errno = 0; + size_t total = 0; + size_t bufsize = MIN(TRANSFER_BUF_SIZE,count); + size_t total_written = 0; + char buffer[bufsize]; + + DEBUG(10,("default_sys_recvfile: from = %d, to = %d, " + "offset=%.0f, count = %lu\n", + fromfd, tofd, (double)offset, + (unsigned long)count)); + + if (count == 0) { + return 0; + } + + if (tofd != -1 && offset != (off_t)-1) { + if (lseek(tofd, offset, SEEK_SET) == -1) { + if (errno != ESPIPE) { + return -1; + } + } + } + + while (total < count) { + size_t num_written = 0; + ssize_t read_ret; + size_t toread = MIN(bufsize,count - total); + + /* + * Read from socket - ignore EINTR. + * Can't use sys_read() as that also + * ignores EAGAIN and EWOULDBLOCK. + */ + do { + read_ret = read(fromfd, buffer, toread); + } while (read_ret == -1 && errno == EINTR); + + if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + /* + * fromfd socket is in non-blocking mode. + * If we already read some and wrote + * it successfully, return that. + * Only return -1 if this is the first read + * attempt. Caller will handle both cases. + */ + if (total_written != 0) { + return total_written; + } + return -1; + } + + if (read_ret <= 0) { + /* EOF or socket error. */ + return -1; + } + + num_written = 0; + + /* Don't write any more after a write error. */ + while (tofd != -1 && (num_written < read_ret)) { + ssize_t write_ret; + + /* Write to file - ignore EINTR. */ + write_ret = sys_write(tofd, + buffer + num_written, + read_ret - num_written); + + if (write_ret <= 0) { + /* write error - stop writing. */ + tofd = -1; + if (total_written == 0) { + /* Ensure we return + -1 if the first + write failed. */ + total_written = -1; + } + saved_errno = errno; + break; + } + + num_written += (size_t)write_ret; + total_written += (size_t)write_ret; + } + + total += read_ret; + } + + if (saved_errno) { + /* Return the correct write error. */ + errno = saved_errno; + } + return (ssize_t)total_written; +} + +#if defined(HAVE_LINUX_SPLICE) + +/* + * Try and use the Linux system call to do this. + * Remember we only return -1 if the socket read + * failed. Else we return the number of bytes + * actually written. We always read count bytes + * from the network in the case of return != -1. + */ + + +ssize_t sys_recvfile(int fromfd, + int tofd, + off_t offset, + size_t count) +{ + static int pipefd[2] = { -1, -1 }; + static bool try_splice_call = false; + size_t total_written = 0; + loff_t splice_offset = offset; + + DEBUG(10,("sys_recvfile: from = %d, to = %d, " + "offset=%.0f, count = %lu\n", + fromfd, tofd, (double)offset, + (unsigned long)count)); + + if (count == 0) { + return 0; + } + + /* + * Older Linux kernels have splice for sendfile, + * but it fails for recvfile. Ensure we only try + * this once and always fall back to the userspace + * implementation if recvfile splice fails. JRA. + */ + + if (!try_splice_call) { + return default_sys_recvfile(fromfd, + tofd, + offset, + count); + } + + if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) { + try_splice_call = false; + return default_sys_recvfile(fromfd, tofd, offset, count); + } + + while (count > 0) { + int nread, to_write; + + nread = splice(fromfd, NULL, pipefd[1], NULL, + MIN(count, 16384), SPLICE_F_MOVE); + if (nread == -1) { + if (errno == EINTR) { + continue; + } + if (total_written == 0 && + (errno == EBADF || errno == EINVAL)) { + try_splice_call = false; + return default_sys_recvfile(fromfd, tofd, + offset, count); + } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * fromfd socket is in non-blocking mode. + * If we already read some and wrote + * it successfully, return that. + * Only return -1 if this is the first read + * attempt. Caller will handle both cases. + */ + if (total_written != 0) { + return total_written; + } + return -1; + } + break; + } + + to_write = nread; + while (to_write > 0) { + int thistime; + thistime = splice(pipefd[0], NULL, tofd, + &splice_offset, to_write, + SPLICE_F_MOVE); + if (thistime == -1) { + goto done; + } + to_write -= thistime; + } + + total_written += nread; + count -= nread; + } + + done: + if (count) { + int saved_errno = errno; + if (drain_socket(fromfd, count) != count) { + /* socket is dead. */ + return -1; + } + errno = saved_errno; + } + + return total_written; +} +#else + +/***************************************************************** + No recvfile system call - use the default 128 chunk implementation. +*****************************************************************/ + +ssize_t sys_recvfile(int fromfd, + int tofd, + off_t offset, + size_t count) +{ + return default_sys_recvfile(fromfd, tofd, offset, count); +} +#endif + +/***************************************************************** + Throw away "count" bytes from the client socket. + Returns count or -1 on error. + Must only operate on a blocking socket. +*****************************************************************/ + +ssize_t drain_socket(int sockfd, size_t count) +{ + size_t total = 0; + size_t bufsize = MIN(TRANSFER_BUF_SIZE,count); + char buffer[bufsize]; + int old_flags = 0; + + if (count == 0) { + return 0; + } + + old_flags = fcntl(sockfd, F_GETFL, 0); + if (set_blocking(sockfd, true) == -1) { + return -1; + } + + while (total < count) { + ssize_t read_ret; + size_t toread = MIN(bufsize,count - total); + + /* Read from socket - ignore EINTR. */ + read_ret = sys_read(sockfd, buffer, toread); + if (read_ret <= 0) { + /* EOF or socket error. */ + count = (size_t)-1; + goto out; + } + total += read_ret; + } + + out: + + if (fcntl(sockfd, F_SETFL, old_flags) == -1) { + return -1; + } + return count; +} diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c new file mode 100644 index 0000000..69c89d2 --- /dev/null +++ b/source3/lib/sendfile.c @@ -0,0 +1,602 @@ +/* + Unix SMB/Netbios implementation. + Version 2.2.x / 3.0.x + sendfile implementations. + Copyright (C) Jeremy Allison 2002. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * This file handles the OS dependent sendfile implementations. + * The API is such that it returns -1 on error, else returns the + * number of bytes written. + */ + +#include "includes.h" +#include "system/filesys.h" + +#if defined(LINUX_SENDFILE_API) + +#include <sys/sendfile.h> + +#ifndef MSG_MORE +#define MSG_MORE 0x8000 +#endif + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) +{ + size_t total=0; + ssize_t ret = -1; + size_t hdr_len = 0; + int old_flags = 0; + bool socket_flags_changed = false; + + /* + * Send the header first. + * Use MSG_MORE to cork the TCP output until sendfile is called. + */ + + if (header) { + hdr_len = header->length; + while (total < hdr_len) { + ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE); + if (ret == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * send() must complete before we can + * send any other outgoing data on the + * socket. Ensure socket is in blocking + * mode. For SMB2 by default the socket + * is in non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + goto out; + } + total += ret; + } + } + + total = count; + while (total) { + ssize_t nwritten; + do { + nwritten = sendfile(tofd, fromfd, &offset, total); + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (socket_flags_changed) { + /* + * We're already in blocking + * mode. This is an error. + */ + ret = -1; + goto out; + } + + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + + if (errno == ENOSYS || errno == EINVAL) { + /* Ok - we're in a world of pain here. We just sent + * the header, but the sendfile failed. We have to + * emulate the sendfile at an upper layer before we + * disable it's use. So we do something really ugly. + * We set the errno to a strange value so we can detect + * this at the upper level and take care of it without + * layer violation. JRA. + */ + errno = EINTR; /* Normally we can never return this. */ + } + ret = -1; + goto out; + } + if (nwritten == 0) { + /* + * EOF, return a short read + */ + ret = hdr_len + (count - total); + goto out; + } + total -= nwritten; + } + + ret = count + hdr_len; + + out: + + if (socket_flags_changed) { + int saved_errno = errno; + int err; + + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; +} + +#elif defined(SOLARIS_SENDFILE_API) + +/* + * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>. + */ + +#include <sys/sendfile.h> + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) +{ + int sfvcnt; + size_t total, xferred; + struct sendfilevec vec[2]; + ssize_t hdr_len = 0; + int old_flags = 0; + ssize_t ret = -1; + bool socket_flags_changed = false; + + if (header) { + sfvcnt = 2; + + vec[0].sfv_fd = SFV_FD_SELF; + vec[0].sfv_flag = 0; + vec[0].sfv_off = (off_t)header->data; + vec[0].sfv_len = hdr_len = header->length; + + vec[1].sfv_fd = fromfd; + vec[1].sfv_flag = 0; + vec[1].sfv_off = offset; + vec[1].sfv_len = count; + + } else { + sfvcnt = 1; + + vec[0].sfv_fd = fromfd; + vec[0].sfv_flag = 0; + vec[0].sfv_off = offset; + vec[0].sfv_len = count; + } + + total = count + hdr_len; + + while (total) { + ssize_t nwritten; + + /* + * Although not listed in the API error returns, this is almost certainly + * a slow system call and will be interrupted by a signal with EINTR. JRA. + */ + + xferred = 0; + + nwritten = sendfilev(tofd, vec, sfvcnt, &xferred); + if (nwritten == -1 && errno == EINTR) { + if (xferred == 0) + continue; /* Nothing written yet. */ + else + nwritten = xferred; + } + + if (nwritten == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + ret = -1; + goto out; + } + if (nwritten == 0) { + ret = -1; + goto out; /* I think we're at EOF here... */ + } + + /* + * If this was a short (signal interrupted) write we may need + * to subtract it from the header data, or null out the header + * data altogether if we wrote more than vec[0].sfv_len bytes. + * We move vec[1].* to vec[0].* and set sfvcnt to 1 + */ + + if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) { + vec[1].sfv_off += nwritten - vec[0].sfv_len; + vec[1].sfv_len -= nwritten - vec[0].sfv_len; + + /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */ + vec[0] = vec[1]; + sfvcnt = 1; + } else { + vec[0].sfv_off += nwritten; + vec[0].sfv_len -= nwritten; + } + total -= nwritten; + } + ret = count + hdr_len; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; +} + +#elif defined(HPUX_SENDFILE_API) + +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) +{ + size_t total=0; + struct iovec hdtrl[2]; + size_t hdr_len = 0; + int old_flags = 0; + ssize_t ret = -1; + bool socket_flags_changed = false; + + if (header) { + /* Set up the header/trailer iovec. */ + hdtrl[0].iov_base = (void *)header->data; + hdtrl[0].iov_len = hdr_len = header->length; + } else { + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = hdr_len = 0; + } + hdtrl[1].iov_base = NULL; + hdtrl[1].iov_len = 0; + + total = count; + while (total + hdtrl[0].iov_len) { + ssize_t nwritten; + + /* + * HPUX guarantees that if any data was written before + * a signal interrupt then sendfile returns the number of + * bytes written (which may be less than requested) not -1. + * nwritten includes the header data sent. + */ + + do { + nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0); + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + ret = -1; + goto out; + } + if (nwritten == 0) { + ret = -1; /* I think we're at EOF here... */ + goto out; + } + + /* + * If this was a short (signal interrupted) write we may need + * to subtract it from the header data, or null out the header + * data altogether if we wrote more than hdtrl[0].iov_len bytes. + * We change nwritten to be the number of file bytes written. + */ + + if (hdtrl[0].iov_base && hdtrl[0].iov_len) { + if (nwritten >= hdtrl[0].iov_len) { + nwritten -= hdtrl[0].iov_len; + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + } else { + /* iov_base is defined as a void *... */ + hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten); + hdtrl[0].iov_len -= nwritten; + nwritten = 0; + } + } + total -= nwritten; + offset += nwritten; + } + ret = count + hdr_len; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; +} + +#elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API) + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t sys_sendfile(int tofd, int fromfd, + const DATA_BLOB *header, off_t offset, size_t count) +{ + struct sf_hdtr sf_header = {0}; + struct iovec io_header = {0}; + int old_flags = 0; + + off_t nwritten; + ssize_t ret = -1; + bool socket_flags_changed = false; + + if (header) { + sf_header.headers = &io_header; + sf_header.hdr_cnt = 1; + io_header.iov_base = header->data; + io_header.iov_len = header->length; + sf_header.trailers = NULL; + sf_header.trl_cnt = 0; + } + + while (count != 0) { + + nwritten = count; +#if defined(DARWIN_SENDFILE_API) + /* Darwin recycles nwritten as a value-result parameter, apart from that this + sendfile implementation is quite the same as the FreeBSD one */ + ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0); +#else + ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0); +#endif + if (ret == -1 && errno != EINTR) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + /* Send failed, we are toast. */ + ret = -1; + goto out; + } + + if (nwritten == 0) { + /* EOF of offset is after EOF. */ + break; + } + + if (sf_header.hdr_cnt) { + if (io_header.iov_len <= nwritten) { + /* Entire header was sent. */ + sf_header.headers = NULL; + sf_header.hdr_cnt = 0; + nwritten -= io_header.iov_len; + } else { + /* Partial header was sent. */ + io_header.iov_len -= nwritten; + io_header.iov_base = + ((uint8_t *)io_header.iov_base) + nwritten; + nwritten = 0; + } + } + + offset += nwritten; + count -= nwritten; + } + + ret = nwritten; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; +} + +#elif defined(AIX_SENDFILE_API) + +/* BEGIN AIX SEND_FILE */ + +/* Contributed by William Jojo <jojowil@hvcc.edu> */ +#include <sys/socket.h> + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) +{ + struct sf_parms hdtrl; + int old_flags = 0; + ssize_t ret = -1; + bool socket_flags_changed = false; + + /* Set up the header/trailer struct params. */ + if (header) { + hdtrl.header_data = header->data; + hdtrl.header_length = header->length; + } else { + hdtrl.header_data = NULL; + hdtrl.header_length = 0; + } + hdtrl.trailer_data = NULL; + hdtrl.trailer_length = 0; + + hdtrl.file_descriptor = fromfd; + hdtrl.file_offset = offset; + hdtrl.file_bytes = count; + + while ( hdtrl.file_bytes + hdtrl.header_length ) { + /* + Return Value + + There are three possible return values from send_file: + + Value Description + + -1 an error has occurred, errno contains the error code. + + 0 the command has completed successfully. + + 1 the command was completed partially, some data has been + transmitted but the command has to return for some reason, + for example, the command was interrupted by signals. + */ + do { + ret = send_file(&tofd, &hdtrl, 0); + } while ((ret == 1) || (ret == -1 && errno == EINTR)); + if ( ret == -1 ) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + goto out; + } + } + + ret = count + header->length; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; +} +/* END AIX SEND_FILE */ + +#else /* No sendfile implementation. Return error. */ + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) +{ + /* No sendfile syscall. */ + errno = ENOSYS; + return -1; +} +#endif diff --git a/source3/lib/server_id_db_util.c b/source3/lib/server_id_db_util.c new file mode 100644 index 0000000..e73af24 --- /dev/null +++ b/source3/lib/server_id_db_util.c @@ -0,0 +1,99 @@ +/* + * Unix SMB/CIFS implementation. + * Utils around server_id_db with more dependencies + * Copyright (C) Volker Lendecke 2014 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "server_id_db_util.h" +#include "lib/util/server_id.h" +#include "serverid.h" +#include "lib/util/samba_util.h" + +static int server_id_db_check_exclusive( + struct server_id_db *db, const char *name, + unsigned num_servers, struct server_id *servers); + +int server_id_db_set_exclusive(struct server_id_db *db, const char *name) +{ + int ret; + unsigned num_servers; + struct server_id *servers; + + ret = server_id_db_add(db, name); + if (ret != 0) { + return ret; + } + + ret = server_id_db_lookup(db, name, talloc_tos(), + &num_servers, &servers); + if (ret != 0) { + goto done; + } + + /* + * Remove entries from the server_id_db for processes that have died + * and could not clean up. This is racy, as two processes could + * simultaneously try to register a name. Both would succeed in the + * server_id_db_add call, and both would see their peer active during + * the check_exclusive call. Both would get an EEXIST, and nobody + * would be able to register itself. But this is okay, as this is + * meant to be a cleanup routine, and normally only one daemon should + * start up at a time anyway. Getting this "right" would mean we would + * have to add locking to server_id_db, or add a dependency on + * serverids_exist to server_id_db. Both are too heavy-weight for my + * taste. + */ + + ret = server_id_db_check_exclusive(db, name, num_servers, servers); + TALLOC_FREE(servers); + +done: + if (ret != 0) { + server_id_db_remove(db, name); + } + return ret; +} + +static int server_id_db_check_exclusive( + struct server_id_db *db, const char *name, + unsigned num_servers, struct server_id *servers) +{ + struct server_id me = server_id_db_pid(db); + unsigned i; + + for (i=0; i<num_servers; i++) { + int ret; + + if (server_id_same_process(&me, &servers[i])) { + /* + * I am always around ... :-) + */ + continue; + } + + if (serverid_exists(&servers[i])) { + return EEXIST; + } + + ret = server_id_db_prune_name(db, name, servers[i]); + if (ret != 0) { + return ret; + } + } + + return 0; +} diff --git a/source3/lib/server_id_db_util.h b/source3/lib/server_id_db_util.h new file mode 100644 index 0000000..5fdd078 --- /dev/null +++ b/source3/lib/server_id_db_util.h @@ -0,0 +1,22 @@ +/* + * Unix SMB/CIFS implementation. + * Utils around server_id_db with more dependencies + * Copyright (C) Volker Lendecke 2014 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "lib/util/server_id_db.h" + +int server_id_db_set_exclusive(struct server_id_db *db, const char *name); diff --git a/source3/lib/server_id_watch.c b/source3/lib/server_id_watch.c new file mode 100644 index 0000000..f0189e0 --- /dev/null +++ b/source3/lib/server_id_watch.c @@ -0,0 +1,104 @@ +/* + * Unix SMB/CIFS implementation. + * Wait for process death + * Copyright (C) Volker Lendecke 2016 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include <tevent.h> +#include <talloc.h> +#include "serverid.h" +#include "server_id_watch.h" +#include "lib/util/tevent_unix.h" + +struct server_id_watch_state { + struct tevent_context *ev; + struct server_id pid; +}; + +static void server_id_watch_waited(struct tevent_req *subreq); + +struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct server_id pid) +{ + struct tevent_req *req, *subreq; + struct server_id_watch_state *state; + + req = tevent_req_create(mem_ctx, &state, struct server_id_watch_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->pid = pid; + + if (!serverid_exists(&state->pid)) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + subreq = tevent_wakeup_send( + state, ev, tevent_timeval_current_ofs(0, 500000)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, server_id_watch_waited, req); + + return req; +} + +static void server_id_watch_waited(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct server_id_watch_state *state = tevent_req_data( + req, struct server_id_watch_state); + bool ok; + + ok = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ok) { + tevent_req_oom(req); + return; + } + + if (!serverid_exists(&state->pid)) { + tevent_req_done(req); + return; + } + + subreq = tevent_wakeup_send( + state, state->ev, tevent_timeval_current_ofs(0, 500000)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, server_id_watch_waited, req); +} + +int server_id_watch_recv(struct tevent_req *req, struct server_id *pid) +{ + struct server_id_watch_state *state = tevent_req_data( + req, struct server_id_watch_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + return err; + } + if (pid) { + *pid = state->pid; + } + return 0; +} diff --git a/source3/lib/server_id_watch.h b/source3/lib/server_id_watch.h new file mode 100644 index 0000000..7e88cb4 --- /dev/null +++ b/source3/lib/server_id_watch.h @@ -0,0 +1,33 @@ +/* + * Unix SMB/CIFS implementation. + * Wait for process death + * Copyright (C) Volker Lendecke 2016 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_SERVER_ID_WATCH_H__ +#define __LIB_SERVER_ID_WATCH_H__ + +#include "replace.h" +#include <tevent.h> +#include <talloc.h> +#include "librpc/gen_ndr/server_id.h" + +struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct server_id pid); +int server_id_watch_recv(struct tevent_req *req, struct server_id *pid); + +#endif diff --git a/source3/lib/server_mutex.c b/source3/lib/server_mutex.c new file mode 100644 index 0000000..cbb8357 --- /dev/null +++ b/source3/lib/server_mutex.c @@ -0,0 +1,104 @@ +/* + Unix SMB/CIFS implementation. + Authenticate against a remote domain + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Andrew Bartlett 2002 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include "util_tdb.h" +#include "lib/param/param.h" + +/* For reasons known only to MS, many of their NT/Win2k versions + need serialised access only. Two connections at the same time + may (in certain situations) cause connections to be reset, + or access to be denied. + + This locking allows smbd's multithread architecture to look + like the single-connection that NT makes. */ + +struct named_mutex { + struct tdb_wrap *tdb; + char *name; +}; + +static int unlock_named_mutex(struct named_mutex *mutex) +{ + tdb_unlock_bystring(mutex->tdb->tdb, mutex->name); + return 0; +} + +struct named_mutex *grab_named_mutex(TALLOC_CTX *mem_ctx, const char *name, + int timeout) +{ + struct named_mutex *result; + struct loadparm_context *lp_ctx; + char *fname; + + result = talloc(mem_ctx, struct named_mutex); + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + lp_ctx = loadparm_init_s3(result, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + DEBUG(0, ("loadparm_init_s3 failed\n")); + talloc_free(result); + return NULL; + } + + result->name = talloc_strdup(result, name); + if (result->name == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + fname = lock_path(talloc_tos(), "mutex.tdb"); + if (fname == NULL) { + TALLOC_FREE(result); + return NULL; + } + + result->tdb = tdb_wrap_open(result, fname, + lpcfg_tdb_hash_size(lp_ctx, fname), + lpcfg_tdb_flags(lp_ctx, + TDB_DEFAULT | + TDB_CLEAR_IF_FIRST | + TDB_INCOMPATIBLE_HASH), + O_RDWR|O_CREAT, 0600); + TALLOC_FREE(fname); + talloc_unlink(result, lp_ctx); + if (result->tdb == NULL) { + DEBUG(1, ("Could not open mutex.tdb: %s\n", + strerror(errno))); + TALLOC_FREE(result); + return NULL; + } + + if (tdb_lock_bystring_with_timeout(result->tdb->tdb, name, + timeout) != 0) { + DEBUG(1, ("Could not get the lock for %s\n", name)); + TALLOC_FREE(result); + return NULL; + } + + talloc_set_destructor(result, unlock_named_mutex); + return result; +} diff --git a/source3/lib/serverid.c b/source3/lib/serverid.c new file mode 100644 index 0000000..70739c7 --- /dev/null +++ b/source3/lib/serverid.c @@ -0,0 +1,62 @@ +/* + Unix SMB/CIFS implementation. + Implementation of a reliable server_exists() + Copyright (C) Volker Lendecke 2010 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util/server_id.h" +#include "serverid.h" +#include "lib/param/param.h" +#include "ctdbd_conn.h" +#include "lib/messages_ctdb.h" +#include "lib/messaging/messages_dgm.h" + +static bool serverid_exists_local(const struct server_id *id) +{ + bool exists = process_exists_by_pid(id->pid); + uint64_t unique; + int ret; + + if (!exists) { + return false; + } + + if (id->unique_id == SERVERID_UNIQUE_ID_NOT_TO_VERIFY) { + return true; + } + + ret = messaging_dgm_get_unique(id->pid, &unique); + if (ret != 0) { + return false; + } + + return (unique == id->unique_id); +} + +bool serverid_exists(const struct server_id *id) +{ + if (procid_is_local(id)) { + return serverid_exists_local(id); + } + + if (lp_clustering()) { + return ctdbd_process_exists(messaging_ctdb_connection(), + id->vnn, id->pid, id->unique_id); + } + + return false; +} diff --git a/source3/lib/sessionid_tdb.c b/source3/lib/sessionid_tdb.c new file mode 100644 index 0000000..2376fd4 --- /dev/null +++ b/source3/lib/sessionid_tdb.c @@ -0,0 +1,95 @@ +/* + Unix SMB/CIFS implementation. + Low-level sessionid.tdb access functions + Copyright (C) Volker Lendecke 2010 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "session.h" +#include "util_tdb.h" +#include "smbd/globals.h" + +struct sessionid_traverse_read_state { + int (*fn)(const char *key, struct sessionid *session, + void *private_data); + void *private_data; +}; + +static int sessionid_traverse_read_fn(struct smbXsrv_session_global0 *global, + void *private_data) +{ + struct sessionid_traverse_read_state *state = + (struct sessionid_traverse_read_state *)private_data; + struct auth_session_info *session_info = global->auth_session_info; + struct sessionid session = { + .uid = -1, + .gid = -1, + .id_num = global->session_global_id, + .connect_start = nt_time_to_unix(global->creation_time), + .pid = global->channels[0].server_id, + .connection_dialect = global->connection_dialect, + .global = global, + }; + + if (session_info != NULL) { + session.uid = session_info->unix_token->uid; + session.gid = session_info->unix_token->gid; + strncpy(session.username, + session_info->unix_info->unix_name, + sizeof(fstring)-1); + } + + strncpy(session.remote_machine, + global->channels[0].remote_name, + sizeof(fstring)-1); + strncpy(session.hostname, + global->channels[0].remote_address, + sizeof(fstring)-1); + strncpy(session.netbios_name, + global->channels[0].remote_name, + sizeof(fstring)-1); + snprintf(session.id_str, sizeof(fstring)-1, + "smb/%u", global->session_global_id); + strncpy(session.ip_addr_str, + global->channels[0].remote_address, + sizeof(fstring)-1); + + session.encryption_flags = global->encryption_flags; + session.cipher = global->channels[0].encryption_cipher; + session.signing_flags = global->signing_flags; + session.signing = global->channels[0].signing_algo; + + return state->fn(NULL, &session, state->private_data); +} + +NTSTATUS sessionid_traverse_read(int (*fn)(const char *key, + struct sessionid *session, + void *private_data), + void *private_data) +{ + struct sessionid_traverse_read_state state; + NTSTATUS status; + + state.fn = fn; + state.private_data = private_data; + status = smbXsrv_session_global_traverse(sessionid_traverse_read_fn, + &state); + + return status; +} diff --git a/source3/lib/sharesec.c b/source3/lib/sharesec.c new file mode 100644 index 0000000..fbb2d47 --- /dev/null +++ b/source3/lib/sharesec.c @@ -0,0 +1,588 @@ +/* + * Unix SMB/Netbios implementation. + * SEC_DESC handling functions + * Copyright (C) Jeremy R. Allison 1995-2003. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "../libcli/security/security.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "util_tdb.h" +#include "libcli/util/ntstatus.h" + +/******************************************************************* + Create the share security tdb. + ********************************************************************/ + +static struct db_context *share_db; /* used for share security descriptors */ +#define SHARE_DATABASE_VERSION_V1 1 +#define SHARE_DATABASE_VERSION_V2 2 /* version id in little endian. */ +#define SHARE_DATABASE_VERSION_V3 3 /* canonicalized sharenames as lower case */ + +#define SHARE_SECURITY_DB_KEY_PREFIX_STR "SECDESC/" +/* Map generic permissions to file object specific permissions */ + +extern const struct generic_mapping file_generic_mapping; + +static int delete_fn(struct db_record *rec, void *priv) +{ + dbwrap_record_delete(rec); + return 0; +} + +/***************************************************** + Looking for keys of the form: SHARE_SECURITY_DB_KEY_PREFIX_STR + "non lower case str". + If we find one re-write it into a canonical case form. +*****************************************************/ + +static int upgrade_v2_to_v3(struct db_record *rec, void *priv) +{ + size_t prefix_len = strlen(SHARE_SECURITY_DB_KEY_PREFIX_STR); + const char *servicename = NULL; + char *c_servicename = NULL; + char *newkey = NULL; + bool *p_upgrade_ok = (bool *)priv; + NTSTATUS status; + TDB_DATA key; + TDB_DATA value; + + key = dbwrap_record_get_key(rec); + + /* Is there space for a one character sharename ? */ + if (key.dsize <= prefix_len+2) { + return 0; + } + + /* Does it start with the share key prefix ? */ + if (memcmp(key.dptr, SHARE_SECURITY_DB_KEY_PREFIX_STR, + prefix_len) != 0) { + return 0; + } + + /* Is it a null terminated string as a key ? */ + if (key.dptr[key.dsize-1] != '\0') { + return 0; + } + + /* Bytes after the prefix are the sharename string. */ + servicename = (char *)&key.dptr[prefix_len]; + c_servicename = canonicalize_servicename(talloc_tos(), servicename); + if (!c_servicename) { + smb_panic("out of memory upgrading share security db from v2 -> v3"); + } + + if (strcmp(servicename, c_servicename) == 0) { + /* Old and new names match. No canonicalization needed. */ + TALLOC_FREE(c_servicename); + return 0; + } + + /* Oops. Need to canonicalize name, delete old then store new. */ + status = dbwrap_record_delete(rec); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("upgrade_v2_to_v3: Failed to delete secdesc for " + "%s: %s\n", (const char *)key.dptr, + nt_errstr(status))); + TALLOC_FREE(c_servicename); + *p_upgrade_ok = false; + return -1; + } else { + DEBUG(10, ("upgrade_v2_to_v3: deleted secdesc for " + "%s\n", (const char *)key.dptr)); + } + + if (!(newkey = talloc_asprintf(talloc_tos(), + SHARE_SECURITY_DB_KEY_PREFIX_STR "%s", + c_servicename))) { + smb_panic("out of memory upgrading share security db from v2 -> v3"); + } + + value = dbwrap_record_get_value(rec); + status = dbwrap_store(share_db, + string_term_tdb_data(newkey), + value, + TDB_REPLACE); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("upgrade_v2_to_v3: Failed to store secdesc for " + "%s: %s\n", c_servicename, nt_errstr(status))); + TALLOC_FREE(c_servicename); + TALLOC_FREE(newkey); + *p_upgrade_ok = false; + return -1; + } else { + DEBUG(10, ("upgrade_v2_to_v3: stored secdesc for " + "%s\n", newkey )); + } + + TALLOC_FREE(newkey); + TALLOC_FREE(c_servicename); + + return 0; +} + +NTSTATUS share_info_db_init(void) +{ + const char *vstring = "INFO/version"; + int32_t vers_id = 0; + bool upgrade_ok = true; + NTSTATUS status; + char *db_path; + + if (share_db != NULL) { + return NT_STATUS_OK; + } + + db_path = state_path(talloc_tos(), "share_info.tdb"); + if (db_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + + share_db = db_open(NULL, db_path, 0, + TDB_DEFAULT, O_RDWR|O_CREAT, 0600, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (share_db == NULL) { + DEBUG(0,("Failed to open share info database %s (%s)\n", + db_path, strerror(errno))); + TALLOC_FREE(db_path); + return map_nt_error_from_unix_common(errno); + } + TALLOC_FREE(db_path); + + status = dbwrap_fetch_int32_bystring(share_db, vstring, &vers_id); + if (!NT_STATUS_IS_OK(status)) { + vers_id = 0; + } + + if (vers_id == SHARE_DATABASE_VERSION_V3) { + return NT_STATUS_OK; + } + + if (dbwrap_transaction_start(share_db) != 0) { + DEBUG(0, ("transaction_start failed\n")); + TALLOC_FREE(share_db); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + status = dbwrap_fetch_int32_bystring(share_db, vstring, &vers_id); + if (!NT_STATUS_IS_OK(status)) { + vers_id = 0; + } + + if (vers_id == SHARE_DATABASE_VERSION_V3) { + /* + * Race condition + */ + if (dbwrap_transaction_cancel(share_db)) { + smb_panic("transaction_cancel failed"); + } + return NT_STATUS_OK; + } + + /* Move to at least V2. */ + + /* Cope with byte-reversed older versions of the db. */ + if ((vers_id == SHARE_DATABASE_VERSION_V1) || (IREV(vers_id) == SHARE_DATABASE_VERSION_V1)) { + /* Written on a bigendian machine with old fetch_int code. Save as le. */ + + status = dbwrap_store_int32_bystring( + share_db, vstring, SHARE_DATABASE_VERSION_V2); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dbwrap_store_int32 failed: %s\n", + nt_errstr(status))); + goto cancel; + } + vers_id = SHARE_DATABASE_VERSION_V2; + } + + if (vers_id != SHARE_DATABASE_VERSION_V2) { + status = dbwrap_traverse(share_db, delete_fn, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("traverse failed\n")); + goto cancel; + } + status = dbwrap_store_int32_bystring( + share_db, vstring, SHARE_DATABASE_VERSION_V2); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dbwrap_store_int32 failed: %s\n", + nt_errstr(status))); + goto cancel; + } + } + + /* Finally upgrade to version 3, with canonicalized sharenames. */ + + status = dbwrap_traverse(share_db, upgrade_v2_to_v3, &upgrade_ok, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("traverse failed\n")); + goto cancel; + } + if (!upgrade_ok) { + DBG_ERR("upgrade failed.\n"); + status = NT_STATUS_INTERNAL_ERROR; + goto cancel; + } + + status = dbwrap_store_int32_bystring( + share_db, vstring, SHARE_DATABASE_VERSION_V3); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dbwrap_store_int32 failed: %s\n", + nt_errstr(status))); + goto cancel; + } + + if (dbwrap_transaction_commit(share_db) != 0) { + DEBUG(0, ("transaction_commit failed\n")); + return NT_STATUS_INTERNAL_ERROR; + } + + return NT_STATUS_OK; + + cancel: + if (dbwrap_transaction_cancel(share_db)) { + smb_panic("transaction_cancel failed"); + } + + return status; +} + +/******************************************************************* + Fake up a Everyone, default access as a default. + def_access is a GENERIC_XXX access mode. + ********************************************************************/ + +static struct security_descriptor *get_share_security_default(TALLOC_CTX *ctx, + size_t *psize, + uint32_t def_access) +{ + uint32_t sa; + struct security_ace ace; + struct security_acl *psa = NULL; + struct security_descriptor *psd = NULL; + uint32_t spec_access = def_access; + + se_map_generic(&spec_access, &file_generic_mapping); + + sa = (def_access | spec_access ); + init_sec_ace(&ace, &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0); + + if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &ace)) != NULL) { + psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, + psa, psize); + } + + if (!psd) { + DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n")); + return NULL; + } + + return psd; +} + +/******************************************************************* + Pull a security descriptor from the share tdb. + ********************************************************************/ + +struct security_descriptor *get_share_security( TALLOC_CTX *ctx, const char *servicename, + size_t *psize) +{ + char *key; + struct security_descriptor *psd = NULL; + TDB_DATA data; + char *c_servicename = canonicalize_servicename(talloc_tos(), servicename); + NTSTATUS status; + + if (!c_servicename) { + return NULL; + } + + status = share_info_db_init(); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(c_servicename); + return NULL; + } + + if (!(key = talloc_asprintf(ctx, SHARE_SECURITY_DB_KEY_PREFIX_STR "%s", c_servicename))) { + TALLOC_FREE(c_servicename); + DEBUG(0, ("talloc_asprintf failed\n")); + return NULL; + } + + TALLOC_FREE(c_servicename); + + status = dbwrap_fetch_bystring(share_db, talloc_tos(), key, &data); + + TALLOC_FREE(key); + + if (!NT_STATUS_IS_OK(status)) { + return get_share_security_default(ctx, psize, + SEC_RIGHTS_DIR_ALL); + } + + status = unmarshall_sec_desc(ctx, data.dptr, data.dsize, &psd); + + TALLOC_FREE(data.dptr); + + if (!NT_STATUS_IS_OK(status)) { + return get_share_security_default(ctx, psize, + SEC_RIGHTS_DIR_ALL); + } + + if (psd) { + *psize = ndr_size_security_descriptor(psd, 0); + } else { + return get_share_security_default(ctx, psize, + SEC_RIGHTS_DIR_ALL); + } + + return psd; +} + +/******************************************************************* + Store a security descriptor in the share db. + ********************************************************************/ + +NTSTATUS set_share_security(const char *share_name, + struct security_descriptor *psd) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *key; + TDB_DATA blob; + NTSTATUS status; + char *c_share_name = canonicalize_servicename(frame, share_name); + + if (c_share_name == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + status = share_info_db_init(); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = marshall_sec_desc(frame, psd, &blob.dptr, &blob.dsize); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("marshall_sec_desc failed: %s\n", + nt_errstr(status))); + goto out; + } + + if (!(key = talloc_asprintf(frame, SHARE_SECURITY_DB_KEY_PREFIX_STR "%s", c_share_name))) { + DEBUG(0, ("talloc_asprintf failed\n")); + status = NT_STATUS_NO_MEMORY; + goto out; + } + + status = dbwrap_trans_store(share_db, string_term_tdb_data(key), blob, + TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("set_share_security: Failed to store secdesc for " + "%s: %s\n", share_name, nt_errstr(status))); + goto out; + } + + DEBUG(5,("set_share_security: stored secdesc for %s\n", share_name )); + status = NT_STATUS_OK; + + out: + TALLOC_FREE(frame); + return status; +} + +/******************************************************************* + Delete a security descriptor. +********************************************************************/ + +NTSTATUS delete_share_security(const char *servicename) +{ + TDB_DATA kbuf; + char *key; + NTSTATUS status; + char *c_servicename = canonicalize_servicename(talloc_tos(), servicename); + + if (c_servicename == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = share_info_db_init(); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(c_servicename); + return status; + } + + if (!(key = talloc_asprintf(talloc_tos(), SHARE_SECURITY_DB_KEY_PREFIX_STR "%s", + c_servicename))) { + TALLOC_FREE(c_servicename); + return NT_STATUS_NO_MEMORY; + } + kbuf = string_term_tdb_data(key); + + status = dbwrap_trans_delete(share_db, kbuf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("delete_share_security: Failed to delete entry for " + "share %s: %s\n", c_servicename, nt_errstr(status))); + TALLOC_FREE(c_servicename); + return status; + } + + TALLOC_FREE(c_servicename); + return NT_STATUS_OK; +} + +/******************************************************************* + Can this user access with share with the required permissions ? +********************************************************************/ + +bool share_access_check(const struct security_token *token, + const char *sharename, + uint32_t desired_access, + uint32_t *pgranted) +{ + uint32_t granted; + NTSTATUS status; + struct security_descriptor *psd = NULL; + size_t sd_size; + + psd = get_share_security(talloc_tos(), sharename, &sd_size); + + if (!psd) { + if (pgranted != NULL) { + *pgranted = desired_access; + } + return false; + } + + status = se_file_access_check(psd, token, true, desired_access, &granted); + + TALLOC_FREE(psd); + + if (pgranted != NULL) { + *pgranted = granted; + } + + return NT_STATUS_IS_OK(status); +} + +/*************************************************************************** + Parse the contents of an acl string from a usershare file. +***************************************************************************/ + +bool parse_usershare_acl(TALLOC_CTX *ctx, const char *acl_str, struct security_descriptor **ppsd) +{ + size_t s_size = 0; + const char *pacl = acl_str; + int num_aces = 0; + struct security_ace *ace_list = NULL; + struct security_acl *psa = NULL; + struct security_descriptor *psd = NULL; + size_t sd_size = 0; + int i; + + *ppsd = NULL; + + /* If the acl string is blank return "Everyone:R" */ + if (!*acl_str) { + struct security_descriptor *default_psd = get_share_security_default(ctx, &s_size, GENERIC_READ_ACCESS); + if (!default_psd) { + return False; + } + *ppsd = default_psd; + return True; + } + + num_aces = 1; + + /* Add the number of ',' characters to get the number of aces. */ + num_aces += count_chars(pacl,','); + + ace_list = talloc_array(ctx, struct security_ace, num_aces); + if (!ace_list) { + return False; + } + + for (i = 0; i < num_aces; i++) { + uint32_t sa; + uint32_t g_access; + uint32_t s_access; + struct dom_sid sid; + char *sidstr; + enum security_ace_type type = SEC_ACE_TYPE_ACCESS_ALLOWED; + + if (!next_token_talloc(ctx, &pacl, &sidstr, ":")) { + DEBUG(0,("parse_usershare_acl: malformed usershare acl looking " + "for ':' in string '%s'\n", pacl)); + return False; + } + + if (!string_to_sid(&sid, sidstr)) { + DEBUG(0,("parse_usershare_acl: failed to convert %s to sid.\n", + sidstr )); + return False; + } + + switch (*pacl) { + case 'F': /* Full Control, ie. R+W */ + case 'f': /* Full Control, ie. R+W */ + s_access = g_access = GENERIC_ALL_ACCESS; + break; + case 'R': /* Read only. */ + case 'r': /* Read only. */ + s_access = g_access = GENERIC_READ_ACCESS; + break; + case 'D': /* Deny all to this SID. */ + case 'd': /* Deny all to this SID. */ + type = SEC_ACE_TYPE_ACCESS_DENIED; + s_access = g_access = GENERIC_ALL_ACCESS; + break; + default: + DEBUG(0,("parse_usershare_acl: unknown acl type at %s.\n", + pacl )); + return False; + } + + pacl++; + if (*pacl && *pacl != ',') { + DEBUG(0,("parse_usershare_acl: bad acl string at %s.\n", + pacl )); + return False; + } + pacl++; /* Go past any ',' */ + + se_map_generic(&s_access, &file_generic_mapping); + sa = (g_access | s_access); + init_sec_ace(&ace_list[i], &sid, type, sa, 0); + } + + if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, num_aces, ace_list)) != NULL) { + psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, + psa, &sd_size); + } + + if (!psd) { + DEBUG(0,("parse_usershare_acl: Failed to make SEC_DESC.\n")); + return False; + } + + *ppsd = psd; + return True; +} diff --git a/source3/lib/smbconf/pys3smbconf.c b/source3/lib/smbconf/pys3smbconf.c new file mode 100644 index 0000000..09d4881 --- /dev/null +++ b/source3/lib/smbconf/pys3smbconf.c @@ -0,0 +1,212 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library - Python bindings + * + * Copyright (C) John Mulligan <phlogistonjohn@asynchrono.us> 2022 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "lib/replace/system/python.h" +#include "includes.h" +#include "python/py3compat.h" + +#include "lib/smbconf/smbconf.h" +#include "source3/lib/smbconf/smbconf_reg.h" +#include "source3/lib/smbconf/smbconf_init.h" +#include "lib/smbconf/pysmbconf.h" + +/* + * The name of the other, general, smbconf module that implements + * the common type. We import this module by name to access + * its methods by the python API. + */ +#define SMBCONF_MOD "samba.smbconf" + +/* + * Return a new but uninitialized SMBConf python object via + * the python API of the (other) smbconf module. + */ +static PyObject *py_new_SMBConf(PyObject * smbconf_mod) +{ + PyObject *obj = NULL; + PyObject *method = PyObject_GetAttrString(smbconf_mod, "SMBConf"); + if (method == NULL) { + return NULL; + } + + obj = PyObject_CallObject(method, NULL); + Py_CLEAR(method); + return obj; +} + +/* + * Raise a new SMBConfError python exception given a error code. + * This uses the python API of the (other) smbconf module. + */ +static PyObject *py_raise_SMBConfError(PyObject * smbconf_mod, sbcErr err) +{ + PyObject *obj = NULL; + PyObject *method = + PyObject_GetAttrString(smbconf_mod, "_smbconf_error"); + if (method == NULL) { + return NULL; + } + + obj = PyObject_CallFunction(method, "i", err); + Py_CLEAR(method); + return obj; +} + +static PyObject *py_init_reg(PyObject * module, PyObject * args) +{ + PyObject *obj = NULL; + PyObject *smbconf_mod = NULL; + char *path = NULL; + struct smbconf_ctx *conf_ctx = NULL; + TALLOC_CTX *mem_ctx = NULL; + sbcErr err; + + /* + * The path here is _NOT_ the path to a file in the file + * system. It's a special HK registry thingy. But passing + * a null string to smbconf_init_reg populates it with + * a functional default value. So we allow the python + * caller to pass None and convert to NULL. + */ + if (!PyArg_ParseTuple(args, "z", &path)) { + return NULL; + } + + smbconf_mod = PyImport_ImportModule(SMBCONF_MOD); + if (smbconf_mod == NULL) { + return NULL; + } + + obj = py_new_SMBConf(smbconf_mod); + if (obj == NULL) { + Py_CLEAR(smbconf_mod); + return NULL; + } + + mem_ctx = ((py_SMBConf_Object *) obj)->mem_ctx; + err = smbconf_init_reg(mem_ctx, &conf_ctx, path); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(smbconf_mod, err); + Py_CLEAR(obj); + Py_CLEAR(smbconf_mod); + return NULL; + } + ((py_SMBConf_Object *) obj)->conf_ctx = conf_ctx; + + Py_DECREF(smbconf_mod); + return obj; +} + +static PyObject *py_init_str(PyObject * module, PyObject * args) +{ + PyObject *obj = NULL; + PyObject *smbconf_mod = NULL; + char *path = NULL; + struct smbconf_ctx *conf_ctx = NULL; + TALLOC_CTX *mem_ctx = NULL; + sbcErr err; + + if (!PyArg_ParseTuple(args, "s", &path)) { + return NULL; + } + + smbconf_mod = PyImport_ImportModule(SMBCONF_MOD); + if (smbconf_mod == NULL) { + return NULL; + } + + obj = py_new_SMBConf(smbconf_mod); + if (obj == NULL) { + Py_CLEAR(smbconf_mod); + return NULL; + } + + mem_ctx = ((py_SMBConf_Object *) obj)->mem_ctx; + err = smbconf_init(mem_ctx, &conf_ctx, path); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(smbconf_mod, err); + Py_CLEAR(obj); + Py_CLEAR(smbconf_mod); + return NULL; + } + ((py_SMBConf_Object *) obj)->conf_ctx = conf_ctx; + + Py_DECREF(smbconf_mod); + return obj; +} + +PyDoc_STRVAR(py_init_reg_doc, +"Return an SMBConf object using the registry based configuration.\n" +"The path argument provided must either be None to use the\n" +"default path or a path within the registry. It must start with\n" +"the characters 'HK' if provided. It is *not* a path to a\n" +"file or database in the file system.\n"); + +PyDoc_STRVAR(py_init_str_doc, +"Return an SMBConf object opened using one of the backends\n" +"supported by Samba.\n" +"The provided string argument must be in the form \"backend:path\".\n" +"The backend portion is to be the name of a supported backend\n" +"such as 'file', or 'registry'. The path following the colon is\n" +"backend specific. In the case of the file backend this is the path\n" +"to a configuration file.\n" +"Examples:\n" +" c1 = samba.samba3.smbconfig.init(\"file:/tmp/smb.conf\")\n" +" c2 = samba.samba3.smbconfig.init(\"registry:\")\n"); +/* + * The major advantage of having this `init` function in the + * python wrapper is that if a backend is added without + * explicit changes to the python wrapper libs, it should still + * be able to access that backend through the general init + * function. The value add is not huge but more like insurance. + */ + +static PyMethodDef pys3smbconf_methods[] = { + { "init_reg", (PyCFunction) py_init_reg, METH_VARARGS, + py_init_reg_doc }, + { "init", (PyCFunction) py_init_str, METH_VARARGS, + py_init_str_doc }, + { 0 }, +}; + +PyDoc_STRVAR(py_s3smbconf_doc, +"The s3smbconf module is a wrapper for Samba's 'source3' smbconf library.\n" +"This library provides functions to use configuration backends that are\n" +"specific to the file server suite of components within Samba.\n" +"This includes functions to access the registry backend of the\n" +"smbconf subsystem. This backend is read-write.\n"); + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "smbconf", + .m_doc = py_s3smbconf_doc, + .m_size = -1, + .m_methods = pys3smbconf_methods, +}; + +MODULE_INIT_FUNC(smbconf) +{ + PyObject *m = PyModule_Create(&moduledef); + if (m == NULL) { + return NULL; + } + + return m; +} diff --git a/source3/lib/smbconf/smbconf_init.c b/source3/lib/smbconf/smbconf_init.c new file mode 100644 index 0000000..e6bf6d8 --- /dev/null +++ b/source3/lib/smbconf/smbconf_init.c @@ -0,0 +1,96 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library, init dispatcher + * Copyright (C) Michael Adam 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/smbconf/smbconf_private.h" +#include "lib/smbconf/smbconf_txt.h" +#include "lib/smbconf/smbconf_reg.h" +#include "lib/smbconf/smbconf_init.h" + +/** + * smbconf initialization dispatcher + * + * this takes a configuration source in the form of + * backend:path and calls the appropriate backend + * init function with the path argument + * + * known backends: + * - "registry" or "reg" + * - "txt" or "file" + */ +sbcErr smbconf_init(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx, + const char *source) +{ + sbcErr err; + char *backend = NULL; + char *path = NULL; + char *sep; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if (conf_ctx == NULL) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + if ((source == NULL) || (*source == '\0')) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + backend = talloc_strdup(tmp_ctx, source); + if (backend == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + sep = strchr(backend, ':'); + if (sep != NULL) { + *sep = '\0'; + path = sep + 1; + if (strlen(path) == 0) { + path = NULL; + } + } + + if (strequal(backend, "registry") || strequal(backend, "reg")) { + err = smbconf_init_reg(mem_ctx, conf_ctx, path); + } else if (strequal(backend, "file") || strequal(backend, "txt")) { + err = smbconf_init_txt(mem_ctx, conf_ctx, path); + } else if (sep == NULL) { + /* + * If no separator was given in the source, and the string is + * not a known backend, assume file backend and use the source + * string as a path argument. + */ + err = smbconf_init_txt(mem_ctx, conf_ctx, backend); + } else { + /* + * Separator was specified but this is not a known backend. + * As a last resort, try to interpret the original source + * string as a file name that contains a ":" sign. + * This may occur with an include directive like this: + * 'include = /path/to/file.%T' + */ + err = smbconf_init_txt(mem_ctx, conf_ctx, source); + } + +done: + talloc_free(tmp_ctx); + return err; +} diff --git a/source3/lib/smbconf/smbconf_init.h b/source3/lib/smbconf/smbconf_init.h new file mode 100644 index 0000000..11ddb0c --- /dev/null +++ b/source3/lib/smbconf/smbconf_init.h @@ -0,0 +1,32 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library + * Copyright (C) Michael Adam 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMBCONF_INIT_H__ +#define __LIBSMBCONF_INIT_H__ + +struct smbconf_ctx; + +/** + * initialization dispatcher function. + * takes source string in the form of "backend:path" + */ +sbcErr smbconf_init(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx, + const char *source); + +#endif /* _LIBSMBCONF_INIT_H_ */ diff --git a/source3/lib/smbconf/smbconf_reg.c b/source3/lib/smbconf/smbconf_reg.c new file mode 100644 index 0000000..fc8aa62 --- /dev/null +++ b/source3/lib/smbconf/smbconf_reg.c @@ -0,0 +1,1246 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library, registry backend + * Copyright (C) Michael Adam 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/smbconf/smbconf_private.h" +#include "registry.h" +#include "registry/reg_api.h" +#include "registry/reg_backend_db.h" +#include "registry/reg_util_token.h" +#include "registry/reg_api_util.h" +#include "registry/reg_init_smbconf.h" +#include "lib/smbconf/smbconf_init.h" +#include "lib/smbconf/smbconf_reg.h" +#include "../libcli/registry/util_reg.h" + +#define INCLUDES_VALNAME "includes" + +struct reg_private_data { + struct registry_key *base_key; + bool open; /* did _we_ open the registry? */ +}; + +/********************************************************************** + * + * helper functions + * + **********************************************************************/ + +/** + * a convenience helper to cast the private data structure + */ +static struct reg_private_data *rpd(struct smbconf_ctx *ctx) +{ + return (struct reg_private_data *)(ctx->data); +} + +/** + * Check whether a given parameter name is valid in the + * smbconf registry backend. + */ +bool smbconf_reg_parameter_is_valid(const char *param_name) +{ + /* hard code the list of forbidden names here for now */ + const char *forbidden_names[] = { + "state directory", + "lock directory", + "lock dir", + "config backend", + "include", + /* + * "includes" has a special meaning internally. + * It is currently not necessary to list it here since it is + * not a valid parameter. But for clarity and safety, we keep + * it for now. + */ + INCLUDES_VALNAME, + NULL + }; + const char **forbidden = NULL; + + if (!lp_parameter_is_valid(param_name)) { + return false; + } + + for (forbidden = forbidden_names; *forbidden != NULL; forbidden++) { + if (strwicmp(param_name, *forbidden) == 0) { + return false; + } + } + + return true; +} + +/** + * Open a subkey of the base key (i.e a service) + */ +static sbcErr smbconf_reg_open_service_key(TALLOC_CTX *mem_ctx, + struct smbconf_ctx *ctx, + const char *servicename, + uint32_t desired_access, + struct registry_key **key) +{ + WERROR werr; + + if (servicename == NULL) { + *key = rpd(ctx)->base_key; + return SBC_ERR_OK; + } + werr = reg_openkey(mem_ctx, rpd(ctx)->base_key, servicename, + desired_access, key); + if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { + return SBC_ERR_NO_SUCH_SERVICE; + } + if (!W_ERROR_IS_OK(werr)) { + return SBC_ERR_NOMEM; + } + + return SBC_ERR_OK; +} + +/** + * check if a value exists in a given registry key + */ +static bool smbconf_value_exists(struct registry_key *key, const char *param) +{ + bool ret = false; + WERROR werr; + TALLOC_CTX *ctx = talloc_stackframe(); + struct registry_value *value = NULL; + + werr = reg_queryvalue(ctx, key, param, &value); + if (W_ERROR_IS_OK(werr)) { + ret = true; + } + + talloc_free(ctx); + return ret; +} + +/** + * create a subkey of the base key (i.e. a service...) + */ +static sbcErr smbconf_reg_create_service_key(TALLOC_CTX *mem_ctx, + struct smbconf_ctx *ctx, + const char * subkeyname, + struct registry_key **newkey) +{ + WERROR werr; + sbcErr err = SBC_ERR_OK; + TALLOC_CTX *create_ctx; + enum winreg_CreateAction action = REG_ACTION_NONE; + + /* create a new talloc ctx for creation. it will hold + * the intermediate parent key (SMBCONF) for creation + * and will be destroyed when leaving this function... */ + create_ctx = talloc_stackframe(); + + werr = reg_createkey(mem_ctx, rpd(ctx)->base_key, subkeyname, + REG_KEY_WRITE, newkey, &action); + if (W_ERROR_IS_OK(werr) && (action != REG_CREATED_NEW_KEY)) { + DEBUG(10, ("Key '%s' already exists.\n", subkeyname)); + err = SBC_ERR_FILE_EXISTS; + } + if (!W_ERROR_IS_OK(werr)) { + DEBUG(5, ("Error creating key %s: %s\n", + subkeyname, win_errstr(werr))); + err = SBC_ERR_UNKNOWN_FAILURE; + } + + talloc_free(create_ctx); + return err; +} + +/** + * add a value to a key. + */ +static sbcErr smbconf_reg_set_value(struct registry_key *key, + const char *valname, + const char *valstr) +{ + struct registry_value val; + WERROR werr; + sbcErr err; + char *subkeyname; + const char *canon_valname; + const char *canon_valstr; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if (!lp_parameter_is_valid(valname)) { + DEBUG(5, ("Invalid parameter '%s' given.\n", valname)); + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + if (!smbconf_reg_parameter_is_valid(valname)) { + DEBUG(5, ("Parameter '%s' not allowed in registry.\n", + valname)); + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + subkeyname = strrchr_m(key->key->name, '\\'); + if ((subkeyname == NULL) || (*(subkeyname +1) == '\0')) { + DEBUG(5, ("Invalid registry key '%s' given as " + "smbconf section.\n", key->key->name)); + err = SBC_ERR_INVALID_PARAM; + goto done; + } + subkeyname++; + if (!strequal(subkeyname, GLOBAL_NAME) && + lp_parameter_is_global(valname)) + { + DEBUG(5, ("Global parameter '%s' not allowed in " + "service definition ('%s').\n", valname, + subkeyname)); + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + if (!lp_canonicalize_parameter_with_value(valname, valstr, + &canon_valname, + &canon_valstr)) + { + /* + * We already know the parameter name is valid. + * So the value must be invalid. + */ + DEBUG(5, ("invalid value '%s' given for parameter '%s'\n", + valstr, valname)); + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + ZERO_STRUCT(val); + + val.type = REG_SZ; + if (!push_reg_sz(tmp_ctx, &val.data, canon_valstr)) { + err = SBC_ERR_NOMEM; + goto done; + } + + werr = reg_setvalue(key, canon_valname, &val); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(5, ("Error adding value '%s' to " + "key '%s': %s\n", + canon_valname, key->key->name, win_errstr(werr))); + err = SBC_ERR_NOMEM; + goto done; + } + + err = SBC_ERR_OK; +done: + talloc_free(tmp_ctx); + return err; +} + +static sbcErr smbconf_reg_set_multi_sz_value(struct registry_key *key, + const char *valname, + const uint32_t num_strings, + const char **strings) +{ + WERROR werr; + sbcErr err = SBC_ERR_OK; + struct registry_value *value; + uint32_t count; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + const char **array; + + if (strings == NULL) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + array = talloc_zero_array(tmp_ctx, const char *, num_strings + 1); + if (array == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + value = talloc_zero(tmp_ctx, struct registry_value); + if (value == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + value->type = REG_MULTI_SZ; + + for (count = 0; count < num_strings; count++) { + array[count] = talloc_strdup(value, strings[count]); + if (array[count] == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + } + + if (!push_reg_multi_sz(value, &value->data, array)) { + err = SBC_ERR_NOMEM; + goto done; + } + + werr = reg_setvalue(key, valname, value); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(5, ("Error adding value '%s' to key '%s': %s\n", + valname, key->key->name, win_errstr(werr))); + err = SBC_ERR_ACCESS_DENIED; + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * format a registry_value into a string. + * + * This is intended to be used for smbconf registry values, + * which are ar stored as REG_SZ values, so the incomplete + * handling should be ok. + */ +static char *smbconf_format_registry_value(TALLOC_CTX *mem_ctx, + struct registry_value *value) +{ + char *result = NULL; + + /* alternatively, create a new talloc context? */ + if (mem_ctx == NULL) { + return result; + } + + switch (value->type) { + case REG_DWORD: + if (value->data.length >= 4) { + uint32_t v = IVAL(value->data.data, 0); + result = talloc_asprintf(mem_ctx, "%d", v); + } + break; + case REG_SZ: + case REG_EXPAND_SZ: { + const char *s; + if (!pull_reg_sz(mem_ctx, &value->data, &s)) { + break; + } + result = talloc_strdup(mem_ctx, s); + break; + } + case REG_MULTI_SZ: { + uint32_t j; + const char **a = NULL; + if (!pull_reg_multi_sz(mem_ctx, &value->data, &a)) { + break; + } + for (j = 0; a[j] != NULL; j++) { + result = talloc_asprintf(mem_ctx, "%s\"%s\" ", + result ? result : "" , + a[j]); + if (result == NULL) { + break; + } + } + break; + } + case REG_BINARY: + result = talloc_asprintf(mem_ctx, "binary (%d bytes)", + (int)value->data.length); + break; + default: + result = talloc_asprintf(mem_ctx, "<unprintable>"); + break; + } + return result; +} + +static sbcErr smbconf_reg_get_includes_internal(TALLOC_CTX *mem_ctx, + struct registry_key *key, + uint32_t *num_includes, + char ***includes) +{ + WERROR werr; + sbcErr err; + uint32_t count; + struct registry_value *value = NULL; + char **tmp_includes = NULL; + const char **array = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if (!smbconf_value_exists(key, INCLUDES_VALNAME)) { + /* no includes */ + *num_includes = 0; + *includes = NULL; + err = SBC_ERR_OK; + goto done; + } + + werr = reg_queryvalue(tmp_ctx, key, INCLUDES_VALNAME, &value); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_ACCESS_DENIED; + goto done; + } + + if (value->type != REG_MULTI_SZ) { + /* wrong type -- ignore */ + err = SBC_ERR_OK; + goto done; + } + + if (!pull_reg_multi_sz(tmp_ctx, &value->data, &array)) { + err = SBC_ERR_NOMEM; + goto done; + } + + for (count = 0; array[count] != NULL; count++) { + err = smbconf_add_string_to_array(tmp_ctx, + &tmp_includes, + count, + array[count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + } + + if (count > 0) { + *includes = talloc_move(mem_ctx, &tmp_includes); + if (*includes == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + *num_includes = count; + } else { + *num_includes = 0; + *includes = NULL; + } + + err = SBC_ERR_OK; +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * Get the values of a key as a list of value names + * and a list of value strings (ordered) + */ +static sbcErr smbconf_reg_get_values(TALLOC_CTX *mem_ctx, + struct registry_key *key, + uint32_t *num_values, + char ***value_names, + char ***value_strings) +{ + TALLOC_CTX *tmp_ctx = NULL; + WERROR werr; + sbcErr err; + uint32_t count; + struct registry_value *valvalue = NULL; + char *valname = NULL; + uint32_t tmp_num_values = 0; + char **tmp_valnames = NULL; + char **tmp_valstrings = NULL; + uint32_t num_includes = 0; + char **includes = NULL; + + if ((num_values == NULL) || (value_names == NULL) || + (value_strings == NULL)) + { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + tmp_ctx = talloc_stackframe(); + + for (count = 0; + werr = reg_enumvalue(tmp_ctx, key, count, &valname, &valvalue), + W_ERROR_IS_OK(werr); + count++) + { + char *valstring; + + if (!smbconf_reg_parameter_is_valid(valname)) { + continue; + } + + err = smbconf_add_string_to_array(tmp_ctx, + &tmp_valnames, + tmp_num_values, valname); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + valstring = smbconf_format_registry_value(tmp_ctx, valvalue); + err = smbconf_add_string_to_array(tmp_ctx, &tmp_valstrings, + tmp_num_values, valstring); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + tmp_num_values++; + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + err = SBC_ERR_NOMEM; + goto done; + } + + /* now add the includes at the end */ + err = smbconf_reg_get_includes_internal(tmp_ctx, key, &num_includes, + &includes); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + for (count = 0; count < num_includes; count++) { + err = smbconf_add_string_to_array(tmp_ctx, &tmp_valnames, + tmp_num_values, "include"); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + err = smbconf_add_string_to_array(tmp_ctx, &tmp_valstrings, + tmp_num_values, + includes[count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + tmp_num_values++; + } + + *num_values = tmp_num_values; + if (tmp_num_values > 0) { + *value_names = talloc_move(mem_ctx, &tmp_valnames); + *value_strings = talloc_move(mem_ctx, &tmp_valstrings); + } else { + *value_names = NULL; + *value_strings = NULL; + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * delete all values from a key + */ +static sbcErr smbconf_reg_delete_values(struct registry_key *key) +{ + WERROR werr; + sbcErr err; + char *valname; + struct registry_value *valvalue; + uint32_t count; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + for (count = 0; + werr = reg_enumvalue(mem_ctx, key, count, &valname, &valvalue), + W_ERROR_IS_OK(werr); + count++) + { + werr = reg_deletevalue(key, valname); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_ACCESS_DENIED; + goto done; + } + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + DEBUG(1, ("smbconf_reg_delete_values: " + "Error enumerating values of %s: %s\n", + key->key->name, + win_errstr(werr))); + err = SBC_ERR_ACCESS_DENIED; + goto done; + } + + err = SBC_ERR_OK; + +done: + talloc_free(mem_ctx); + return err; +} + +/********************************************************************** + * + * smbconf operations: registry implementations + * + **********************************************************************/ + +/** + * initialize the registry smbconf backend + */ +static sbcErr smbconf_reg_init(struct smbconf_ctx *ctx, const char *path) +{ + WERROR werr; + sbcErr err; + struct security_token *token; + + if (path == NULL) { + path = KEY_SMBCONF; + } + ctx->path = talloc_strdup(ctx, path); + if (ctx->path == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + ctx->data = talloc_zero(ctx, struct reg_private_data); + + werr = ntstatus_to_werror(registry_create_admin_token(ctx, &token)); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(1, ("Error creating admin token\n")); + err = SBC_ERR_UNKNOWN_FAILURE; + goto done; + } + rpd(ctx)->open = false; + + werr = registry_init_smbconf(path); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_BADFILE; + goto done; + } + + err = ctx->ops->open_conf(ctx); + if (!SBC_ERROR_IS_OK(err)) { + DEBUG(1, ("Error opening the registry.\n")); + goto done; + } + + werr = reg_open_path(ctx, ctx->path, + KEY_ENUMERATE_SUB_KEYS | REG_KEY_WRITE, + token, &rpd(ctx)->base_key); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_UNKNOWN_FAILURE; + goto done; + } + +done: + return err; +} + +static int smbconf_reg_shutdown(struct smbconf_ctx *ctx) +{ + return ctx->ops->close_conf(ctx); +} + +static bool smbconf_reg_requires_messaging(struct smbconf_ctx *ctx) +{ + if (lp_clustering() && lp_parm_bool(-1, "ctdb", "registry.tdb", true)) { + return true; + } + + return false; +} + +static bool smbconf_reg_is_writeable(struct smbconf_ctx *ctx) +{ + /* + * The backend has write support. + * + * TODO: add access checks whether the concrete + * config source is really writeable by the calling user. + */ + return true; +} + +static sbcErr smbconf_reg_open(struct smbconf_ctx *ctx) +{ + WERROR werr; + + if (rpd(ctx)->open) { + return SBC_ERR_OK; + } + + werr = regdb_open(); + if (!W_ERROR_IS_OK(werr)) { + return SBC_ERR_BADFILE; + } + + rpd(ctx)->open = true; + return SBC_ERR_OK; +} + +static int smbconf_reg_close(struct smbconf_ctx *ctx) +{ + int ret; + + if (!rpd(ctx)->open) { + return 0; + } + + ret = regdb_close(); + if (ret == 0) { + rpd(ctx)->open = false; + } + return ret; +} + +/** + * Get the change sequence number of the given service/parameter. + * service and parameter strings may be NULL. + */ +static void smbconf_reg_get_csn(struct smbconf_ctx *ctx, + struct smbconf_csn *csn, + const char *service, const char *param) +{ + if (csn == NULL) { + return; + } + + if (!SBC_ERROR_IS_OK(ctx->ops->open_conf(ctx))) { + return; + } + + csn->csn = (uint64_t)regdb_get_seqnum(); +} + +/** + * Drop the whole configuration (restarting empty) - registry version + */ +static sbcErr smbconf_reg_drop(struct smbconf_ctx *ctx) +{ + char *path, *p; + WERROR werr; + sbcErr err = SBC_ERR_OK; + struct registry_key *parent_key = NULL; + struct registry_key *new_key = NULL; + TALLOC_CTX* mem_ctx = talloc_stackframe(); + enum winreg_CreateAction action; + struct security_token *token; + + werr = ntstatus_to_werror(registry_create_admin_token(ctx, &token)); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(1, ("Error creating admin token\n")); + err = SBC_ERR_UNKNOWN_FAILURE; + goto done; + } + + path = talloc_strdup(mem_ctx, ctx->path); + if (path == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + p = strrchr(path, '\\'); + if (p == NULL) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + *p = '\0'; + werr = reg_open_path(mem_ctx, path, REG_KEY_WRITE, token, + &parent_key); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_IO_FAILURE; + goto done; + } + + werr = reg_deletesubkeys_recursive(parent_key, p+1); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_IO_FAILURE; + goto done; + } + + werr = reg_createkey(mem_ctx, parent_key, p+1, REG_KEY_WRITE, + &new_key, &action); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_IO_FAILURE; + goto done; + } + +done: + talloc_free(mem_ctx); + return err; +} + +/** + * get the list of share names defined in the configuration. + * registry version. + */ +static sbcErr smbconf_reg_get_share_names(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + char ***share_names) +{ + uint32_t count; + uint32_t added_count = 0; + TALLOC_CTX *tmp_ctx = NULL; + WERROR werr; + sbcErr err = SBC_ERR_OK; + char *subkey_name = NULL; + char **tmp_share_names = NULL; + + if ((num_shares == NULL) || (share_names == NULL)) { + return SBC_ERR_INVALID_PARAM; + } + + tmp_ctx = talloc_stackframe(); + + /* make sure "global" is always listed first */ + if (smbconf_share_exists(ctx, GLOBAL_NAME)) { + err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names, + added_count, GLOBAL_NAME); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + added_count++; + } + + for (count = 0; + werr = reg_enumkey(tmp_ctx, rpd(ctx)->base_key, count, + &subkey_name, NULL), + W_ERROR_IS_OK(werr); + count++) + { + if (strequal(subkey_name, GLOBAL_NAME)) { + continue; + } + + err = smbconf_add_string_to_array(tmp_ctx, + &tmp_share_names, + added_count, + subkey_name); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + added_count++; + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + err = SBC_ERR_NO_MORE_ITEMS; + goto done; + } + err = SBC_ERR_OK; + + *num_shares = added_count; + if (added_count > 0) { + *share_names = talloc_move(mem_ctx, &tmp_share_names); + } else { + *share_names = NULL; + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * check if a share/service of a given name exists - registry version + */ +static bool smbconf_reg_share_exists(struct smbconf_ctx *ctx, + const char *servicename) +{ + bool ret = false; + sbcErr err; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct registry_key *key = NULL; + + err = smbconf_reg_open_service_key(mem_ctx, ctx, servicename, + REG_KEY_READ, &key); + if (SBC_ERROR_IS_OK(err)) { + ret = true; + } + + talloc_free(mem_ctx); + return ret; +} + +/** + * Add a service if it does not already exist - registry version + */ +static sbcErr smbconf_reg_create_share(struct smbconf_ctx *ctx, + const char *servicename) +{ + sbcErr err; + struct registry_key *key = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if (servicename == NULL) { + return SBC_ERR_OK; + } + + err = smbconf_reg_create_service_key(tmp_ctx, ctx, + servicename, &key); + + talloc_free(tmp_ctx); + return err; +} + +/** + * get a definition of a share (service) from configuration. + */ +static sbcErr smbconf_reg_get_share(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *servicename, + struct smbconf_service **service) +{ + sbcErr err; + struct registry_key *key = NULL; + struct smbconf_service *tmp_service = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + err = smbconf_reg_open_service_key(tmp_ctx, ctx, servicename, + REG_KEY_READ, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + tmp_service = talloc_zero(tmp_ctx, struct smbconf_service); + if (tmp_service == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + if (servicename != NULL) { + WERROR werr; + uint32_t count = 0; + char *name = NULL; + + /* + * Determine correct upper/lowercase. + */ + for (count = 0; + werr = reg_enumkey(tmp_ctx, rpd(ctx)->base_key, count, + &name, NULL), + W_ERROR_IS_OK(werr); + count++) { + if (!strequal(name, servicename)) { + continue; + } + + tmp_service->name = talloc_strdup(tmp_service, name); + if (tmp_service->name == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + break; + } + } + + err = smbconf_reg_get_values(tmp_service, key, + &(tmp_service->num_params), + &(tmp_service->param_names), + &(tmp_service->param_values)); + if (SBC_ERROR_IS_OK(err)) { + *service = talloc_move(mem_ctx, &tmp_service); + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * delete a service from configuration + */ +static sbcErr smbconf_reg_delete_share(struct smbconf_ctx *ctx, + const char *servicename) +{ + WERROR werr; + sbcErr err = SBC_ERR_OK; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (servicename != NULL) { + werr = reg_deletekey_recursive(rpd(ctx)->base_key, servicename); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_ACCESS_DENIED; + } + } else { + err = smbconf_reg_delete_values(rpd(ctx)->base_key); + } + + talloc_free(mem_ctx); + return err; +} + +/** + * set a configuration parameter to the value provided. + */ +static sbcErr smbconf_reg_set_parameter(struct smbconf_ctx *ctx, + const char *service, + const char *param, + const char *valstr) +{ + sbcErr err; + struct registry_key *key = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + err = smbconf_reg_open_service_key(mem_ctx, ctx, service, + REG_KEY_WRITE, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + err = smbconf_reg_set_value(key, param, valstr); + +done: + talloc_free(mem_ctx); + return err; +} + +/** + * get the value of a configuration parameter as a string + */ +static sbcErr smbconf_reg_get_parameter(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + const char *param, + char **valstr) +{ + WERROR werr; + sbcErr err; + struct registry_key *key = NULL; + struct registry_value *value = NULL; + + err = smbconf_reg_open_service_key(mem_ctx, ctx, service, + REG_KEY_READ, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + if (!smbconf_reg_parameter_is_valid(param)) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + if (!smbconf_value_exists(key, param)) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + werr = reg_queryvalue(mem_ctx, key, param, &value); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_NOMEM; + goto done; + } + + *valstr = smbconf_format_registry_value(mem_ctx, value); + if (*valstr == NULL) { + err = SBC_ERR_NOMEM; + } + +done: + talloc_free(key); + talloc_free(value); + return err; +} + +/** + * delete a parameter from configuration + */ +static sbcErr smbconf_reg_delete_parameter(struct smbconf_ctx *ctx, + const char *service, + const char *param) +{ + struct registry_key *key = NULL; + WERROR werr; + sbcErr err; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + err = smbconf_reg_open_service_key(mem_ctx, ctx, service, + REG_KEY_ALL, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + if (!smbconf_reg_parameter_is_valid(param)) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + if (!smbconf_value_exists(key, param)) { + err = SBC_ERR_OK; + goto done; + } + + werr = reg_deletevalue(key, param); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_ACCESS_DENIED; + } + +done: + talloc_free(mem_ctx); + return err; +} + +static sbcErr smbconf_reg_get_includes(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + uint32_t *num_includes, + char ***includes) +{ + sbcErr err; + struct registry_key *key = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + err = smbconf_reg_open_service_key(tmp_ctx, ctx, service, + REG_KEY_READ, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + err = smbconf_reg_get_includes_internal(mem_ctx, key, num_includes, + includes); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + +done: + talloc_free(tmp_ctx); + return err; +} + +static sbcErr smbconf_reg_set_includes(struct smbconf_ctx *ctx, + const char *service, + uint32_t num_includes, + const char **includes) +{ + sbcErr err; + struct registry_key *key = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + err = smbconf_reg_open_service_key(tmp_ctx, ctx, service, + REG_KEY_ALL, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + if (num_includes == 0) { + WERROR werr; + if (!smbconf_value_exists(key, INCLUDES_VALNAME)) { + err = SBC_ERR_OK; + goto done; + } + werr = reg_deletevalue(key, INCLUDES_VALNAME); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_ACCESS_DENIED; + goto done; + } + } else { + err = smbconf_reg_set_multi_sz_value(key, INCLUDES_VALNAME, + num_includes, includes); + } + +done: + talloc_free(tmp_ctx); + return err; +} + +static sbcErr smbconf_reg_delete_includes(struct smbconf_ctx *ctx, + const char *service) +{ + WERROR werr; + sbcErr err; + struct registry_key *key = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + err = smbconf_reg_open_service_key(tmp_ctx, ctx, service, + REG_KEY_ALL, &key); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + if (!smbconf_value_exists(key, INCLUDES_VALNAME)) { + err = SBC_ERR_OK; + goto done; + } + + werr = reg_deletevalue(key, INCLUDES_VALNAME); + if (!W_ERROR_IS_OK(werr)) { + err = SBC_ERR_ACCESS_DENIED; + goto done; + } + + err = SBC_ERR_OK; +done: + talloc_free(tmp_ctx); + return err; +} + +static sbcErr smbconf_reg_transaction_start(struct smbconf_ctx *ctx) +{ + WERROR werr; + + werr = regdb_transaction_start(); + if (!W_ERROR_IS_OK(werr)) { + return SBC_ERR_IO_FAILURE; + } + + return SBC_ERR_OK; +} + +static sbcErr smbconf_reg_transaction_commit(struct smbconf_ctx *ctx) +{ + WERROR werr; + + werr = regdb_transaction_commit(); + if (!W_ERROR_IS_OK(werr)) { + return SBC_ERR_IO_FAILURE; + } + + return SBC_ERR_OK; +} + +static sbcErr smbconf_reg_transaction_cancel(struct smbconf_ctx *ctx) +{ + WERROR werr; + + werr = regdb_transaction_cancel(); + if (!W_ERROR_IS_OK(werr)) { + return SBC_ERR_IO_FAILURE; + } + + return SBC_ERR_OK; +} + +struct smbconf_ops smbconf_ops_reg = { + .init = smbconf_reg_init, + .shutdown = smbconf_reg_shutdown, + .requires_messaging = smbconf_reg_requires_messaging, + .is_writeable = smbconf_reg_is_writeable, + .open_conf = smbconf_reg_open, + .close_conf = smbconf_reg_close, + .get_csn = smbconf_reg_get_csn, + .drop = smbconf_reg_drop, + .get_share_names = smbconf_reg_get_share_names, + .share_exists = smbconf_reg_share_exists, + .create_share = smbconf_reg_create_share, + .get_share = smbconf_reg_get_share, + .delete_share = smbconf_reg_delete_share, + .set_parameter = smbconf_reg_set_parameter, + .get_parameter = smbconf_reg_get_parameter, + .delete_parameter = smbconf_reg_delete_parameter, + .get_includes = smbconf_reg_get_includes, + .set_includes = smbconf_reg_set_includes, + .delete_includes = smbconf_reg_delete_includes, + .transaction_start = smbconf_reg_transaction_start, + .transaction_commit = smbconf_reg_transaction_commit, + .transaction_cancel = smbconf_reg_transaction_cancel, +}; + + +/** + * initialize the smbconf registry backend + * the only function that is exported from this module + */ +sbcErr smbconf_init_reg(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx, + const char *path) +{ + /* + * this tmp_ctx stackframe is required to initialize the registry backend. + * Without it, the calls panics due to the use of talloc_tos in the + * source3/registry code. + */ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + sbcErr err = smbconf_init_internal(mem_ctx, conf_ctx, path, &smbconf_ops_reg); + talloc_free(tmp_ctx); + return err; +} diff --git a/source3/lib/smbconf/smbconf_reg.h b/source3/lib/smbconf/smbconf_reg.h new file mode 100644 index 0000000..a3f343f --- /dev/null +++ b/source3/lib/smbconf/smbconf_reg.h @@ -0,0 +1,38 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library + * Copyright (C) Michael Adam 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMBCONF_REG_H__ +#define __LIBSMBCONF_REG_H__ + +struct smbconf_ctx; + +/** + * initialization functions for the registry backend modules + */ + +sbcErr smbconf_init_reg(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx, + const char *path); + +/** + * Check whether a given parameter name is valid in the + * smbconf registry backend. + */ +bool smbconf_reg_parameter_is_valid(const char *param_name); + +#endif /* _LIBSMBCONF_REG_H_ */ diff --git a/source3/lib/smbconf/testsuite.c b/source3/lib/smbconf/testsuite.c new file mode 100644 index 0000000..1dd7eec --- /dev/null +++ b/source3/lib/smbconf/testsuite.c @@ -0,0 +1,338 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library: testsuite + * Copyright (C) Michael Adam 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_init.h" +#include "lib/smbconf/smbconf_reg.h" +#include "lib/smbconf/smbconf_txt.h" + +static void print_strings(const char *prefix, + uint32_t num_strings, + const char * const *strings) +{ + uint32_t count; + + if (prefix == NULL) { + prefix = ""; + } + + for (count = 0; count < num_strings; count++) { + printf("%s%s\n", prefix, strings[count]); + } +} + +static bool test_get_includes(struct smbconf_ctx *ctx) +{ + sbcErr err; + bool ret = false; + uint32_t num_includes = 0; + char **includes = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + printf("TEST: get_includes\n"); + err = smbconf_get_global_includes(ctx, mem_ctx, + &num_includes, &includes); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: get_includes - %s\n", sbcErrorString(err)); + goto done; + } + + printf("got %u includes%s\n", num_includes, + (num_includes > 0) ? ":" : "."); + print_strings("", num_includes, (const char * const *)includes); + + printf("OK: get_includes\n"); + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +static bool test_set_get_includes(struct smbconf_ctx *ctx) +{ + sbcErr err; + uint32_t count; + bool ret = false; + const char *set_includes[] = { + "/path/to/include1", + "/path/to/include2" + }; + uint32_t set_num_includes = 2; + char **get_includes = NULL; + uint32_t get_num_includes = 0; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + printf("TEST: set_get_includes\n"); + + err = smbconf_set_global_includes(ctx, set_num_includes, set_includes); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: get_set_includes (setting includes) - %s\n", + sbcErrorString(err)); + goto done; + } + + err = smbconf_get_global_includes(ctx, mem_ctx, &get_num_includes, + &get_includes); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: get_set_includes (getting includes) - %s\n", + sbcErrorString(err)); + goto done; + } + + if (get_num_includes != set_num_includes) { + printf("FAIL: get_set_includes - set %d includes, got %d\n", + set_num_includes, get_num_includes); + goto done; + } + + for (count = 0; count < get_num_includes; count++) { + if (!strequal(set_includes[count], get_includes[count])) { + printf("expected: \n"); + print_strings("* ", set_num_includes, + (const char * const *)set_includes); + printf("got: \n"); + print_strings("* ", get_num_includes, + (const char * const *)get_includes); + printf("FAIL: get_set_includes - data mismatch:\n"); + goto done; + } + } + + printf("OK: set_includes\n"); + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +static bool test_delete_includes(struct smbconf_ctx *ctx) +{ + sbcErr err; + bool ret = false; + const char *set_includes[] = { + "/path/to/include", + }; + uint32_t set_num_includes = 1; + char **get_includes = NULL; + uint32_t get_num_includes = 0; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + printf("TEST: delete_includes\n"); + + err = smbconf_set_global_includes(ctx, set_num_includes, set_includes); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: delete_includes (setting includes) - %s\n", + sbcErrorString(err)); + goto done; + } + + err = smbconf_delete_global_includes(ctx); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: delete_includes (deleting includes) - %s\n", + sbcErrorString(err)); + goto done; + } + + err = smbconf_get_global_includes(ctx, mem_ctx, &get_num_includes, + &get_includes); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: delete_includes (getting includes) - %s\n", + sbcErrorString(err)); + goto done; + } + + if (get_num_includes != 0) { + printf("FAIL: delete_includes (not empty after delete)\n"); + goto done; + } + + err = smbconf_delete_global_includes(ctx); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: delete_includes (delete empty includes) - " + "%s\n", sbcErrorString(err)); + goto done; + } + + printf("OK: delete_includes\n"); + ret = true; + +done: + talloc_free(mem_ctx); + return ret; +} + +static bool create_conf_file(const char *filename) +{ + FILE *f; + + printf("TEST: creating file\n"); + f = fopen(filename, "w"); + if (!f) { + printf("failure: failed to open %s for writing: %s\n", + filename, strerror(errno)); + return false; + } + + fprintf(f, "[global]\n"); + fprintf(f, "\tserver string = smbconf testsuite\n"); + fprintf(f, "\tworkgroup = SAMBA\n"); + fprintf(f, "\tsecurity = user\n"); + + fclose(f); + + printf("OK: create file\n"); + return true; +} + +static bool torture_smbconf_txt(void) +{ + sbcErr err; + bool ret = true; + const char *filename = "/tmp/smb.conf.smbconf_testsuite"; + struct smbconf_ctx *conf_ctx = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + printf("test: text backend\n"); + + if (!create_conf_file(filename)) { + ret = false; + goto done; + } + + printf("TEST: init\n"); + err = smbconf_init_txt(mem_ctx, &conf_ctx, filename); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: text backend failed: %s\n", sbcErrorString(err)); + ret = false; + goto done; + } + printf("OK: init\n"); + + ret &= test_get_includes(conf_ctx); + + smbconf_shutdown(conf_ctx); + + printf("TEST: unlink file\n"); + if (unlink(filename) != 0) { + printf("OK: unlink failed: %s\n", strerror(errno)); + ret = false; + goto done; + } + printf("OK: unlink file\n"); + +done: + printf("%s: text backend\n", ret ? "success" : "failure"); + talloc_free(mem_ctx); + return ret; +} + +static bool torture_smbconf_reg(void) +{ + sbcErr err; + bool ret = true; + struct smbconf_ctx *conf_ctx = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + printf("test: registry backend\n"); + + printf("TEST: init\n"); + err = smbconf_init_reg(mem_ctx, &conf_ctx, NULL); + if (!SBC_ERROR_IS_OK(err)) { + printf("FAIL: init failed: %s\n", sbcErrorString(err)); + ret = false; + goto done; + } + printf("OK: init\n"); + + ret &= test_get_includes(conf_ctx); + ret &= test_set_get_includes(conf_ctx); + ret &= test_delete_includes(conf_ctx); + + smbconf_shutdown(conf_ctx); + +done: + printf("%s: registry backend\n", ret ? "success" : "failure"); + talloc_free(mem_ctx); + return ret; +} + +static bool torture_smbconf(void) +{ + bool ret = true; + ret &= torture_smbconf_txt(); + printf("\n"); + ret &= torture_smbconf_reg(); + return ret; +} + +int main(int argc, const char **argv) +{ + bool ret; + poptContext pc; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + int opt; + + struct poptOption long_options[] = { + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + smb_init_locale(); + + ret = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + true /* require_smbconf */); + if (!ret) { + goto done; + } + + /* parse options */ + pc = samba_popt_get_context(getprogname(), + argc, + (const char **)argv, + long_options, + 0); + if (pc == NULL) { + ret = false; + goto done; + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + poptFreeContext(pc); + + ret = torture_smbconf(); + +done: + talloc_free(mem_ctx); + return ret ? 0 : -1; +} diff --git a/source3/lib/smbd_shim.c b/source3/lib/smbd_shim.c new file mode 100644 index 0000000..c7c64f7 --- /dev/null +++ b/source3/lib/smbd_shim.c @@ -0,0 +1,116 @@ +/* + Unix SMB/CIFS implementation. + Runtime plugin adapter for various "smbd"-functions. + + Copyright (C) Gerald (Jerry) Carter 2004. + Copyright (C) Andrew Bartlett 2011. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Shim functions required due to the horrible dependency mess + in Samba. */ + +#include "includes.h" +#include "smbd_shim.h" + +static struct smbd_shim shim; + +void set_smbd_shim(const struct smbd_shim *shim_functions) +{ + shim = *shim_functions; +} + +bool change_to_root_user(void) +{ + if (shim.change_to_root_user) { + return shim.change_to_root_user(); + } + return false; +} + +bool become_authenticated_pipe_user(struct auth_session_info *session_info) +{ + if (shim.become_authenticated_pipe_user) { + return shim.become_authenticated_pipe_user(session_info); + } + + return false; +} + +bool unbecome_authenticated_pipe_user(void) +{ + if (shim.unbecome_authenticated_pipe_user) { + return shim.unbecome_authenticated_pipe_user(); + } + + return false; +} + +/** + * The following two functions need to be called from inside the low-level BRL + * code for oplocks correctness in smbd. Since other utility binaries also + * link in some of the brl code directly, these dummy functions are necessary + * to avoid needing to link in the oplocks code and its dependencies to all of + * the utility binaries. + */ +void contend_level2_oplocks_begin(files_struct *fsp, + enum level2_contention_type type) +{ + if (shim.contend_level2_oplocks_begin) { + shim.contend_level2_oplocks_begin(fsp, type); + } + return; +} + +void contend_level2_oplocks_end(files_struct *fsp, + enum level2_contention_type type) +{ + if (shim.contend_level2_oplocks_end) { + shim.contend_level2_oplocks_end(fsp, type); + } + return; +} + +void become_root(void) +{ + if (shim.become_root) { + shim.become_root(); + } + return; +} + +void unbecome_root(void) +{ + if (shim.unbecome_root) { + shim.unbecome_root(); + } + return; +} + +void exit_server(const char *reason) +{ + if (shim.exit_server) { + shim.exit_server(reason); + } + exit(1); +} + +void exit_server_cleanly(const char *const reason) +{ + if (shim.exit_server_cleanly) { + shim.exit_server_cleanly(reason); + } + exit(0); +} diff --git a/source3/lib/smbd_shim.h b/source3/lib/smbd_shim.h new file mode 100644 index 0000000..c4bf330 --- /dev/null +++ b/source3/lib/smbd_shim.h @@ -0,0 +1,53 @@ +/* + Unix SMB/CIFS implementation. + Runtime plugin adapter for various "smbd"-functions. + + Copyright (C) Gerald (Jerry) Carter 2004. + Copyright (C) Andrew Bartlett 2011. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + shim functions are used required to allow library code to have + references to smbd specific code. The smbd daemon sets up the set + of function calls that it wants used by calling + set_smbd_shim(). Other executables don't make this call, and get + default (dummy) versions of these functions. +*/ + +struct smbd_shim +{ + bool (*change_to_root_user)(void); + bool (*become_authenticated_pipe_user)(struct auth_session_info *session_info); + bool (*unbecome_authenticated_pipe_user)(void); + + void (*contend_level2_oplocks_begin)(files_struct *fsp, + enum level2_contention_type type); + + void (*contend_level2_oplocks_end)(files_struct *fsp, + enum level2_contention_type type); + + void (*become_root)(void); + + void (*unbecome_root)(void); + + void (*exit_server)(const char *const explanation) _NORETURN_; + + void (*exit_server_cleanly)(const char *const explanation) _NORETURN_; +}; + +void set_smbd_shim(const struct smbd_shim *shim_functions); + + diff --git a/source3/lib/smbldap.c b/source3/lib/smbldap.c new file mode 100644 index 0000000..c0d6884 --- /dev/null +++ b/source3/lib/smbldap.c @@ -0,0 +1,1941 @@ +/* + Unix SMB/CIFS implementation. + LDAP protocol helper functions for SAMBA + Copyright (C) Jean François Micouleau 1998 + Copyright (C) Gerald Carter 2001-2003 + Copyright (C) Shahms King 2001 + Copyright (C) Andrew Bartlett 2002-2003 + Copyright (C) Stefan (metze) Metzmacher 2002-2003 + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "smbldap.h" +#include "../libcli/security/security.h" +#include <tevent.h> +#include "lib/param/loadparm.h" + +/* Try not to hit the up or down server forever */ + +#define SMBLDAP_DONT_PING_TIME 10 /* ping only all 10 seconds */ +#define SMBLDAP_NUM_RETRIES 8 /* retry only 8 times */ + +#define SMBLDAP_IDLE_TIME 150 /* After 2.5 minutes disconnect */ + +struct smbldap_state { + LDAP *ldap_struct; + pid_t pid; + time_t last_ping; /* monotonic */ + /* retrieve-once info */ + const char *uri; + + /* credentials */ + bool anonymous; + char *bind_dn; + char *bind_secret; + smbldap_bind_callback_fn bind_callback; + void *bind_callback_data; + + bool paged_results; + + unsigned int num_failures; + + time_t last_use; /* monotonic */ + struct tevent_context *tevent_context; + struct tevent_timer *idle_event; + + struct timeval last_rebind; /* monotonic */ +}; + +LDAP *smbldap_get_ldap(struct smbldap_state *state) +{ + return state->ldap_struct; +} + +bool smbldap_get_paged_results(struct smbldap_state *state) +{ + return state->paged_results; +} + +void smbldap_set_paged_results(struct smbldap_state *state, + bool paged_results) +{ + state->paged_results = paged_results; +} + +void smbldap_set_bind_callback(struct smbldap_state *state, + smbldap_bind_callback_fn callback, + void *callback_data) +{ + state->bind_callback = callback; + state->bind_callback_data = callback_data; +} +/******************************************************************* + Search an attribute and return the first value found. +******************************************************************/ + + bool smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry, + const char *attribute, char *value, + int max_len) +{ + char **values; + size_t size = 0; + + if ( !attribute ) + return False; + + value[0] = '\0'; + + if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) { + DEBUG (10, ("smbldap_get_single_attribute: [%s] = [<does not exist>]\n", attribute)); + + return False; + } + + if (!convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, &size)) { + DEBUG(1, ("smbldap_get_single_attribute: string conversion of [%s] = [%s] failed!\n", + attribute, values[0])); + ldap_value_free(values); + return False; + } + + ldap_value_free(values); +#ifdef DEBUG_PASSWORDS + DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", attribute, value)); +#endif + return True; +} + + char * smbldap_talloc_single_attribute(LDAP *ldap_struct, LDAPMessage *entry, + const char *attribute, + TALLOC_CTX *mem_ctx) +{ + char **values; + char *result; + size_t converted_size; + + if (attribute == NULL) { + return NULL; + } + + values = ldap_get_values(ldap_struct, entry, attribute); + + if (values == NULL) { + DEBUG(10, ("attribute %s does not exist\n", attribute)); + return NULL; + } + + if (ldap_count_values(values) != 1) { + DEBUG(10, ("attribute %s has %d values, expected only one\n", + attribute, ldap_count_values(values))); + ldap_value_free(values); + return NULL; + } + + if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { + DEBUG(10, ("pull_utf8_talloc failed\n")); + ldap_value_free(values); + return NULL; + } + + ldap_value_free(values); + +#ifdef DEBUG_PASSWORDS + DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", + attribute, result)); +#endif + return result; +} + + char * smbldap_talloc_first_attribute(LDAP *ldap_struct, LDAPMessage *entry, + const char *attribute, + TALLOC_CTX *mem_ctx) +{ + char **values; + char *result; + size_t converted_size; + + if (attribute == NULL) { + return NULL; + } + + values = ldap_get_values(ldap_struct, entry, attribute); + + if (values == NULL) { + DEBUG(10, ("attribute %s does not exist\n", attribute)); + return NULL; + } + + if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { + DEBUG(10, ("pull_utf8_talloc failed\n")); + ldap_value_free(values); + return NULL; + } + + ldap_value_free(values); + +#ifdef DEBUG_PASSWORDS + DEBUG (100, ("smbldap_get_first_attribute: [%s] = [%s]\n", + attribute, result)); +#endif + return result; +} + + char * smbldap_talloc_smallest_attribute(LDAP *ldap_struct, LDAPMessage *entry, + const char *attribute, + TALLOC_CTX *mem_ctx) +{ + char **values; + char *result; + size_t converted_size; + int i, num_values; + + if (attribute == NULL) { + return NULL; + } + + values = ldap_get_values(ldap_struct, entry, attribute); + + if (values == NULL) { + DEBUG(10, ("attribute %s does not exist\n", attribute)); + return NULL; + } + + if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { + DEBUG(10, ("pull_utf8_talloc failed\n")); + ldap_value_free(values); + return NULL; + } + + num_values = ldap_count_values(values); + + for (i=1; i<num_values; i++) { + char *tmp; + + if (!pull_utf8_talloc(mem_ctx, &tmp, values[i], + &converted_size)) { + DEBUG(10, ("pull_utf8_talloc failed\n")); + TALLOC_FREE(result); + ldap_value_free(values); + return NULL; + } + + if (strcasecmp_m(tmp, result) < 0) { + TALLOC_FREE(result); + result = tmp; + } else { + TALLOC_FREE(tmp); + } + } + + ldap_value_free(values); + +#ifdef DEBUG_PASSWORDS + DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", + attribute, result)); +#endif + return result; +} + + bool smbldap_talloc_single_blob(TALLOC_CTX *mem_ctx, LDAP *ld, + LDAPMessage *msg, const char *attrib, + DATA_BLOB *blob) +{ + struct berval **values; + + values = ldap_get_values_len(ld, msg, attrib); + if (!values) { + return false; + } + + if (ldap_count_values_len(values) != 1) { + DEBUG(10, ("Expected one value for %s, got %d\n", attrib, + ldap_count_values_len(values))); + return false; + } + + *blob = data_blob_talloc(mem_ctx, values[0]->bv_val, + values[0]->bv_len); + ldap_value_free_len(values); + + return (blob->data != NULL); +} + + bool smbldap_pull_sid(LDAP *ld, LDAPMessage *msg, const char *attrib, + struct dom_sid *sid) +{ + DATA_BLOB blob; + ssize_t ret; + + if (!smbldap_talloc_single_blob(talloc_tos(), ld, msg, attrib, + &blob)) { + return false; + } + ret = sid_parse(blob.data, blob.length, sid); + TALLOC_FREE(blob.data); + return (ret != -1); +} + + static int ldapmsg_destructor(LDAPMessage **result) { + ldap_msgfree(*result); + return 0; +} + + void smbldap_talloc_autofree_ldapmsg(TALLOC_CTX *mem_ctx, LDAPMessage *result) +{ + LDAPMessage **handle; + + if (result == NULL) { + return; + } + + handle = talloc(mem_ctx, LDAPMessage *); + SMB_ASSERT(handle != NULL); + + *handle = result; + talloc_set_destructor(handle, ldapmsg_destructor); +} + + static int ldapmod_destructor(LDAPMod ***mod) { + ldap_mods_free(*mod, True); + return 0; +} + + void smbldap_talloc_autofree_ldapmod(TALLOC_CTX *mem_ctx, LDAPMod **mod) +{ + LDAPMod ***handle; + + if (mod == NULL) { + return; + } + + handle = talloc(mem_ctx, LDAPMod **); + SMB_ASSERT(handle != NULL); + + *handle = mod; + talloc_set_destructor(handle, ldapmod_destructor); +} + +/************************************************************************ + Routine to manage the LDAPMod structure array + manage memory used by the array, by each struct, and values + ***********************************************************************/ + +static void smbldap_set_mod_internal(LDAPMod *** modlist, int modop, const char *attribute, const char *value, const DATA_BLOB *blob) +{ + LDAPMod **mods; + int i; + int j; + + mods = *modlist; + + /* sanity checks on the mod values */ + + if (attribute == NULL || *attribute == '\0') { + return; + } + +#if 0 /* commented out after discussion with abartlet. Do not re-enable. + left here so other do not re-add similar code --jerry */ + if (value == NULL || *value == '\0') + return; +#endif + + if (mods == NULL) { + mods = SMB_MALLOC_P(LDAPMod *); + if (mods == NULL) { + smb_panic("smbldap_set_mod: out of memory!"); + /* notreached. */ + } + mods[0] = NULL; + } + + for (i = 0; mods[i] != NULL; ++i) { + if (mods[i]->mod_op == modop && strequal(mods[i]->mod_type, attribute)) + break; + } + + if (mods[i] == NULL) { + mods = SMB_REALLOC_ARRAY (mods, LDAPMod *, i + 2); + if (mods == NULL) { + smb_panic("smbldap_set_mod: out of memory!"); + /* notreached. */ + } + mods[i] = SMB_MALLOC_P(LDAPMod); + if (mods[i] == NULL) { + smb_panic("smbldap_set_mod: out of memory!"); + /* notreached. */ + } + mods[i]->mod_op = modop; + mods[i]->mod_values = NULL; + mods[i]->mod_type = SMB_STRDUP(attribute); + mods[i + 1] = NULL; + } + + if (blob && (modop & LDAP_MOD_BVALUES)) { + j = 0; + if (mods[i]->mod_bvalues != NULL) { + for (; mods[i]->mod_bvalues[j] != NULL; j++); + } + mods[i]->mod_bvalues = SMB_REALLOC_ARRAY(mods[i]->mod_bvalues, struct berval *, j + 2); + + if (mods[i]->mod_bvalues == NULL) { + smb_panic("smbldap_set_mod: out of memory!"); + /* notreached. */ + } + + mods[i]->mod_bvalues[j] = SMB_MALLOC_P(struct berval); + SMB_ASSERT(mods[i]->mod_bvalues[j] != NULL); + + mods[i]->mod_bvalues[j]->bv_val = (char *)smb_memdup(blob->data, blob->length); + SMB_ASSERT(mods[i]->mod_bvalues[j]->bv_val != NULL); + mods[i]->mod_bvalues[j]->bv_len = blob->length; + + mods[i]->mod_bvalues[j + 1] = NULL; + } else if (value != NULL) { + char *utf8_value = NULL; + size_t converted_size; + + j = 0; + if (mods[i]->mod_values != NULL) { + for (; mods[i]->mod_values[j] != NULL; j++); + } + mods[i]->mod_values = SMB_REALLOC_ARRAY(mods[i]->mod_values, char *, j + 2); + + if (mods[i]->mod_values == NULL) { + smb_panic("smbldap_set_mod: out of memory!"); + /* notreached. */ + } + + if (!push_utf8_talloc(talloc_tos(), &utf8_value, value, &converted_size)) { + smb_panic("smbldap_set_mod: String conversion failure!"); + /* notreached. */ + } + + mods[i]->mod_values[j] = SMB_STRDUP(utf8_value); + TALLOC_FREE(utf8_value); + SMB_ASSERT(mods[i]->mod_values[j] != NULL); + + mods[i]->mod_values[j + 1] = NULL; + } + *modlist = mods; +} + + void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value) +{ + smbldap_set_mod_internal(modlist, modop, attribute, value, NULL); +} + + void smbldap_set_mod_blob(LDAPMod *** modlist, int modop, const char *attribute, const DATA_BLOB *value) +{ + smbldap_set_mod_internal(modlist, modop | LDAP_MOD_BVALUES, attribute, NULL, value); +} + +/********************************************************************** + Set attribute to newval in LDAP, regardless of what value the + attribute had in LDAP before. +*********************************************************************/ + +static void smbldap_make_mod_internal(LDAP *ldap_struct, LDAPMessage *existing, + LDAPMod ***mods, + const char *attribute, int op, + const char *newval, + const DATA_BLOB *newblob) +{ + char oldval[2048]; /* current largest allowed value is mungeddial */ + bool existed; + DATA_BLOB oldblob = data_blob_null; + + if (existing != NULL) { + if (op & LDAP_MOD_BVALUES) { + existed = smbldap_talloc_single_blob(talloc_tos(), ldap_struct, existing, attribute, &oldblob); + } else { + existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval)); + } + } else { + existed = False; + *oldval = '\0'; + } + + if (existed) { + bool equal = false; + if (op & LDAP_MOD_BVALUES) { + equal = (newblob && (data_blob_cmp(&oldblob, newblob) == 0)); + } else { + /* all of our string attributes are case insensitive */ + equal = (newval && (strcasecmp_m(oldval, newval) == 0)); + } + + if (equal) { + /* Believe it or not, but LDAP will deny a delete and + an add at the same time if the values are the + same... */ + DEBUG(10,("smbldap_make_mod: attribute |%s| not changed.\n", attribute)); + return; + } + + /* There has been no value before, so don't delete it. + * Here's a possible race: We might end up with + * duplicate attributes */ + /* By deleting exactly the value we found in the entry this + * should be race-free in the sense that the LDAP-Server will + * deny the complete operation if somebody changed the + * attribute behind our back. */ + /* This will also allow modifying single valued attributes + * in Novell NDS. In NDS you have to first remove attribute and then + * you could add new value */ + + if (op & LDAP_MOD_BVALUES) { + DEBUG(10,("smbldap_make_mod: deleting attribute |%s| blob\n", attribute)); + smbldap_set_mod_blob(mods, LDAP_MOD_DELETE, attribute, &oldblob); + } else { + DEBUG(10,("smbldap_make_mod: deleting attribute |%s| values |%s|\n", attribute, oldval)); + smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval); + } + } + + /* Regardless of the real operation (add or modify) + we add the new value here. We rely on deleting + the old value, should it exist. */ + + if (op & LDAP_MOD_BVALUES) { + if (newblob && newblob->length) { + DEBUG(10,("smbldap_make_mod: adding attribute |%s| blob\n", attribute)); + smbldap_set_mod_blob(mods, LDAP_MOD_ADD, attribute, newblob); + } + } else { + if ((newval != NULL) && (strlen(newval) > 0)) { + DEBUG(10,("smbldap_make_mod: adding attribute |%s| value |%s|\n", attribute, newval)); + smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval); + } + } +} + + void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing, + LDAPMod ***mods, + const char *attribute, const char *newval) +{ + smbldap_make_mod_internal(ldap_struct, existing, mods, attribute, + 0, newval, NULL); +} + + void smbldap_make_mod_blob(LDAP *ldap_struct, LDAPMessage *existing, + LDAPMod ***mods, + const char *attribute, const DATA_BLOB *newblob) +{ + smbldap_make_mod_internal(ldap_struct, existing, mods, attribute, + LDAP_MOD_BVALUES, NULL, newblob); +} + +/********************************************************************** + Some variants of the LDAP rebind code do not pass in the third 'arg' + pointer to a void*, so we try and work around it by assuming that the + value of the 'LDAP *' pointer is the same as the one we had passed in + **********************************************************************/ + +struct smbldap_state_lookup { + LDAP *ld; + struct smbldap_state *smbldap_state; + struct smbldap_state_lookup *prev, *next; +}; + +static struct smbldap_state_lookup *smbldap_state_lookup_list; + +static struct smbldap_state *smbldap_find_state(LDAP *ld) +{ + struct smbldap_state_lookup *t; + + for (t = smbldap_state_lookup_list; t; t = t->next) { + if (t->ld == ld) { + return t->smbldap_state; + } + } + return NULL; +} + +static void smbldap_delete_state(struct smbldap_state *smbldap_state) +{ + struct smbldap_state_lookup *t; + + for (t = smbldap_state_lookup_list; t; t = t->next) { + if (t->smbldap_state == smbldap_state) { + DLIST_REMOVE(smbldap_state_lookup_list, t); + SAFE_FREE(t); + return; + } + } +} + +static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state) +{ + struct smbldap_state *tmp_ldap_state; + struct smbldap_state_lookup *t; + + if ((tmp_ldap_state = smbldap_find_state(ld))) { + SMB_ASSERT(tmp_ldap_state == smbldap_state); + return; + } + + t = SMB_XMALLOC_P(struct smbldap_state_lookup); + ZERO_STRUCTP(t); + + DLIST_ADD_END(smbldap_state_lookup_list, t); + t->ld = ld; + t->smbldap_state = smbldap_state; +} + +/******************************************************************** + start TLS on an existing LDAP connection per config +*******************************************************************/ + +int smbldap_start_tls(LDAP *ldap_struct, int version) +{ + if (lp_ldap_ssl() != LDAP_SSL_START_TLS) { + return LDAP_SUCCESS; + } + + return smbldap_start_tls_start(ldap_struct, version); +} + +/******************************************************************** + start TLS on an existing LDAP connection unconditionally +*******************************************************************/ + +int smbldap_start_tls_start(LDAP *ldap_struct, int version) +{ +#ifdef LDAP_OPT_X_TLS + int rc,tls; + + /* check if we use ldaps already */ + ldap_get_option(ldap_struct, LDAP_OPT_X_TLS, &tls); + if (tls == LDAP_OPT_X_TLS_HARD) { + return LDAP_SUCCESS; + } + + if (version != LDAP_VERSION3) { + DEBUG(0, ("Need LDAPv3 for Start TLS\n")); + return LDAP_OPERATIONS_ERROR; + } + + if ((rc = ldap_start_tls_s (ldap_struct, NULL, NULL)) != LDAP_SUCCESS) { + DEBUG(0,("Failed to issue the StartTLS instruction: %s\n", + ldap_err2string(rc))); + return rc; + } + + DEBUG (3, ("StartTLS issued: using a TLS connection\n")); + return LDAP_SUCCESS; +#else + DEBUG(0,("StartTLS not supported by LDAP client libraries!\n")); + return LDAP_OPERATIONS_ERROR; +#endif +} + +/******************************************************************** + setup a connection to the LDAP server based on a uri +*******************************************************************/ + +static int smb_ldap_setup_conn(LDAP **ldap_struct, const char *uri) +{ + int rc; + + DEBUG(10, ("smb_ldap_setup_connection: %s\n", uri)); + +#ifdef HAVE_LDAP_INITIALIZE + + rc = ldap_initialize(ldap_struct, uri); + if (rc) { + DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc))); + return rc; + } + + if (lp_ldap_follow_referral() != Auto) { + rc = ldap_set_option(*ldap_struct, LDAP_OPT_REFERRALS, + lp_ldap_follow_referral() ? LDAP_OPT_ON : LDAP_OPT_OFF); + if (rc != LDAP_SUCCESS) + DEBUG(0, ("Failed to set LDAP_OPT_REFERRALS: %s\n", + ldap_err2string(rc))); + } + +#else + + /* Parse the string manually */ + + { + int port = 0; + fstring protocol; + fstring host; + SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254); + + + /* skip leading "URL:" (if any) */ + if ( strnequal( uri, "URL:", 4 ) ) { + uri += 4; + } + + sscanf(uri, "%10[^:]://%254[^:/]:%d", protocol, host, &port); + + if (port == 0) { + if (strequal(protocol, "ldap")) { + port = LDAP_PORT; + } else if (strequal(protocol, "ldaps")) { + port = LDAPS_PORT; + } else { + DEBUG(0, ("unrecognised protocol (%s)!\n", protocol)); + } + } + + if ((*ldap_struct = ldap_init(host, port)) == NULL) { + DEBUG(0, ("ldap_init failed !\n")); + return LDAP_OPERATIONS_ERROR; + } + + if (strequal(protocol, "ldaps")) { +#ifdef LDAP_OPT_X_TLS + int tls = LDAP_OPT_X_TLS_HARD; + if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) + { + DEBUG(0, ("Failed to setup a TLS session\n")); + } + + DEBUG(3,("LDAPS option set...!\n")); +#else + DEBUG(0,("smbldap_open_connection: Secure connection not supported by LDAP client libraries!\n")); + return LDAP_OPERATIONS_ERROR; +#endif /* LDAP_OPT_X_TLS */ + } + } + + /* now set connection timeout */ +#ifdef LDAP_X_OPT_CONNECT_TIMEOUT /* Netscape */ + { + int ct = lp_ldap_connection_timeout()*1000; + rc = ldap_set_option(*ldap_struct, LDAP_X_OPT_CONNECT_TIMEOUT, &ct); + if (rc != LDAP_SUCCESS) { + DEBUG(0,("Failed to setup an ldap connection timeout %d: %s\n", + ct, ldap_err2string(rc))); + } + } +#elif defined (LDAP_OPT_NETWORK_TIMEOUT) /* OpenLDAP */ + { + struct timeval ct; + ct.tv_usec = 0; + ct.tv_sec = lp_ldap_connection_timeout(); + rc = ldap_set_option(*ldap_struct, LDAP_OPT_NETWORK_TIMEOUT, &ct); + if (rc != LDAP_SUCCESS) { + DEBUG(0,("Failed to setup an ldap connection timeout %d: %s\n", + (int)ct.tv_sec, ldap_err2string(rc))); + } + } +#endif + +#endif /* HAVE_LDAP_INITIALIZE */ + return LDAP_SUCCESS; +} + +/******************************************************************** + try to upgrade to Version 3 LDAP if not already, in either case return current + version + *******************************************************************/ + +static int smb_ldap_upgrade_conn(LDAP *ldap_struct, int *new_version) +{ + int version; + int rc; + + /* assume the worst */ + *new_version = LDAP_VERSION2; + + rc = ldap_get_option(ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); + if (rc) { + return rc; + } + + if (version == LDAP_VERSION3) { + *new_version = LDAP_VERSION3; + return LDAP_SUCCESS; + } + + /* try upgrade */ + version = LDAP_VERSION3; + rc = ldap_set_option (ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); + if (rc) { + return rc; + } + + *new_version = LDAP_VERSION3; + return LDAP_SUCCESS; +} + +/******************************************************************* + open a connection to the ldap server (just until the bind) + ******************************************************************/ + +int smbldap_setup_full_conn(LDAP **ldap_struct, const char *uri) +{ + int rc, version; + + rc = smb_ldap_setup_conn(ldap_struct, uri); + if (rc) { + return rc; + } + + rc = smb_ldap_upgrade_conn(*ldap_struct, &version); + if (rc) { + return rc; + } + + rc = smbldap_start_tls(*ldap_struct, version); + if (rc) { + return rc; + } + + return LDAP_SUCCESS; +} + +/******************************************************************* + open a connection to the ldap server. +******************************************************************/ +static int smbldap_open_connection (struct smbldap_state *ldap_state) + +{ + int rc = LDAP_SUCCESS; + int version; + int deref; + LDAP **ldap_struct = &ldap_state->ldap_struct; + + rc = smb_ldap_setup_conn(ldap_struct, ldap_state->uri); + if (rc) { + return rc; + } + + /* Store the LDAP pointer in a lookup list */ + + smbldap_store_state(*ldap_struct, ldap_state); + + /* Upgrade to LDAPv3 if possible */ + + rc = smb_ldap_upgrade_conn(*ldap_struct, &version); + if (rc) { + return rc; + } + + /* Start TLS if required */ + + rc = smbldap_start_tls(*ldap_struct, version); + if (rc) { + return rc; + } + + /* Set alias dereferencing method */ + deref = lp_ldap_deref(); + if (deref != -1) { + if (ldap_set_option (*ldap_struct, LDAP_OPT_DEREF, &deref) != LDAP_OPT_SUCCESS) { + DEBUG(1,("smbldap_open_connection: Failed to set dereferencing method: %d\n", deref)); + } else { + DEBUG(5,("Set dereferencing method: %d\n", deref)); + } + } + + DEBUG(2, ("smbldap_open_connection: connection opened\n")); + return rc; +} + +/******************************************************************* + a rebind function for authenticated referrals + This version takes a void* that we can shove useful stuff in :-) +******************************************************************/ +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +#else +static int rebindproc_with_state (LDAP * ld, char **whop, char **credp, + int *methodp, int freeit, void *arg) +{ + struct smbldap_state *ldap_state = arg; + struct timespec ts; + + /** @TODO Should we be doing something to check what servers we rebind to? + Could we get a referral to a machine that we don't want to give our + username and password to? */ + + if (freeit) { + SAFE_FREE(*whop); + if (*credp) { + memset(*credp, '\0', strlen(*credp)); + } + SAFE_FREE(*credp); + } else { + DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", + ldap_state->bind_dn?ldap_state->bind_dn:"[Anonymous bind]")); + + if (ldap_state->anonymous) { + *whop = NULL; + *credp = NULL; + } else { + *whop = SMB_STRDUP(ldap_state->bind_dn); + if (!*whop) { + return LDAP_NO_MEMORY; + } + *credp = SMB_STRDUP(ldap_state->bind_secret); + if (!*credp) { + SAFE_FREE(*whop); + return LDAP_NO_MEMORY; + } + } + *methodp = LDAP_AUTH_SIMPLE; + } + + clock_gettime_mono(&ts); + ldap_state->last_rebind = convert_timespec_to_timeval(ts); + + return 0; +} +#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ + +/******************************************************************* + a rebind function for authenticated referrals + This version takes a void* that we can shove useful stuff in :-) + and actually does the connection. +******************************************************************/ +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +static int rebindproc_connect_with_state (LDAP *ldap_struct, + LDAP_CONST char *url, + ber_tag_t request, + ber_int_t msgid, void *arg) +{ + struct smbldap_state *ldap_state = + (struct smbldap_state *)arg; + int rc; + struct timespec ts; + int version; + + DEBUG(5,("rebindproc_connect_with_state: Rebinding to %s as \"%s\"\n", + url, ldap_state->bind_dn?ldap_state->bind_dn:"[Anonymous bind]")); + + /* call START_TLS again (ldaps:// is handled by the OpenLDAP library + * itself) before rebinding to another LDAP server to avoid to expose + * our credentials. At least *try* to secure the connection - Guenther */ + + smb_ldap_upgrade_conn(ldap_struct, &version); + smbldap_start_tls(ldap_struct, version); + + /** @TODO Should we be doing something to check what servers we rebind to? + Could we get a referral to a machine that we don't want to give our + username and password to? */ + + rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret); + + /* only set the last rebind timestamp when we did rebind after a + * non-read LDAP operation. That way we avoid the replication sleep + * after a simple redirected search operation - Guenther */ + + switch (request) { + + case LDAP_REQ_MODIFY: + case LDAP_REQ_ADD: + case LDAP_REQ_DELETE: + case LDAP_REQ_MODDN: + case LDAP_REQ_EXTENDED: + DEBUG(10,("rebindproc_connect_with_state: " + "setting last_rebind timestamp " + "(req: 0x%02x)\n", (unsigned int)request)); + clock_gettime_mono(&ts); + ldap_state->last_rebind = convert_timespec_to_timeval(ts); + break; + default: + ZERO_STRUCT(ldap_state->last_rebind); + break; + } + + return rc; +} +#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ + +/******************************************************************* + Add a rebind function for authenticated referrals +******************************************************************/ +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +#else +# if LDAP_SET_REBIND_PROC_ARGS == 2 +static int rebindproc (LDAP *ldap_struct, char **whop, char **credp, + int *method, int freeit ) +{ + struct smbldap_state *ldap_state = smbldap_find_state(ldap_struct); + + return rebindproc_with_state(ldap_struct, whop, credp, + method, freeit, ldap_state); +} +# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/ +#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ + +/******************************************************************* + a rebind function for authenticated referrals + this also does the connection, but no void*. +******************************************************************/ +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +# if LDAP_SET_REBIND_PROC_ARGS == 2 +static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request, + ber_int_t msgid) +{ + struct smbldap_state *ldap_state = smbldap_find_state(ld); + + return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid, + ldap_state); +} +# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/ +#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ + +/******************************************************************* + connect to the ldap server under system privilege. +******************************************************************/ +static int smbldap_connect_system(struct smbldap_state *ldap_state) +{ + LDAP *ldap_struct = smbldap_get_ldap(ldap_state); + int rc; + int version; + + /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite + (OpenLDAP) doesn't seem to support it */ + + DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n", + ldap_state->uri, ldap_state->bind_dn)); + +#ifdef HAVE_LDAP_SET_REBIND_PROC +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +# if LDAP_SET_REBIND_PROC_ARGS == 2 + ldap_set_rebind_proc(ldap_struct, &rebindproc_connect); +# endif +# if LDAP_SET_REBIND_PROC_ARGS == 3 + ldap_set_rebind_proc(ldap_struct, &rebindproc_connect_with_state, (void *)ldap_state); +# endif +#else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ +# if LDAP_SET_REBIND_PROC_ARGS == 2 + ldap_set_rebind_proc(ldap_struct, &rebindproc); +# endif +# if LDAP_SET_REBIND_PROC_ARGS == 3 + ldap_set_rebind_proc(ldap_struct, &rebindproc_with_state, (void *)ldap_state); +# endif +#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ +#endif + + /* When there is an alternative bind callback is set, + attempt to use it to perform the bind */ + if (ldap_state->bind_callback != NULL) { + /* We have to allow bind callback to be run under become_root/unbecome_root + to make sure within smbd the callback has proper write access to its resources, + like credential cache. This is similar to passdb case where this callback is supposed + to be used. When used outside smbd, become_root()/unbecome_root() are no-op. + */ + become_root(); + rc = ldap_state->bind_callback(ldap_struct, ldap_state, ldap_state->bind_callback_data); + unbecome_root(); + } else { + rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret); + } + + if (rc != LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_ERROR_STRING, + &ld_error); + DEBUG(ldap_state->num_failures ? 2 : 0, + ("failed to bind to server %s with dn=\"%s\" Error: %s\n\t%s\n", + ldap_state->uri, + ldap_state->bind_dn ? ldap_state->bind_dn : "[Anonymous bind]", + ldap_err2string(rc), + ld_error ? ld_error : "(unknown)")); + SAFE_FREE(ld_error); + ldap_state->num_failures++; + goto done; + } + + ldap_state->num_failures = 0; + ldap_state->paged_results = False; + + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_PROTOCOL_VERSION, &version); + + if (smbldap_has_control(smbldap_get_ldap(ldap_state), ADS_PAGE_CTL_OID) + && version == 3) { + ldap_state->paged_results = True; + } + + DEBUG(3, ("ldap_connect_system: successful connection to the LDAP server\n")); + DEBUGADD(10, ("ldap_connect_system: LDAP server %s support paged results\n", + ldap_state->paged_results ? "does" : "does not")); +done: + if (rc != 0) { + ldap_unbind(ldap_struct); + ldap_state->ldap_struct = NULL; + } + return rc; +} + +static void smbldap_idle_fn(struct tevent_context *tevent_ctx, + struct tevent_timer *te, + struct timeval now_abs, + void *private_data); + +/********************************************************************** + Connect to LDAP server (called before every ldap operation) +*********************************************************************/ +static int smbldap_open(struct smbldap_state *ldap_state) +{ + int rc, opt_rc; + bool reopen = False; + SMB_ASSERT(ldap_state); + + if ((smbldap_get_ldap(ldap_state) != NULL) && + ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < + time_mono(NULL))) { + +#ifdef HAVE_UNIXSOCKET + struct sockaddr_un addr; +#else + struct sockaddr_storage addr; +#endif + socklen_t len = sizeof(addr); + int sd; + + opt_rc = ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_DESC, &sd); + if (opt_rc == 0 && (getpeername(sd, (struct sockaddr *) &addr, &len)) < 0 ) + reopen = True; + +#ifdef HAVE_UNIXSOCKET + if (opt_rc == 0 && addr.sun_family == AF_UNIX) + reopen = True; +#endif + if (reopen) { + /* the other end has died. reopen. */ + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + ldap_state->last_ping = (time_t)0; + } else { + ldap_state->last_ping = time_mono(NULL); + } + } + + if (smbldap_get_ldap(ldap_state) != NULL) { + DEBUG(11,("smbldap_open: already connected to the LDAP server\n")); + return LDAP_SUCCESS; + } + + if ((rc = smbldap_open_connection(ldap_state))) { + return rc; + } + + if ((rc = smbldap_connect_system(ldap_state))) { + return rc; + } + + + ldap_state->last_ping = time_mono(NULL); + ldap_state->pid = getpid(); + + TALLOC_FREE(ldap_state->idle_event); + + if (ldap_state->tevent_context != NULL) { + ldap_state->idle_event = tevent_add_timer( + ldap_state->tevent_context, ldap_state, + timeval_current_ofs(SMBLDAP_IDLE_TIME, 0), + smbldap_idle_fn, ldap_state); + } + + DEBUG(4,("The LDAP server is successfully connected\n")); + + return LDAP_SUCCESS; +} + +/********************************************************************** +Disconnect from LDAP server +*********************************************************************/ +static NTSTATUS smbldap_close(struct smbldap_state *ldap_state) +{ + if (!ldap_state) + return NT_STATUS_INVALID_PARAMETER; + + if (smbldap_get_ldap(ldap_state) != NULL) { + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } + + smbldap_delete_state(ldap_state); + + TALLOC_FREE(ldap_state->idle_event); + + DEBUG(5,("The connection to the LDAP server was closed\n")); + /* maybe free the results here --metze */ + + return NT_STATUS_OK; +} + +static SIG_ATOMIC_T got_alarm; + +static void gotalarm_sig(int dummy) +{ + got_alarm = 1; +} + +static time_t calc_ldap_abs_endtime(int ldap_to) +{ + if (ldap_to == 0) { + /* No timeout - don't + return a value for + the alarm. */ + return (time_t)0; + } + + /* Make the alarm time one second beyond + the timeout we're setting for the + remote search timeout, to allow that + to fire in preference. */ + + return time_mono(NULL)+ldap_to+1; +} + +static int end_ldap_local_alarm(time_t absolute_endtime, int rc) +{ + if (absolute_endtime) { + alarm(0); + CatchSignal(SIGALRM, SIG_IGN); + if (got_alarm) { + /* Client timeout error code. */ + got_alarm = 0; + return LDAP_TIMEOUT; + } + } + return rc; +} + +static void setup_ldap_local_alarm(struct smbldap_state *ldap_state, time_t absolute_endtime) +{ + time_t now = time_mono(NULL); + + if (absolute_endtime) { + got_alarm = 0; + CatchSignal(SIGALRM, gotalarm_sig); + alarm(absolute_endtime - now); + } + + if (ldap_state->pid != getpid()) { + smbldap_close(ldap_state); + } +} + +static void get_ldap_errs(struct smbldap_state *ldap_state, char **pp_ld_error, int *p_ld_errno) +{ + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_ERROR_NUMBER, p_ld_errno); + + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_ERROR_STRING, pp_ld_error); +} + +static int get_cached_ldap_connect(struct smbldap_state *ldap_state, time_t abs_endtime) +{ + int attempts = 0; + + while (1) { + int rc; + time_t now; + + now = time_mono(NULL); + ldap_state->last_use = now; + + if (abs_endtime && now > abs_endtime) { + smbldap_close(ldap_state); + return LDAP_TIMEOUT; + } + + rc = smbldap_open(ldap_state); + + if (rc == LDAP_SUCCESS) { + return LDAP_SUCCESS; + } + + attempts++; + DEBUG(1, ("Connection to LDAP server failed for the " + "%d try!\n", attempts)); + + if (rc == LDAP_INSUFFICIENT_ACCESS) { + /* The fact that we are non-root or any other + * access-denied condition will not change in the next + * round of trying */ + return rc; + } + + if (got_alarm) { + smbldap_close(ldap_state); + return LDAP_TIMEOUT; + } + + smb_msleep(1000); + + if (got_alarm) { + smbldap_close(ldap_state); + return LDAP_TIMEOUT; + } + } +} + +/********************************************************************* + ********************************************************************/ + +static int smbldap_search_ext(struct smbldap_state *ldap_state, + const char *base, int scope, const char *filter, + const char *attrs[], int attrsonly, + LDAPControl **sctrls, LDAPControl **cctrls, + int sizelimit, LDAPMessage **res) +{ + int rc = LDAP_SERVER_DOWN; + char *utf8_filter; + int to = lp_ldap_timeout(); + time_t abs_endtime = calc_ldap_abs_endtime(to); + struct timeval timeout; + struct timeval *timeout_ptr = NULL; + size_t converted_size; + + SMB_ASSERT(ldap_state); + + DEBUG(5,("smbldap_search_ext: base => [%s], filter => [%s], " + "scope => [%d]\n", base, filter, scope)); + + if (ldap_state->last_rebind.tv_sec > 0) { + struct timeval tval; + struct timespec ts; + int64_t tdiff = 0; + int sleep_time = 0; + + clock_gettime_mono(&ts); + tval = convert_timespec_to_timeval(ts); + + tdiff = usec_time_diff(&tval, &ldap_state->last_rebind); + tdiff /= 1000; /* Convert to milliseconds. */ + + sleep_time = lp_ldap_replication_sleep()-(int)tdiff; + sleep_time = MIN(sleep_time, MAX_LDAP_REPLICATION_SLEEP_TIME); + + if (sleep_time > 0) { + /* we wait for the LDAP replication */ + DEBUG(5,("smbldap_search_ext: waiting %d milliseconds " + "for LDAP replication.\n",sleep_time)); + smb_msleep(sleep_time); + DEBUG(5,("smbldap_search_ext: go on!\n")); + } + ZERO_STRUCT(ldap_state->last_rebind); + } + + if (!push_utf8_talloc(talloc_tos(), &utf8_filter, filter, &converted_size)) { + return LDAP_NO_MEMORY; + } + + /* Setup remote timeout for the ldap_search_ext_s call. */ + if (to) { + timeout.tv_sec = to; + timeout.tv_usec = 0; + timeout_ptr = &timeout; + } + + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } + + rc = ldap_search_ext_s(smbldap_get_ldap(ldap_state), + base, scope, + utf8_filter, + discard_const_p(char *, attrs), + attrsonly, sctrls, cctrls, timeout_ptr, + sizelimit, res); + if (rc == LDAP_SUCCESS) { + break; + } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed search for base: %s, error: %d (%s) " + "(%s)\n", base, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } + + TALLOC_FREE(utf8_filter); + return end_ldap_local_alarm(abs_endtime, rc); +} + +int smbldap_search(struct smbldap_state *ldap_state, + const char *base, int scope, const char *filter, + const char *attrs[], int attrsonly, + LDAPMessage **res) +{ + return smbldap_search_ext(ldap_state, base, scope, filter, attrs, + attrsonly, NULL, NULL, LDAP_NO_LIMIT, res); +} + +int smbldap_search_paged(struct smbldap_state *ldap_state, + const char *base, int scope, const char *filter, + const char **attrs, int attrsonly, int pagesize, + LDAPMessage **res, void **cookie) +{ + LDAPControl pr; + LDAPControl **rcontrols; + LDAPControl *controls[2] = { NULL, NULL}; + BerElement *cookie_be = NULL; + struct berval *cookie_bv = NULL; + int tmp = 0, i, rc; + bool critical = True; + + *res = NULL; + + DEBUG(3,("smbldap_search_paged: base => [%s], filter => [%s]," + "scope => [%d], pagesize => [%d]\n", + base, filter, scope, pagesize)); + + cookie_be = ber_alloc_t(LBER_USE_DER); + if (cookie_be == NULL) { + DEBUG(0,("smbldap_create_page_control: ber_alloc_t returns " + "NULL\n")); + return LDAP_NO_MEMORY; + } + + /* construct cookie */ + if (*cookie != NULL) { + ber_printf(cookie_be, "{iO}", (ber_int_t) pagesize, *cookie); + ber_bvfree((struct berval *)*cookie); /* don't need it from last time */ + *cookie = NULL; + } else { + ber_printf(cookie_be, "{io}", (ber_int_t) pagesize, "", 0); + } + ber_flatten(cookie_be, &cookie_bv); + + pr.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID); + pr.ldctl_iscritical = (char) critical; + pr.ldctl_value.bv_len = cookie_bv->bv_len; + pr.ldctl_value.bv_val = cookie_bv->bv_val; + + controls[0] = ≺ + controls[1] = NULL; + + rc = smbldap_search_ext(ldap_state, base, scope, filter, attrs, + 0, controls, NULL, LDAP_NO_LIMIT, res); + + ber_free(cookie_be, 1); + ber_bvfree(cookie_bv); + + if (rc != 0) { + DEBUG(3,("smbldap_search_paged: smbldap_search_ext(%s) " + "failed with [%s]\n", filter, ldap_err2string(rc))); + goto done; + } + + DEBUG(3,("smbldap_search_paged: search was successful\n")); + + rc = ldap_parse_result(smbldap_get_ldap(ldap_state), *res, NULL, NULL, + NULL, NULL, &rcontrols, 0); + if (rc != 0) { + DEBUG(3,("smbldap_search_paged: ldap_parse_result failed " \ + "with [%s]\n", ldap_err2string(rc))); + goto done; + } + + if (rcontrols == NULL) + goto done; + + for (i=0; rcontrols[i]; i++) { + + if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) != 0) + continue; + + cookie_be = ber_init(&rcontrols[i]->ldctl_value); + ber_scanf(cookie_be,"{iO}", &tmp, &cookie_bv); + /* the berval is the cookie, but must be freed when it is all + done */ + if (cookie_bv->bv_len) + *cookie=ber_bvdup(cookie_bv); + else + *cookie=NULL; + ber_bvfree(cookie_bv); + ber_free(cookie_be, 1); + break; + } + ldap_controls_free(rcontrols); +done: + return rc; +} + +int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[]) +{ + int rc = LDAP_SERVER_DOWN; + char *utf8_dn; + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); + size_t converted_size; + + SMB_ASSERT(ldap_state); + + DEBUG(5,("smbldap_modify: dn => [%s]\n", dn )); + + if (!push_utf8_talloc(talloc_tos(), &utf8_dn, dn, &converted_size)) { + return LDAP_NO_MEMORY; + } + + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } + + rc = ldap_modify_s(smbldap_get_ldap(ldap_state), utf8_dn, + attrs); + if (rc == LDAP_SUCCESS) { + break; + } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed to modify dn: %s, error: %d (%s) " + "(%s)\n", dn, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } + + TALLOC_FREE(utf8_dn); + return end_ldap_local_alarm(abs_endtime, rc); +} + +int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[]) +{ + int rc = LDAP_SERVER_DOWN; + char *utf8_dn; + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); + size_t converted_size; + + SMB_ASSERT(ldap_state); + + DEBUG(5,("smbldap_add: dn => [%s]\n", dn )); + + if (!push_utf8_talloc(talloc_tos(), &utf8_dn, dn, &converted_size)) { + return LDAP_NO_MEMORY; + } + + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } + + rc = ldap_add_s(smbldap_get_ldap(ldap_state), utf8_dn, attrs); + if (rc == LDAP_SUCCESS) { + break; + } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed to add dn: %s, error: %d (%s) " + "(%s)\n", dn, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } + + TALLOC_FREE(utf8_dn); + return end_ldap_local_alarm(abs_endtime, rc); +} + +int smbldap_delete(struct smbldap_state *ldap_state, const char *dn) +{ + int rc = LDAP_SERVER_DOWN; + char *utf8_dn; + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); + size_t converted_size; + + SMB_ASSERT(ldap_state); + + DEBUG(5,("smbldap_delete: dn => [%s]\n", dn )); + + if (!push_utf8_talloc(talloc_tos(), &utf8_dn, dn, &converted_size)) { + return LDAP_NO_MEMORY; + } + + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } + + rc = ldap_delete_s(smbldap_get_ldap(ldap_state), utf8_dn); + if (rc == LDAP_SUCCESS) { + break; + } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed to delete dn: %s, error: %d (%s) " + "(%s)\n", dn, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } + + TALLOC_FREE(utf8_dn); + return end_ldap_local_alarm(abs_endtime, rc); +} + +int smbldap_extended_operation(struct smbldap_state *ldap_state, + LDAP_CONST char *reqoid, struct berval *reqdata, + LDAPControl **serverctrls, LDAPControl **clientctrls, + char **retoidp, struct berval **retdatap) +{ + int rc = LDAP_SERVER_DOWN; + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); + + if (!ldap_state) + return (-1); + + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } + + rc = ldap_extended_operation_s(smbldap_get_ldap(ldap_state), + reqoid, + reqdata, serverctrls, + clientctrls, retoidp, retdatap); + if (rc == LDAP_SUCCESS) { + break; + } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Extended operation failed with error: " + "%d (%s) (%s)\n", ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } + + return end_ldap_local_alarm(abs_endtime, rc); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +int smbldap_search_suffix (struct smbldap_state *ldap_state, + const char *filter, const char **search_attr, + LDAPMessage ** result) +{ + return smbldap_search(ldap_state, lp_ldap_suffix(), + LDAP_SCOPE_SUBTREE, + filter, search_attr, 0, result); +} + +static void smbldap_idle_fn(struct tevent_context *tevent_ctx, + struct tevent_timer *te, + struct timeval now_abs, + void *private_data) +{ + struct smbldap_state *state = (struct smbldap_state *)private_data; + + TALLOC_FREE(state->idle_event); + + if (smbldap_get_ldap(state) == NULL) { + DEBUG(10,("ldap connection not connected...\n")); + return; + } + + if ((state->last_use+SMBLDAP_IDLE_TIME) > time_mono(NULL)) { + DEBUG(10,("ldap connection not idle...\n")); + + /* this needs to be made monotonic clock aware inside tevent: */ + state->idle_event = tevent_add_timer( + tevent_ctx, state, + timeval_add(&now_abs, SMBLDAP_IDLE_TIME, 0), + smbldap_idle_fn, + private_data); + return; + } + + DEBUG(7,("ldap connection idle...closing connection\n")); + smbldap_close(state); +} + +/********************************************************************** + Housekeeping + *********************************************************************/ + +void smbldap_free_struct(struct smbldap_state **ldap_state) +{ + smbldap_close(*ldap_state); + + if ((*ldap_state)->bind_secret) { + memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret)); + } + + SAFE_FREE((*ldap_state)->bind_dn); + BURN_FREE_STR((*ldap_state)->bind_secret); + smbldap_set_bind_callback(*ldap_state, NULL, NULL); + + TALLOC_FREE(*ldap_state); + + /* No need to free any further, as it is talloc()ed */ +} + +static int smbldap_state_destructor(struct smbldap_state *state) +{ + smbldap_free_struct(&state); + return 0; +} + + +/********************************************************************** + Initialise the 'general' ldap structures, on which ldap operations may be conducted + *********************************************************************/ + +NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, struct tevent_context *tevent_ctx, + const char *location, + bool anon, + const char *bind_dn, + const char *bind_secret, + struct smbldap_state **smbldap_state) +{ + *smbldap_state = talloc_zero(mem_ctx, struct smbldap_state); + if (!*smbldap_state) { + DEBUG(0, ("talloc() failed for ldapsam private_data!\n")); + return NT_STATUS_NO_MEMORY; + } + + if (location) { + (*smbldap_state)->uri = talloc_strdup(mem_ctx, location); + } else { + (*smbldap_state)->uri = "ldap://localhost"; + } + + (*smbldap_state)->tevent_context = tevent_ctx; + + if (bind_dn && bind_secret) { + smbldap_set_creds(*smbldap_state, anon, bind_dn, bind_secret); + } + + talloc_set_destructor(*smbldap_state, smbldap_state_destructor); + return NT_STATUS_OK; +} + + char *smbldap_talloc_dn(TALLOC_CTX *mem_ctx, LDAP *ld, + LDAPMessage *entry) +{ + char *utf8_dn, *unix_dn; + size_t converted_size; + + utf8_dn = ldap_get_dn(ld, entry); + if (!utf8_dn) { + DEBUG (5, ("smbldap_talloc_dn: ldap_get_dn failed\n")); + return NULL; + } + if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) { + DEBUG (0, ("smbldap_talloc_dn: String conversion failure utf8 " + "[%s]\n", utf8_dn)); + return NULL; + } + ldap_memfree(utf8_dn); + return unix_dn; +} + +/******************************************************************* + Check if root-dse has a certain Control or Extension +********************************************************************/ + +static bool smbldap_check_root_dse(LDAP *ld, const char **attrs, const char *value) +{ + LDAPMessage *msg = NULL; + LDAPMessage *entry = NULL; + char **values = NULL; + int rc, num_result, num_values, i; + bool result = False; + + if (!attrs[0]) { + DEBUG(3,("smbldap_check_root_dse: nothing to look for\n")); + return False; + } + + if (!strequal(attrs[0], "supportedExtension") && + !strequal(attrs[0], "supportedControl") && + !strequal(attrs[0], "namingContexts")) { + DEBUG(3,("smbldap_check_root_dse: no idea what to query root-dse for: %s ?\n", attrs[0])); + return False; + } + + rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, + "(objectclass=*)", discard_const_p(char *, attrs), 0 , &msg); + + if (rc != LDAP_SUCCESS) { + DEBUG(3,("smbldap_check_root_dse: Could not search rootDSE\n")); + return False; + } + + num_result = ldap_count_entries(ld, msg); + + if (num_result != 1) { + DEBUG(3,("smbldap_check_root_dse: Expected one rootDSE, got %d\n", num_result)); + goto done; + } + + entry = ldap_first_entry(ld, msg); + + if (entry == NULL) { + DEBUG(3,("smbldap_check_root_dse: Could not retrieve rootDSE\n")); + goto done; + } + + values = ldap_get_values(ld, entry, attrs[0]); + + if (values == NULL) { + DEBUG(5,("smbldap_check_root_dse: LDAP Server does not support any %s\n", attrs[0])); + goto done; + } + + num_values = ldap_count_values(values); + + if (num_values == 0) { + DEBUG(5,("smbldap_check_root_dse: LDAP Server does not have any %s\n", attrs[0])); + goto done; + } + + for (i=0; i<num_values; i++) { + if (strcmp(values[i], value) == 0) + result = True; + } + + + done: + if (values != NULL) + ldap_value_free(values); + if (msg != NULL) + ldap_msgfree(msg); + + return result; + +} + +/******************************************************************* + Check if LDAP-Server supports a certain Control (OID in string format) +********************************************************************/ + +bool smbldap_has_control(LDAP *ld, const char *control) +{ + const char *attrs[] = { "supportedControl", NULL }; + return smbldap_check_root_dse(ld, attrs, control); +} + +/******************************************************************* + Check if LDAP-Server supports a certain Extension (OID in string format) +********************************************************************/ + +bool smbldap_has_extension(LDAP *ld, const char *extension) +{ + const char *attrs[] = { "supportedExtension", NULL }; + return smbldap_check_root_dse(ld, attrs, extension); +} + +/******************************************************************* + Check if LDAP-Server holds a given namingContext +********************************************************************/ + +bool smbldap_has_naming_context(LDAP *ld, const char *naming_context) +{ + const char *attrs[] = { "namingContexts", NULL }; + return smbldap_check_root_dse(ld, attrs, naming_context); +} + +bool smbldap_set_creds(struct smbldap_state *ldap_state, bool anon, const char *dn, const char *secret) +{ + ldap_state->anonymous = anon; + + /* free any previously set credential */ + + SAFE_FREE(ldap_state->bind_dn); + smbldap_set_bind_callback(ldap_state, NULL, NULL); + + if (ldap_state->bind_secret) { + /* make sure secrets are zeroed out of memory */ + memset(ldap_state->bind_secret, '\0', strlen(ldap_state->bind_secret)); + SAFE_FREE(ldap_state->bind_secret); + } + + if ( ! anon) { + ldap_state->bind_dn = SMB_STRDUP(dn); + ldap_state->bind_secret = SMB_STRDUP(secret); + } + + return True; +} diff --git a/source3/lib/smbrun.c b/source3/lib/smbrun.c new file mode 100644 index 0000000..8e3675f --- /dev/null +++ b/source3/lib/smbrun.c @@ -0,0 +1,354 @@ +/* + Unix SMB/CIFS implementation. + run a command as a specified user + Copyright (C) Andrew Tridgell 1992-1998 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" + +/* need to move this from here!! need some sleep ... */ +struct current_user current_user; + +/**************************************************************************** +This is a utility function of smbrun(). +****************************************************************************/ + +static int setup_out_fd(void) +{ + int fd; + TALLOC_CTX *ctx = talloc_stackframe(); + char *path = NULL; + mode_t mask; + + path = talloc_asprintf(ctx, + "%s/smb.XXXXXX", + tmpdir()); + if (!path) { + TALLOC_FREE(ctx); + errno = ENOMEM; + return -1; + } + + /* now create the file */ + mask = umask(S_IRWXO | S_IRWXG); + fd = mkstemp(path); + umask(mask); + + if (fd == -1) { + DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n", + path, strerror(errno) )); + TALLOC_FREE(ctx); + return -1; + } + + DEBUG(10,("setup_out_fd: Created tmp file %s\n", path )); + + /* Ensure file only kept around by open fd. */ + unlink(path); + TALLOC_FREE(ctx); + return fd; +} + +/**************************************************************************** +run a command being careful about uid/gid handling and putting the output in +outfd (or discard it if outfd is NULL). +****************************************************************************/ + +static int smbrun_internal(const char *cmd, int *outfd, bool sanitize, + char * const *env) +{ + pid_t pid; + uid_t uid = current_user.ut.uid; + gid_t gid = current_user.ut.gid; + void (*saved_handler)(int); + + /* + * Lose any elevated privileges. + */ + drop_effective_capability(KERNEL_OPLOCK_CAPABILITY); + drop_effective_capability(DMAPI_ACCESS_CAPABILITY); + + /* point our stdout at the file we want output to go into */ + + if (outfd && ((*outfd = setup_out_fd()) == -1)) { + return -1; + } + + /* in this method we will exec /bin/sh with the correct + arguments, after first setting stdout to point at the file */ + + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + saved_handler = CatchChildLeaveStatus(); + + if ((pid=fork()) < 0) { + DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) )); + (void)CatchSignal(SIGCLD, saved_handler); + if (outfd) { + close(*outfd); + *outfd = -1; + } + return errno; + } + + if (pid) { + /* + * Parent. + */ + int status=0; + pid_t wpid; + + + /* the parent just waits for the child to exit */ + while((wpid = waitpid(pid,&status,0)) < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + break; + } + + (void)CatchSignal(SIGCLD, saved_handler); + + if (wpid != pid) { + DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno))); + if (outfd) { + close(*outfd); + *outfd = -1; + } + return -1; + } + + /* Reset the seek pointer. */ + if (outfd) { + lseek(*outfd, 0, SEEK_SET); + } + +#if defined(WIFEXITED) && defined(WEXITSTATUS) + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } +#endif + + return status; + } + + (void)CatchChild(); + + /* we are in the child. we exec /bin/sh to do the work for us. we + don't directly exec the command we want because it may be a + pipeline or anything else the config file specifies */ + + /* point our stdout at the file we want output to go into */ + if (outfd) { + close(1); + if (dup2(*outfd,1) != 1) { + DEBUG(2,("Failed to create stdout file descriptor\n")); + close(*outfd); + exit(80); + } + } + + /* now completely lose our privileges. This is a fairly paranoid + way of doing it, but it does work on all systems that I know of */ + + become_user_permanently(uid, gid); + + if (!non_root_mode()) { + if (getuid() != uid || geteuid() != uid || + getgid() != gid || getegid() != gid) { + /* we failed to lose our privileges - do not execute + the command */ + exit(81); /* we can't print stuff at this stage, + instead use exit codes for debugging */ + } + } + + /* close all other file descriptors, leaving only 0, 1 and 2. 0 and + 2 point to /dev/null from the startup code */ + closefrom(3); + + { + char *newcmd = NULL; + if (sanitize) { + newcmd = escape_shell_string(cmd); + if (!newcmd) + exit(82); + } + + if (env != NULL) { + execle("/bin/sh","sh","-c", + newcmd ? (const char *)newcmd : cmd, NULL, + env); + } else { + execl("/bin/sh","sh","-c", + newcmd ? (const char *)newcmd : cmd, NULL); + } + + SAFE_FREE(newcmd); + } + + /* not reached */ + exit(83); + return 1; +} + +/**************************************************************************** + Use only in known safe shell calls (printing). +****************************************************************************/ + +int smbrun_no_sanitize(const char *cmd, int *outfd, char * const *env) +{ + return smbrun_internal(cmd, outfd, false, env); +} + +/**************************************************************************** + By default this now sanitizes shell expansion. +****************************************************************************/ + +int smbrun(const char *cmd, int *outfd, char * const *env) +{ + return smbrun_internal(cmd, outfd, true, env); +} + +/**************************************************************************** +run a command being careful about uid/gid handling and putting the output in +outfd (or discard it if outfd is NULL). +sends the provided secret to the child stdin. +****************************************************************************/ + +int smbrunsecret(const char *cmd, const char *secret) +{ + pid_t pid; + uid_t uid = current_user.ut.uid; + gid_t gid = current_user.ut.gid; + int ifd[2]; + void (*saved_handler)(int); + + /* + * Lose any elevated privileges. + */ + drop_effective_capability(KERNEL_OPLOCK_CAPABILITY); + drop_effective_capability(DMAPI_ACCESS_CAPABILITY); + + /* build up an input pipe */ + if(pipe(ifd)) { + return -1; + } + + /* in this method we will exec /bin/sh with the correct + arguments, after first setting stdout to point at the file */ + + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + saved_handler = CatchChildLeaveStatus(); + + if ((pid=fork()) < 0) { + DEBUG(0, ("smbrunsecret: fork failed with error %s\n", strerror(errno))); + (void)CatchSignal(SIGCLD, saved_handler); + return errno; + } + + if (pid) { + /* + * Parent. + */ + int status = 0; + pid_t wpid; + size_t towrite; + ssize_t wrote; + + close(ifd[0]); + /* send the secret */ + towrite = strlen(secret); + wrote = write(ifd[1], secret, towrite); + if ( wrote != towrite ) { + DEBUG(0,("smbrunsecret: wrote %ld of %lu bytes\n",(long)wrote,(unsigned long)towrite)); + } + fsync(ifd[1]); + close(ifd[1]); + + /* the parent just waits for the child to exit */ + while((wpid = waitpid(pid, &status, 0)) < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + break; + } + + (void)CatchSignal(SIGCLD, saved_handler); + + if (wpid != pid) { + DEBUG(2, ("waitpid(%d) : %s\n", (int)pid, strerror(errno))); + return -1; + } + +#if defined(WIFEXITED) && defined(WEXITSTATUS) + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } +#endif + + return status; + } + + (void)CatchChild(); + + /* we are in the child. we exec /bin/sh to do the work for us. we + don't directly exec the command we want because it may be a + pipeline or anything else the config file specifies */ + + close(ifd[1]); + close(0); + if (dup2(ifd[0], 0) != 0) { + DEBUG(2,("Failed to create stdin file descriptor\n")); + close(ifd[0]); + exit(80); + } + + /* now completely lose our privileges. This is a fairly paranoid + way of doing it, but it does work on all systems that I know of */ + + become_user_permanently(uid, gid); + + if (!non_root_mode()) { + if (getuid() != uid || geteuid() != uid || + getgid() != gid || getegid() != gid) { + /* we failed to lose our privileges - do not execute + the command */ + exit(81); /* we can't print stuff at this stage, + instead use exit codes for debugging */ + } + } + + /* close all other file descriptors, leaving only 0, 1 and 2. 0 and + 2 point to /dev/null from the startup code */ + closefrom(3); + + execl("/bin/sh", "sh", "-c", cmd, NULL); + + /* not reached */ + exit(82); + return 1; +} diff --git a/source3/lib/srprs.c b/source3/lib/srprs.c new file mode 100644 index 0000000..67ada37 --- /dev/null +++ b/source3/lib/srprs.c @@ -0,0 +1,228 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2010 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @file srprs.c + * @author Gregor Beck <gb@sernet.de> + * @date Aug 2010 + * @brief A simple recursive parser. + */ + +#include "replace.h" +#include "system/locale.h" +#include "srprs.h" +#include "cbuf.h" +#include <assert.h> + +bool srprs_skipws(const char** ptr) { + while (isspace(**ptr)) + ++(*ptr); + return true; +} + +bool srprs_char(const char** ptr, char c) { + if (**ptr == c) { + ++(*ptr); + return true; + } + return false; +} + +bool srprs_str(const char** ptr, const char* str, ssize_t len) +{ + /* By definition *ptr must be null terminated. */ + size_t ptr_len = strlen(*ptr); + + if (len == -1) + len = strlen(str); + + /* Don't memcmp read past end of buffer. */ + if (len > ptr_len) { + return false; + } + + if (memcmp(*ptr, str, len) == 0) { + *ptr += len; + return true; + } + return false; +} + +bool srprs_charset(const char** ptr, const char* set, cbuf* oss) +{ + const char* p = strchr(set, **ptr); + if (p != NULL && *p != '\0') { + cbuf_putc(oss, **ptr); + ++(*ptr); + return true; + } + return false; +} + +bool srprs_charsetinv(const char** ptr, const char* set, cbuf* oss) +{ + if ((**ptr != '\0') && (strchr(set, **ptr) == NULL)) { + cbuf_putc(oss, **ptr); + ++(*ptr); + return true; + } + return false; +} + + + +bool srprs_quoted_string(const char** ptr, cbuf* str, bool* cont) +{ + const char* pos = *ptr; + const size_t spos = cbuf_getpos(str); + + if (cont == NULL || *cont == false) { + if (!srprs_char(&pos, '\"')) + goto fail; + } + + while (true) { + while (srprs_charsetinv(&pos, "\\\"", str)) + ; + + switch (*pos) { + case '\0': + if (cont == NULL) { + goto fail; + } else { + *ptr = pos; + *cont = true; + return true; + } + case '\"': + *ptr = pos+1; + if (cont != NULL) { + *cont = false; + } + return true; + + case '\\': + pos++; + if (!srprs_charset(&pos, "\\\"", str)) + goto fail; + break; + + default: + assert(false); + } + } + +fail: + cbuf_setpos(str, spos); + return false; +} + +bool srprs_hex(const char** ptr, size_t len, unsigned* u) +{ + const char *str = *ptr; + const char *pos = *ptr; + int ret; + size_t i; + char buf[8+1] = {}; + + assert((len >= 1) && (len <= 8)); + + for (i=0; i<len; i++) { + if (!srprs_charset(&pos, "0123456789abcdefABCDEF", NULL)) { + break; + } + buf[i] = str[i]; + } + + ret = sscanf(buf, "%8x", u); + + if ( ret != 1 ) { + return false; + } + + *ptr = pos; + return true; +} + +bool srprs_nl(const char** ptr, cbuf* nl) +{ + static const char CRLF[] = "\r\n"; + if (srprs_str(ptr, CRLF, sizeof(CRLF) - 1)) { + cbuf_puts(nl, CRLF, sizeof(CRLF) - 1); + return true; + } + return srprs_charset(ptr, "\n\r", nl); +} + +bool srprs_eos(const char** ptr) +{ + return (**ptr == '\0'); +} + +bool srprs_eol(const char** ptr, cbuf* nl) +{ + return srprs_eos(ptr) || srprs_nl(ptr, nl); +} + +bool srprs_line(const char** ptr, cbuf* str) +{ + while (srprs_charsetinv(ptr, "\n\r", str)) + ; + return true; +} + +bool srprs_quoted(const char** ptr, cbuf* str) +{ + const char* pos = *ptr; + const size_t spos = cbuf_getpos(str); + + if (!srprs_char(&pos, '"')) { + goto fail; + } + + while (true) { + while (srprs_charsetinv(&pos, "\\\"", str)) + ; + + switch (*pos) { + case '\0': + goto fail; + case '"': + *ptr = pos+1; + return true; + + case '\\': + pos++; + if (!srprs_charset(&pos, "\\\"", str)) { + unsigned u; + if (!srprs_hex(&pos, 2, &u)) { + goto fail; + } + cbuf_putc(str, u); + } + break; + default: + assert(false); + } + } + +fail: + cbuf_setpos(str, spos); + return false; +} diff --git a/source3/lib/srprs.h b/source3/lib/srprs.h new file mode 100644 index 0000000..fb3ca1b --- /dev/null +++ b/source3/lib/srprs.h @@ -0,0 +1,189 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2010 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @file srprs.h + * @author Gregor Beck <gb@sernet.de> + * @date Aug 2010 + * + * @brief A simple recursive parser. + * + * This file contains functions which may be used to build a simple recursive + * parser. They all take the parse position as their first argument. If they + * match the parse position and the output arguments are updated accordingly and + * true is returned else no argument is altered. For arguments of type @ref cbuf + * this may hold only up to the current write position. + */ + +#ifndef __SRPRS_H +#define __SRPRS_H + +struct cbuf; + +/** + * Matches any amount of whitespace. + * + * @see isspace + * @param ptr parse position + * + * @return true + */ +bool srprs_skipws(const char** ptr); + +/** + * Match a single character. + * + * @param[in,out] ptr parse position + * @param c the character to match + * + * @return true if matched + */ +bool srprs_char(const char** ptr, char c); + +/** + * Match a string. + * + * @param[in,out] ptr parse position + * @param str string to match + * @param len number of bytes to compare, -1 means strlen(str) + * + * @return true if matched + */ +bool srprs_str(const char** ptr, const char* str, ssize_t len); + +/** + * Match a single character from a set. + * Didn't match '\\0' + * + * @param[in,out] ptr parse position + * @param[in] set the character set to look for + * @param[out] oss output buffer where to put the match, may be NULL + * + * @return true if matched + */ +bool srprs_charset(const char** ptr, const char* set, struct cbuf* oss); + +/** + * Match a single character not in set. + * Didn't match '\\0' + * + * @param[in,out] ptr parse position + * @param[in] set the character set to look for + * @param[out] oss output buffer where to put the match, may be NULL + * + * @return true if matched + */ +bool srprs_charsetinv(const char** ptr, const char* set, struct cbuf* oss); + +/** + * Match a quoted string. + * + * + * If cont is not NULL the match may span multiple invocations. + * @code + * const char* start = "\"start..."; + * const char* cont = "continued..."; + * const char* end = "end\""; + * bool cont = false; + * cbuf* out = cbuf_new(talloc_tos()); + * srprs_quoted_string(&start, out, &cont); + * assert(*cont == true); + * srprs_quoted_string(&cont, out, &cont); + * assert(*cont == true); + * srprs_quoted_string(&end, out, &cont); + * assert(*cont == false); + * assert(strcmp(cbuf_gets(out, 0), "start...continued...end")==0); + * @endcode + * @see cbuf_print_quoted_string + * + * @param[in,out] ptr parse position + * @param[out] str output buffer where to put the match + * @param[in,out] cont + * + * @return true if matched + */ +bool srprs_quoted_string(const char** ptr, struct cbuf* str, bool* cont); + +/** + * Match a hex string. + * + * @param[in,out] ptr parse position + * @param len maximum number of digits to match + * @param[out] u value of the match + * + * @return true if matched + */ +bool srprs_hex(const char** ptr, size_t len, unsigned* u); + +/** + * Match the empty string at End Of String. + * It doesn't consume the '\\0' unlike + * @code + * srprs_char(ptr, '\0', NULL); + * @endcode + * + * @param[in,out] ptr parse position + * + * @return true if **ptr == '\\0' + */ +bool srprs_eos(const char** ptr); + +/** + * Match a newline. + * A newline is either '\\n' (LF), '\\r' (CR), or "\r\n" (CRLF) + * + * @param[in,out] ptr parse position + * @param[out] nl output buffer where to put the match, may be NULL + * + * @return true if matched + */ +bool srprs_nl(const char** ptr, struct cbuf* nl); + +/** + * Match a newline or eos. + * + * @param ptr parse position + * @param nl output buffer where to put the match, may be NULL + * + * @return true if matched + */ +bool srprs_eol(const char** ptr, struct cbuf* nl); + +/** + * Match a line up to but not including the newline. + * + * @param[in,out] ptr parse position + * @param[out] str output buffer where to put the match, may be NULL + * + * @return true + */ +bool srprs_line(const char** ptr, struct cbuf* str); + +/** + * Match a quoted string with escaped characters. + * @see cbuf_print_quoted + * + * @param[in,out] ptr parse position + * @param[out] str output buffer where to put the match + * + * @return true if matched + */ +bool srprs_quoted(const char** ptr, struct cbuf* str); + +#endif /* __SRPRS_H */ diff --git a/source3/lib/string_replace.c b/source3/lib/string_replace.c new file mode 100644 index 0000000..f66f548 --- /dev/null +++ b/source3/lib/string_replace.c @@ -0,0 +1,192 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) Volker Lendecke, 2005 + * Copyright (C) Aravind Srinivasan, 2009 + * Copyright (C) Guenter Kukkukk, 2013 + * Copyright (C) Ralph Boehme, 2017 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbd/smbd.h" +#include "string_replace.h" +#include "lib/util/string_wrappers.h" + +#define MAP_SIZE 0xFF +#define MAP_NUM 0x101 /* max unicode charval / MAP_SIZE */ +#define T_OFFSET(_v_) ((_v_ % MAP_SIZE)) +#define T_START(_v_) (((_v_ / MAP_SIZE) * MAP_SIZE)) +#define T_PICK(_v_) ((_v_ / MAP_SIZE)) + +struct char_mappings { + smb_ucs2_t entry[MAP_SIZE][2]; +}; + +static bool build_table(struct char_mappings **cmaps, int value) +{ + int i; + int start = T_START(value); + + (*cmaps) = talloc_zero(NULL, struct char_mappings); + + if (!*cmaps) + return False; + + for (i = 0; i < MAP_SIZE;i++) { + (*cmaps)->entry[i][vfs_translate_to_unix] = start + i; + (*cmaps)->entry[i][vfs_translate_to_windows] = start + i; + } + + return True; +} + +static void set_tables(struct char_mappings **cmaps, + long unix_map, + long windows_map) +{ + int i; + + /* set unix -> windows */ + i = T_OFFSET(unix_map); + cmaps[T_PICK(unix_map)]->entry[i][vfs_translate_to_windows] = windows_map; + + /* set windows -> unix */ + i = T_OFFSET(windows_map); + cmaps[T_PICK(windows_map)]->entry[i][vfs_translate_to_unix] = unix_map; +} + +static bool build_ranges(struct char_mappings **cmaps, + long unix_map, + long windows_map) +{ + + if (!cmaps[T_PICK(unix_map)]) { + if (!build_table(&cmaps[T_PICK(unix_map)], unix_map)) + return False; + } + + if (!cmaps[T_PICK(windows_map)]) { + if (!build_table(&cmaps[T_PICK(windows_map)], windows_map)) + return False; + } + + set_tables(cmaps, unix_map, windows_map); + + return True; +} + +struct char_mappings **string_replace_init_map(TALLOC_CTX *mem_ctx, + const char **mappings) +{ + int i; + char *tmp; + fstring mapping; + long unix_map, windows_map; + struct char_mappings **cmaps = NULL; + + if (mappings == NULL) { + return NULL; + } + + cmaps = talloc_zero_array(mem_ctx, struct char_mappings *, MAP_NUM); + if (cmaps == NULL) { + return NULL; + } + + /* + * catia mappings are of the form : + * UNIX char (in 0xnn hex) : WINDOWS char (in 0xnn hex) + * + * multiple mappings are comma separated in smb.conf + */ + + for (i = 0; mappings[i]; i++) { + fstrcpy(mapping, mappings[i]); + unix_map = strtol(mapping, &tmp, 16); + if (unix_map == 0 && errno == EINVAL) { + DEBUG(0, ("INVALID CATIA MAPPINGS - %s\n", mapping)); + continue; + } + windows_map = strtol(++tmp, NULL, 16); + if (windows_map == 0 && errno == EINVAL) { + DEBUG(0, ("INVALID CATIA MAPPINGS - %s\n", mapping)); + continue; + } + + if (!build_ranges(cmaps, unix_map, windows_map)) { + DEBUG(0, ("TABLE ERROR - CATIA MAPPINGS - %s\n", mapping)); + continue; + } + } + + return cmaps; +} + +NTSTATUS string_replace_allocate(connection_struct *conn, + const char *name_in, + struct char_mappings **cmaps, + TALLOC_CTX *mem_ctx, + char **mapped_name, + enum vfs_translate_direction direction) +{ + static smb_ucs2_t *tmpbuf = NULL; + smb_ucs2_t *ptr = NULL; + struct char_mappings *map = NULL; + size_t converted_size; + bool ok; + + ok = push_ucs2_talloc(talloc_tos(), &tmpbuf, name_in, + &converted_size); + if (!ok) { + return map_nt_error_from_unix(errno); + } + + for (ptr = tmpbuf; *ptr; ptr++) { + if (*ptr == 0) { + break; + } + if (cmaps == NULL) { + continue; + } + map = cmaps[T_PICK((*ptr))]; + if (map == NULL) { + /* nothing to do */ + continue; + } + + *ptr = map->entry[T_OFFSET((*ptr))][direction]; + } + + ok = pull_ucs2_talloc(mem_ctx, mapped_name, tmpbuf, + &converted_size); + TALLOC_FREE(tmpbuf); + if (!ok) { + return map_nt_error_from_unix(errno); + } + return NT_STATUS_OK; +} + +const char *macos_string_replace_map = + "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004," + "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008," + "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c," + "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010," + "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014," + "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018," + "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c," + "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f," + "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023," + "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027"; diff --git a/source3/lib/string_replace.h b/source3/lib/string_replace.h new file mode 100644 index 0000000..012b1aa --- /dev/null +++ b/source3/lib/string_replace.h @@ -0,0 +1,35 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) Volker Lendecke, 2005 + * Copyright (C) Aravind Srinivasan, 2009 + * Copyright (C) Guenter Kukkukk, 2013 + * Copyright (C) Ralph Boehme, 2017 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +struct char_mappings; + +struct char_mappings **string_replace_init_map(TALLOC_CTX *mem_ctx, + const char **mappings); + +NTSTATUS string_replace_allocate(connection_struct *conn, + const char *name_in, + struct char_mappings **cmaps, + TALLOC_CTX *mem_ctx, + char **mapped_name, + enum vfs_translate_direction direction); + +extern const char *macos_string_replace_map; diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c new file mode 100644 index 0000000..40eb15a --- /dev/null +++ b/source3/lib/substitute.c @@ -0,0 +1,675 @@ +/* + Unix SMB/CIFS implementation. + string substitution functions + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Gerald Carter 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#include "substitute.h" +#include "system/passwd.h" +#include "secrets.h" +#include "auth.h" +#include "lib/util/string_wrappers.h" + +/* Max DNS name is 253 + '\0' */ +#define MACHINE_NAME_SIZE 254 + +static char local_machine[MACHINE_NAME_SIZE]; +static char remote_machine[MACHINE_NAME_SIZE]; + +userdom_struct current_user_info; +static fstring remote_proto="UNKNOWN"; + +void set_remote_proto(const char *proto) +{ + fstrcpy(remote_proto, proto); +} + +/** + * Set the 'local' machine name + * @param local_name the name we are being called + * @param if this is the 'final' name for us, not be be changed again + */ +bool set_local_machine_name(const char *local_name, bool perm) +{ + static bool already_perm = false; + char tmp[MACHINE_NAME_SIZE]; + + if (already_perm) { + return true; + } + + strlcpy(tmp, local_name, sizeof(tmp)); + trim_char(tmp, ' ', ' '); + + alpha_strcpy(local_machine, + tmp, + SAFE_NETBIOS_CHARS, + sizeof(local_machine) - 1); + if (!strlower_m(local_machine)) { + return false; + } + + already_perm = perm; + + return true; +} + +const char *get_local_machine_name(void) +{ + if (local_machine[0] == '\0') { + return lp_netbios_name(); + } + + return local_machine; +} + +/** + * Set the 'remote' machine name + * + * @param remote_name the name our client wants to be called by + * @param if this is the 'final' name for them, not be be changed again + */ +bool set_remote_machine_name(const char *remote_name, bool perm) +{ + static bool already_perm = False; + char tmp[MACHINE_NAME_SIZE]; + + if (already_perm) { + return true; + } + + strlcpy(tmp, remote_name, sizeof(tmp)); + trim_char(tmp, ' ', ' '); + + alpha_strcpy(remote_machine, + tmp, + SAFE_NETBIOS_CHARS, + sizeof(remote_machine) - 1); + if (!strlower_m(remote_machine)) { + return false; + } + + already_perm = perm; + + return true; +} + +const char *get_remote_machine_name(void) +{ + return remote_machine; +} + +static char sub_peeraddr[INET6_ADDRSTRLEN]; +static const char *sub_peername = NULL; +static char sub_sockaddr[INET6_ADDRSTRLEN]; + +void sub_set_socket_ids(const char *peeraddr, const char *peername, + const char *sockaddr) +{ + const char *addr = peeraddr; + + if (strnequal(addr, "::ffff:", 7)) { + addr += 7; + } + strlcpy(sub_peeraddr, addr, sizeof(sub_peeraddr)); + + if (sub_peername != NULL && + sub_peername != sub_peeraddr) { + talloc_free(discard_const_p(char,sub_peername)); + sub_peername = NULL; + } + sub_peername = talloc_strdup(NULL, peername); + if (sub_peername == NULL) { + sub_peername = sub_peeraddr; + } + + /* + * Shouldn't we do the ::ffff: cancellation here as well? The + * original code in talloc_sub_basic() did not do it, so I'm + * leaving it out here as well for compatibility. + */ + strlcpy(sub_sockaddr, sockaddr, sizeof(sub_sockaddr)); +} + +/******************************************************************* + Setup the strings used by substitutions. Called per packet. Ensure + %U name is set correctly also. + + smb_name must be sanitized by alpha_strcpy +********************************************************************/ + +void set_current_user_info(const char *smb_name, const char *unix_name, + const char *domain) +{ + static const void *last_smb_name; + static const void *last_unix_name; + static const void *last_domain; + + if (likely(last_smb_name == smb_name && + last_unix_name == unix_name && + last_domain == domain)) + { + return; + } + + fstrcpy(current_user_info.smb_name, smb_name); + fstrcpy(current_user_info.unix_name, unix_name); + fstrcpy(current_user_info.domain, domain); + + last_smb_name = smb_name; + last_unix_name = unix_name; + last_domain = domain; +} + +/******************************************************************* + Return the current active user name. +*******************************************************************/ + +const char *get_current_username(void) +{ + return current_user_info.smb_name; +} + +const char *get_current_user_info_domain(void) +{ + return current_user_info.domain; +} + +/******************************************************************* + Given a pointer to a %$(NAME) in p and the whole string in str + expand it as an environment variable. + str must be a talloced string. + Return a new allocated and expanded string. + Based on code by Branko Cibej <branko.cibej@hermes.si> + When this is called p points at the '%' character. + May substitute multiple occurrences of the same env var. +********************************************************************/ + +static char *realloc_expand_env_var(char *str, char *p) +{ + char *envname; + char *envval; + char *q, *r; + int copylen; + + if (p[0] != '%' || p[1] != '$' || p[2] != '(') { + return str; + } + + /* + * Look for the terminating ')'. + */ + + if ((q = strchr_m(p,')')) == NULL) { + DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p)); + return str; + } + + /* + * Extract the name from within the %$(NAME) string. + */ + + r = p + 3; + copylen = q - r; + + /* reserve space for use later add %$() chars */ + if ( (envname = talloc_array(talloc_tos(), char, copylen + 1 + 4)) == NULL ) { + return NULL; + } + + strncpy(envname,r,copylen); + envname[copylen] = '\0'; + + if ((envval = getenv(envname)) == NULL) { + DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname)); + TALLOC_FREE(envname); + return str; + } + + /* + * Copy the full %$(NAME) into envname so it + * can be replaced. + */ + + copylen = q + 1 - p; + strncpy(envname,p,copylen); + envname[copylen] = '\0'; + r = realloc_string_sub(str, envname, envval); + TALLOC_FREE(envname); + + return r; +} + +/**************************************************************************** + Do some standard substitutions in a string. + len is the length in bytes of the space allowed in string str. If zero means + don't allow expansions. +****************************************************************************/ + +void standard_sub_basic(const char *smb_name, const char *domain_name, + char *str, size_t len) +{ + char *s; + + if ( (s = talloc_sub_basic(talloc_tos(), smb_name, domain_name, str )) != NULL ) { + strncpy( str, s, len ); + } + + TALLOC_FREE( s ); +} + +/* + * Limit addresses to hexalpha characters and underscore, safe for path + * components for Windows clients. + */ +static void make_address_pathsafe(char *addr) +{ + while(addr && *addr) { + if(!isxdigit(*addr)) { + *addr = '_'; + } + ++addr; + } +} + +/**************************************************************************** + Do some standard substitutions in a string. + This function will return a talloced string that has to be freed. +****************************************************************************/ + +char *talloc_sub_basic(TALLOC_CTX *mem_ctx, + const char *smb_name, + const char *domain_name, + const char *str) +{ + char *b, *p, *s, *r, *a_string; + fstring pidstr, vnnstr; + const char *local_machine_name = get_local_machine_name(); + TALLOC_CTX *tmp_ctx = NULL; + + /* workaround to prevent a crash while looking at bug #687 */ + + if (!str) { + DEBUG(0,("talloc_sub_basic: NULL source string! This should not happen\n")); + return NULL; + } + + a_string = talloc_strdup(mem_ctx, str); + if (a_string == NULL) { + DEBUG(0, ("talloc_sub_basic: Out of memory!\n")); + return NULL; + } + + tmp_ctx = talloc_stackframe(); + + for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { + + r = NULL; + b = a_string; + + switch (*(p+1)) { + case 'U' : + r = strlower_talloc(tmp_ctx, smb_name); + if (r == NULL) { + goto error; + } + a_string = realloc_string_sub(a_string, "%U", r); + break; + case 'G' : { + struct passwd *pass; + bool is_domain_name = false; + const char *sep = lp_winbind_separator(); + + if (domain_name != NULL && domain_name[0] != '\0' && + (lp_security() == SEC_ADS || + lp_security() == SEC_DOMAIN)) { + r = talloc_asprintf(tmp_ctx, + "%s%c%s", + domain_name, + *sep, + smb_name); + is_domain_name = true; + } else { + r = talloc_strdup(tmp_ctx, smb_name); + } + if (r == NULL) { + goto error; + } + + pass = Get_Pwnam_alloc(tmp_ctx, r); + if (pass != NULL) { + char *group_name; + + group_name = gidtoname(pass->pw_gid); + if (is_domain_name) { + char *group_sep; + group_sep = strchr_m(group_name, *sep); + if (group_sep != NULL) { + group_name = group_sep + 1; + } + } + a_string = realloc_string_sub(a_string, + "%G", + group_name); + } + TALLOC_FREE(pass); + break; + } + case 'D' : + r = strupper_talloc(tmp_ctx, domain_name); + if (r == NULL) { + goto error; + } + a_string = realloc_string_sub(a_string, "%D", r); + break; + case 'I' : { + a_string = realloc_string_sub( + a_string, "%I", + sub_peeraddr[0] ? sub_peeraddr : "0.0.0.0"); + break; + } + case 'J' : { + r = talloc_strdup(tmp_ctx, + sub_peeraddr[0] ? sub_peeraddr : "0.0.0.0"); + make_address_pathsafe(r); + a_string = realloc_string_sub(a_string, "%J", r); + break; + } + case 'i': + a_string = realloc_string_sub( + a_string, "%i", + sub_sockaddr[0] ? sub_sockaddr : "0.0.0.0"); + break; + case 'j' : { + r = talloc_strdup(tmp_ctx, + sub_sockaddr[0] ? sub_sockaddr : "0.0.0.0"); + make_address_pathsafe(r); + a_string = realloc_string_sub(a_string, "%j", r); + break; + } + case 'L' : + if ( strncasecmp_m(p, "%LOGONSERVER%", strlen("%LOGONSERVER%")) == 0 ) { + break; + } + if (local_machine_name && *local_machine_name) { + a_string = realloc_string_sub(a_string, "%L", local_machine_name); + } else { + a_string = realloc_string_sub(a_string, "%L", lp_netbios_name()); + } + break; + case 'N' : + a_string = realloc_string_sub(a_string, + "%N", + lp_netbios_name()); + break; + case 'M' : + a_string = realloc_string_sub(a_string, "%M", + sub_peername ? sub_peername : ""); + break; + case 'R' : + a_string = realloc_string_sub(a_string, "%R", remote_proto); + break; + case 'T' : + a_string = realloc_string_sub(a_string, "%T", current_timestring(tmp_ctx, False)); + break; + case 't' : + a_string = realloc_string_sub(a_string, "%t", + current_minimal_timestring(tmp_ctx, False)); + break; + case 'a' : + a_string = realloc_string_sub(a_string, "%a", + get_remote_arch_str()); + break; + case 'd' : + slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)getpid()); + a_string = realloc_string_sub(a_string, "%d", pidstr); + break; + case 'h' : + a_string = realloc_string_sub(a_string, "%h", myhostname()); + break; + case 'm' : + a_string = realloc_string_sub(a_string, "%m", + remote_machine); + break; + case 'v' : + a_string = realloc_string_sub(a_string, "%v", samba_version_string()); + break; + case 'w' : + a_string = realloc_string_sub(a_string, "%w", lp_winbind_separator()); + break; + case '$' : + a_string = realloc_expand_env_var(a_string, p); /* Expand environment variables */ + break; + case 'V' : + slprintf(vnnstr,sizeof(vnnstr)-1, "%u", get_my_vnn()); + a_string = realloc_string_sub(a_string, "%V", vnnstr); + break; + default: + break; + } + + p++; + TALLOC_FREE(r); + + if (a_string == NULL) { + goto done; + } + } + + goto done; + +error: + TALLOC_FREE(a_string); + +done: + TALLOC_FREE(tmp_ctx); + return a_string; +} + +/**************************************************************************** + Do some specific substitutions in a string. + This function will return an allocated string that have to be freed. +****************************************************************************/ + +char *talloc_sub_specified(TALLOC_CTX *mem_ctx, + const char *input_string, + const char *username, + const char *grpname, + const char *domain, + uid_t uid, + gid_t gid) +{ + char *a_string; + char *ret_string = NULL; + char *b, *p, *s; + TALLOC_CTX *tmp_ctx; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + DEBUG(0, ("talloc_new failed\n")); + return NULL; + } + + a_string = talloc_strdup(tmp_ctx, input_string); + if (a_string == NULL) { + DEBUG(0, ("talloc_sub_specified: Out of memory!\n")); + goto done; + } + + for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { + + b = a_string; + + switch (*(p+1)) { + case 'U' : + a_string = talloc_string_sub( + tmp_ctx, a_string, "%U", username); + break; + case 'u' : + a_string = talloc_string_sub( + tmp_ctx, a_string, "%u", username); + break; + case 'G' : + if (gid != -1) { + const char *name; + + if (grpname != NULL) { + name = grpname; + } else { + name = gidtoname(gid); + } + + a_string = talloc_string_sub(tmp_ctx, + a_string, + "%G", + name); + } else { + a_string = talloc_string_sub( + tmp_ctx, a_string, + "%G", "NO_GROUP"); + } + break; + case 'g' : + if (gid != -1) { + const char *name; + + if (grpname != NULL) { + name = grpname; + } else { + name = gidtoname(gid); + } + + a_string = talloc_string_sub(tmp_ctx, + a_string, + "%g", + name); + } else { + a_string = talloc_string_sub( + tmp_ctx, a_string, "%g", "NO_GROUP"); + } + break; + case 'D' : + a_string = talloc_string_sub(tmp_ctx, a_string, + "%D", domain); + break; + case 'N' : + a_string = talloc_string_sub(tmp_ctx, a_string, + "%N", lp_netbios_name()); + break; + default: + break; + } + + p++; + if (a_string == NULL) { + goto done; + } + } + + /* Watch out, using "mem_ctx" here, so all intermediate stuff goes + * away with the TALLOC_FREE(tmp_ctx) further down. */ + + ret_string = talloc_sub_basic(mem_ctx, username, domain, a_string); + + done: + TALLOC_FREE(tmp_ctx); + return ret_string; +} + +/**************************************************************************** +****************************************************************************/ + +char *talloc_sub_advanced(TALLOC_CTX *ctx, + const char *servicename, + const char *user, + const char *connectpath, + gid_t gid, + const char *str) +{ + char *a_string; + char *b, *p, *s; + + a_string = talloc_strdup(talloc_tos(), str); + if (a_string == NULL) { + DEBUG(0, ("talloc_sub_advanced_only: Out of memory!\n")); + return NULL; + } + + for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { + + b = a_string; + + switch (*(p+1)) { + case 'N': + a_string = realloc_string_sub(a_string, + "%N", + lp_netbios_name()); + break; + case 'H': { + char *h; + if ((h = get_user_home_dir(talloc_tos(), user))) + a_string = realloc_string_sub(a_string, "%H", h); + TALLOC_FREE(h); + break; + } + case 'P': + a_string = realloc_string_sub(a_string, "%P", connectpath); + break; + case 'S': + a_string = realloc_string_sub(a_string, "%S", servicename); + break; + case 'g': + a_string = realloc_string_sub(a_string, "%g", gidtoname(gid)); + break; + case 'u': + a_string = realloc_string_sub(a_string, "%u", user); + break; + default: + break; + } + + p++; + if (a_string == NULL) { + return NULL; + } + } + + return a_string; +} + +char *talloc_sub_full(TALLOC_CTX *ctx, + const char *servicename, + const char *user, + const char *connectpath, + gid_t gid, + const char *smb_name, + const char *domain_name, + const char *str) +{ + char *a_string, *ret_string; + + a_string = talloc_sub_advanced(ctx, servicename, user, connectpath, + gid, str); + if (a_string == NULL) { + return NULL; + } + + ret_string = talloc_sub_basic(ctx, smb_name, domain_name, a_string); + TALLOC_FREE(a_string); + return ret_string; +} diff --git a/source3/lib/substitute.h b/source3/lib/substitute.h new file mode 100644 index 0000000..6596ffd --- /dev/null +++ b/source3/lib/substitute.h @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + string substitution functions + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Gerald Carter 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef SUBSTITUTE_H +#define SUBSTITUTE_H + +void set_remote_proto(const char *proto); +bool set_local_machine_name(const char *local_name, bool perm); +const char *get_local_machine_name(void); +bool set_remote_machine_name(const char *remote_name, bool perm); +const char *get_remote_machine_name(void); +void sub_set_socket_ids(const char *peeraddr, const char *peername, + const char *sockaddr); +void set_current_user_info(const char *smb_name, + const char *unix_name, + const char *domain); +const char *get_current_username(void); +const char *get_current_user_info_domain(void); +void standard_sub_basic(const char *smb_name, + const char *domain_name, + char *str, + size_t len); +char *talloc_sub_basic(TALLOC_CTX *mem_ctx, + const char *smb_name, + const char *domain_name, + const char *str); +char *talloc_sub_specified(TALLOC_CTX *mem_ctx, + const char *input_string, + const char *username, + const char *grpname, + const char *domain, + uid_t uid, + gid_t gid); +char *talloc_sub_advanced(TALLOC_CTX *ctx, + const char *servicename, + const char *user, + const char *connectpath, + gid_t gid, + const char *str); +char *talloc_sub_full(TALLOC_CTX *ctx, + const char *servicename, + const char *user, + const char *connectpath, + gid_t gid, + const char *smb_name, + const char *domain_name, + const char *str); +#endif diff --git a/source3/lib/substitute_generic.c b/source3/lib/substitute_generic.c new file mode 100644 index 0000000..26c5ee7 --- /dev/null +++ b/source3/lib/substitute_generic.c @@ -0,0 +1,113 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Martin Pool 2003 + Copyright (C) James Peach 2006 + Copyright (C) Jeremy Allison 1992-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/** + Similar to string_sub2, but it will accept only allocated strings + and may realloc them so pay attention at what you pass on no + pointers inside strings, no const may be passed + as string. +**/ + +char *realloc_string_sub2(char *string, + const char *pattern, + const char *insert, + bool remove_unsafe_characters, + bool allow_trailing_dollar) +{ + char *p, *in; + char *s; + ssize_t ls,lp,li,ld, i; + + if (!insert || !pattern || !*pattern || !string || !*string) + return NULL; + + s = string; + + in = talloc_strdup(talloc_tos(), insert); + if (!in) { + DEBUG(0, ("realloc_string_sub: out of memory!\n")); + return NULL; + } + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + ld = li - lp; + for (i=0;i<li;i++) { + switch (in[i]) { + case '$': + /* allow a trailing $ + * (as in machine accounts) */ + if (allow_trailing_dollar && (i == li - 1 )) { + break; + } + FALL_THROUGH; + case '`': + case '"': + case '\'': + case ';': + case '%': + case '\r': + case '\n': + if ( remove_unsafe_characters ) { + in[i] = '_'; + break; + } + FALL_THROUGH; + default: + /* ok */ + break; + } + } + + while ((p = strstr_m(s,pattern))) { + if (ld > 0) { + int offset = PTR_DIFF(s,string); + string = talloc_realloc(NULL, string, char, ls + ld + 1); + if (!string) { + DEBUG(0, ("realloc_string_sub: " + "out of memory!\n")); + talloc_free(in); + return NULL; + } + p = string + offset + (p - s); + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + memcpy(p, in, li); + s = p + li; + ls += ld; + } + talloc_free(in); + return string; +} + +char *realloc_string_sub(char *string, + const char *pattern, + const char *insert) +{ + return realloc_string_sub2(string, pattern, insert, true, false); +} diff --git a/source3/lib/sysacls.c b/source3/lib/sysacls.c new file mode 100644 index 0000000..234094a --- /dev/null +++ b/source3/lib/sysacls.c @@ -0,0 +1,518 @@ +/* + Unix SMB/CIFS implementation. + Samba system utilities for ACL support. + Copyright (C) Jeremy Allison 2000. + Copyright (C) Volker Lendecke 2006 + Copyright (C) Michael Adam 2006,2008 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/passwd.h" + +#if defined(HAVE_POSIX_ACLS) +#include "modules/vfs_posixacl.h" +#endif + +#if defined(HAVE_SOLARIS_UNIXWARE_ACLS) +#include "modules/vfs_solarisacl.h" +#endif + +#if defined(HAVE_HPUX_ACLS) +#include "modules/vfs_hpuxacl.h" +#endif + +#if defined(HAVE_AIX_ACLS) +#include "modules/vfs_aixacl.h" +#endif + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_ACLS + +/* + * Note that while this code implements sufficient functionality + * to support the sys_acl_* interfaces it does not provide all + * of the semantics of the POSIX ACL interfaces. + * + * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned + * from a call to sys_acl_get_entry() should not be assumed to be + * valid after calling any of the following functions, which may + * reorder the entries in the ACL. + * + * sys_acl_valid() + * sys_acl_set_fd() + */ + +int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) { + errno = EINVAL; + return -1; + } + + if (entry_p == NULL) { + errno = EINVAL; + return -1; + } + + if (entry_id == SMB_ACL_FIRST_ENTRY) { + acl_d->next = 0; + } + + if (acl_d->next < 0) { + errno = EINVAL; + return -1; + } + + if (acl_d->next >= acl_d->count) { + return 0; + } + + *entry_p = &acl_d->acl[acl_d->next++]; + + return 1; +} + +int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p) +{ + *type_p = entry_d->a_type; + + return 0; +} + +int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + *permset_p = &entry_d->a_perm; + + return 0; +} + +void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d) +{ + if (entry_d->a_type == SMB_ACL_USER) { + return &entry_d->info.user.uid; + } + + if (entry_d->a_type == SMB_ACL_GROUP) { + return &entry_d->info.group.gid; + } + + errno = EINVAL; + return NULL; +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d) +{ + *permset_d = 0; + + return 0; +} + +int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE + && perm != SMB_ACL_EXECUTE) { + errno = EINVAL; + return -1; + } + + if (permset_d == NULL) { + errno = EINVAL; + return -1; + } + + *permset_d |= perm; + + return 0; +} + +int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + return *permset_d & perm; +} + +char *sys_acl_to_text(const struct smb_acl_t *acl_d, ssize_t *len_p) +{ + int i; + int len, maxlen; + char *text; + + /* + * use an initial estimate of 20 bytes per ACL entry + * when allocating memory for the text representation + * of the ACL + */ + len = 0; + maxlen = 20 * acl_d->count; + if ((text = (char *)SMB_MALLOC(maxlen)) == NULL) { + errno = ENOMEM; + return NULL; + } + + for (i = 0; i < acl_d->count; i++) { + struct smb_acl_entry *ap = &acl_d->acl[i]; + struct group *gr; + char tagbuf[12]; + char idbuf[12]; + const char *tag; + const char *id = ""; + char perms[4]; + int nbytes; + + switch (ap->a_type) { + /* + * for debugging purposes it's probably more + * useful to dump unknown tag types rather + * than just returning an error + */ + default: + slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x", + ap->a_type); + tag = tagbuf; + break; + + case SMB_ACL_USER: + id = uidtoname(ap->info.user.uid); + + FALL_THROUGH; + case SMB_ACL_USER_OBJ: + tag = "user"; + break; + + case SMB_ACL_GROUP: + if ((gr = getgrgid(ap->info.group.gid)) == NULL) { + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->info.group.gid); + id = idbuf; + } else { + id = gr->gr_name; + } + + FALL_THROUGH; + case SMB_ACL_GROUP_OBJ: + tag = "group"; + break; + + case SMB_ACL_OTHER: + tag = "other"; + break; + + case SMB_ACL_MASK: + tag = "mask"; + break; + + } + + perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-'; + perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-'; + perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-'; + perms[3] = '\0'; + + /* <tag> : <qualifier> : rwx \n \0 */ + nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1; + + /* + * If this entry would overflow the buffer + * allocate enough additional memory for this + * entry and an estimate of another 20 bytes + * for each entry still to be processed + */ + if ((len + nbytes) > maxlen) { + maxlen += nbytes + 20 * (acl_d->count - i); + if ((text = (char *)SMB_REALLOC(text, maxlen)) == NULL) { + errno = ENOMEM; + return NULL; + } + } + + + slprintf(&text[len], nbytes, "%s:%s:%s\n", tag, id, perms); + len += (nbytes - 1); + } + + if (len_p) + *len_p = len; + + return text; +} + +SMB_ACL_T sys_acl_init(TALLOC_CTX *mem_ctx) +{ + SMB_ACL_T a; + + if ((a = talloc(mem_ctx, struct smb_acl_t)) == NULL) { + errno = ENOMEM; + return NULL; + } + + a->count = 0; + a->next = -1; + + a->acl = talloc_array(a, struct smb_acl_entry, 0); + if (!a->acl) { + TALLOC_FREE(a); + errno = ENOMEM; + return NULL; + } + + return a; +} + +int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p) +{ + SMB_ACL_T acl_d; + SMB_ACL_ENTRY_T entry_d; + struct smb_acl_entry *acl; + + if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) { + errno = EINVAL; + return -1; + } + + acl = talloc_realloc(acl_d, acl_d->acl, struct smb_acl_entry, acl_d->count+1); + if (!acl) { + errno = ENOMEM; + return -1; + } + acl_d->acl = acl; + entry_d = &acl_d->acl[acl_d->count]; + entry_d->a_type = SMB_ACL_TAG_INVALID; + entry_d->a_perm = 0; + *entry_p = entry_d; + + acl_d->count++; + return 0; +} + +int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type) +{ + switch (tag_type) { + case SMB_ACL_USER: + case SMB_ACL_USER_OBJ: + case SMB_ACL_GROUP: + case SMB_ACL_GROUP_OBJ: + case SMB_ACL_OTHER: + case SMB_ACL_MASK: + entry_d->a_type = tag_type; + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p) +{ + if (entry_d->a_type == SMB_ACL_USER) { + entry_d->info.user.uid = *((uid_t *)qual_p); + return 0; + } + if (entry_d->a_type == SMB_ACL_GROUP) { + entry_d->info.group.gid = *((gid_t *)qual_p); + return 0; + } + + errno = EINVAL; + return -1; +} + +int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d) +{ + if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) { + errno = EINVAL; + return -1; + } + + entry_d->a_perm = *permset_d; + + return 0; +} + +int sys_acl_free_text(char *text) +{ + SAFE_FREE(text); + return 0; +} + +int sys_acl_valid(SMB_ACL_T acl_d) +{ + errno = EINVAL; + return -1; +} + +/* + * acl_get_file, acl_get_fd, acl_set_file, acl_set_fd and + * sys_acl_delete_def_fd are to be redirected to the default + * statically-bound acl vfs module, but they are replaceable. + */ + +#if defined(HAVE_POSIX_ACLS) + +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + TALLOC_CTX *mem_ctx) +{ + return posixacl_sys_acl_get_fd(handle, fsp, type, mem_ctx); +} + +int sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + SMB_ACL_T acl_d) +{ + return posixacl_sys_acl_set_fd(handle, fsp, type, acl_d); +} + +int sys_acl_delete_def_fd(vfs_handle_struct *handle, + files_struct *fsp) +{ + return posixacl_sys_acl_delete_def_fd(handle, fsp); +} + +#elif defined(HAVE_AIX_ACLS) + +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + TALLOC_CTX *mem_ctx) +{ + return aixacl_sys_acl_get_fd(handle, fsp, type, mem_ctx); +} + +int sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + SMB_ACL_T acl_d) +{ + return aixacl_sys_acl_set_fd(handle, fsp, type, acl_d); +} + +int sys_acl_delete_def_fd(vfs_handle_struct *handle, + files_struct *fsp) +{ + return aixacl_sys_acl_delete_def_fd(handle, fsp); +} +#elif defined(HAVE_SOLARIS_UNIXWARE_ACLS) + +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + TALLOC_CTX *mem_ctx) +{ + return solarisacl_sys_acl_get_fd(handle, fsp, type, + mem_ctx); +} + +int sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + SMB_ACL_T acl_d) +{ + return solarisacl_sys_acl_set_fd(handle, + fsp, + type, + acl_d); +} + +int sys_acl_delete_def_fd(vfs_handle_struct *handle, + files_struct *fsp) +{ + return solarisacl_sys_acl_delete_def_fd(handle, fsp); +} +#elif defined(HAVE_HPUX_ACLS) + +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + TALLOC_CTX *mem_ctx) +{ + return hpuxacl_sys_acl_get_fd(handle, fsp, mem_ctx); +} + +int sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + SMB_ACL_T acl_d) +{ + return hpuxacl_sys_acl_set_file(handle, fsp->fsp_name, type, acl_d); +} + +int sys_acl_delete_def_fd(vfs_handle_struct *handle, + files_struct *fsp) +{ + return hpuxacl_sys_acl_delete_def_fd(handle, fsp); +} +#else /* No ACLs. */ + +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + TALLOC_CTX *mem_ctx) +{ +#ifdef ENOTSUP + errno = ENOTSUP; +#else + errno = ENOSYS; +#endif + return NULL; +} + +int sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_TYPE_T type, + SMB_ACL_T acl_d) +{ +#ifdef ENOTSUP + errno = ENOTSUP; +#else + errno = ENOSYS; +#endif + return -1; +} + +int sys_acl_delete_def_fd(vfs_handle_struct *handle, + files_struct *fsp) +{ +#ifdef ENOTSUP + errno = ENOTSUP; +#else + errno = ENOSYS; +#endif + return -1; +} +#endif + +/************************************************************************ + Deliberately outside the ACL defines. Return 1 if this is a "no acls" + errno, 0 if not. +************************************************************************/ + +int no_acl_syscall_error(int err) +{ +#if defined(ENOSYS) + if (err == ENOSYS) { + return 1; + } +#endif +#if defined(ENOTSUP) + if (err == ENOTSUP) { + return 1; + } +#endif + return 0; +} diff --git a/source3/lib/sysquotas.c b/source3/lib/sysquotas.c new file mode 100644 index 0000000..b7eedcd --- /dev/null +++ b/source3/lib/sysquotas.c @@ -0,0 +1,619 @@ +/* + Unix SMB/CIFS implementation. + System QUOTA function wrappers + Copyright (C) Stefan (metze) Metzmacher 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#include "lib/util_file.h" +#include "lib/util/smb_strtox.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#ifdef HAVE_SYS_QUOTAS + +#if defined(HAVE_QUOTACTL_4A) + +/*#endif HAVE_QUOTACTL_4A */ +#elif defined(HAVE_QUOTACTL_4B) + +/*#endif HAVE_QUOTACTL_4B */ +#elif defined(HAVE_QUOTACTL_3) + +#error HAVE_QUOTACTL_3 not implemented + +/* #endif HAVE_QUOTACTL_3 */ +#else /* NO_QUOTACTL_USED */ + +#endif /* NO_QUOTACTL_USED */ + +#if defined(HAVE_MNTENT) && defined(HAVE_REALPATH) +static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs) +{ + int ret = -1; + SMB_STRUCT_STAT S; + FILE *fp; + struct mntent *mnt = NULL; + SMB_DEV_T devno; + char *stat_mntpath = NULL; + char *p; + + /* find the block device file */ + (*mntpath) = NULL; + (*bdev) = NULL; + (*fs) = NULL; + + if (sys_stat(path, &S, false) != 0) { + return -1; + } + + devno = S.st_ex_dev ; + + stat_mntpath = sys_realpath(path); + if (stat_mntpath == NULL) { + DBG_WARNING("realpath(%s) failed - %s\n", path, + strerror(errno)); + goto out; + } + + if (sys_stat(stat_mntpath, &S, false) != 0) { + DBG_WARNING("cannot stat real path %s - %s\n", stat_mntpath, + strerror(errno)); + goto out; + } + + if (S.st_ex_dev != devno) { + DBG_WARNING("device on real path has changed\n"); + goto out; + } + + while (true) { + char save_ch; + + p = strrchr(stat_mntpath, '/'); + if (p == NULL) { + DBG_ERR("realpath for %s does not begin with a '/'\n", + path); + goto out; + } + + if (p == stat_mntpath) { + ++p; + } + + save_ch = *p; + *p = 0; + if (sys_stat(stat_mntpath, &S, false) != 0) { + DBG_WARNING("cannot stat real path component %s - %s\n", + stat_mntpath, strerror(errno)); + goto out; + } + if (S.st_ex_dev != devno) { + *p = save_ch; + break; + } + + if (p <= stat_mntpath + 1) { + break; + } + } + + fp = setmntent(MOUNTED,"r"); + if (fp == NULL) { + goto out; + } + + while ((mnt = getmntent(fp))) { + if (!strequal(mnt->mnt_dir, stat_mntpath)) { + continue; + } + + if ( sys_stat(mnt->mnt_dir, &S, false) == -1 ) + continue ; + + if (S.st_ex_dev == devno) { + (*mntpath) = SMB_STRDUP(mnt->mnt_dir); + (*bdev) = SMB_STRDUP(mnt->mnt_fsname); + (*fs) = SMB_STRDUP(mnt->mnt_type); + if ((*mntpath)&&(*bdev)&&(*fs)) { + ret = 0; + } else { + SAFE_FREE(*mntpath); + SAFE_FREE(*bdev); + SAFE_FREE(*fs); + ret = -1; + } + + break; + } + } + + endmntent(fp) ; + +out: + SAFE_FREE(stat_mntpath); + return ret; +} +/* #endif HAVE_MNTENT */ +#elif defined(HAVE_DEVNM) + +/* we have this on HPUX, ... */ +static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs) +{ + int ret = -1; + char dev_disk[256]; + SMB_STRUCT_STAT S; + + if (!path||!mntpath||!bdev||!fs) + smb_panic("sys_path_to_bdev: called with NULL pointer"); + + (*mntpath) = NULL; + (*bdev) = NULL; + (*fs) = NULL; + + /* find the block device file */ + + if ((ret=sys_stat(path, &S, false))!=0) { + return ret; + } + + if ((ret=devnm(S_IFBLK, S.st_ex_dev, dev_disk, 256, 1))!=0) { + return ret; + } + + /* we should get the mntpath right... + * but I don't know how + * --metze + */ + (*mntpath) = SMB_STRDUP(path); + (*bdev) = SMB_STRDUP(dev_disk); + if ((*mntpath)&&(*bdev)) { + ret = 0; + } else { + SAFE_FREE(*mntpath); + SAFE_FREE(*bdev); + ret = -1; + } + + + return ret; +} + +/* #endif HAVE_DEVNM */ +#else +/* we should fake this up...*/ +static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs) +{ + int ret = -1; + + if (!path||!mntpath||!bdev||!fs) + smb_panic("sys_path_to_bdev: called with NULL pointer"); + + (*mntpath) = NULL; + (*bdev) = NULL; + (*fs) = NULL; + + (*mntpath) = SMB_STRDUP(path); + if (*mntpath) { + ret = 0; + } else { + SAFE_FREE(*mntpath); + ret = -1; + } + + return ret; +} +#endif + +/********************************************************************* + Now the list of all filesystem specific quota systems we have found +**********************************************************************/ +static struct { + const char *name; + int (*get_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp); + int (*set_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp); +} sys_quota_backends[] = { +#ifdef HAVE_JFS_QUOTA_H + {"jfs2", sys_get_jfs2_quota, sys_set_jfs2_quota}, +#endif +#if defined HAVE_XFS_QUOTAS + {"xfs", sys_get_xfs_quota, sys_set_xfs_quota}, + {"gfs", sys_get_xfs_quota, sys_set_xfs_quota}, + {"gfs2", sys_get_xfs_quota, sys_set_xfs_quota}, +#endif /* HAVE_XFS_QUOTAS */ +#ifdef HAVE_NFS_QUOTAS + {"nfs", sys_get_nfs_quota, sys_set_nfs_quota}, + {"nfs4", sys_get_nfs_quota, sys_set_nfs_quota}, +#endif /* HAVE_NFS_QUOTAS */ + {NULL, NULL, NULL} +}; + +static int command_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char *get_quota_command; + char **lines = NULL; + + get_quota_command = lp_get_quota_command(talloc_tos(), lp_sub); + if (get_quota_command && *get_quota_command) { + const char *p; + char *p2; + int _id = -1; + int error = 0; + char **argl = NULL; + + switch(qtype) { + case SMB_USER_QUOTA_TYPE: + case SMB_USER_FS_QUOTA_TYPE: + _id = id.uid; + break; + case SMB_GROUP_QUOTA_TYPE: + case SMB_GROUP_FS_QUOTA_TYPE: + _id = id.gid; + break; + default: + DEBUG(0,("invalid quota type.\n")); + return -1; + } + + argl = str_list_make_empty(talloc_tos()); + str_list_add_printf(&argl, "%s", get_quota_command); + str_list_add_printf(&argl, "%s", path); + str_list_add_printf(&argl, "%d", qtype); + str_list_add_printf(&argl, "%d", _id); + if (argl == NULL) { + return -1; + } + + DBG_NOTICE("Running command %s %s %d %d\n", + get_quota_command, + path, + qtype, + _id); + + lines = file_lines_ploadv(talloc_tos(), argl, NULL); + TALLOC_FREE(argl); + + if (lines) { + char *line = lines[0]; + + DEBUG (3, ("Read output from get_quota, \"%s\"\n", line)); + + /* we need to deal with long long unsigned here, if supported */ + + dp->qflags = smb_strtoul(line, + &p2, + 10, + &error, + SMB_STR_STANDARD); + if (error != 0) { + goto invalid_param; + } + + p = p2; + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p); + } else { + goto invalid_param; + } + + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p); + } else { + goto invalid_param; + } + + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p); + } else { + goto invalid_param; + } + + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p); + } else { + goto invalid_param; + } + + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p); + } else { + goto invalid_param; + } + + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p); + } else { + goto invalid_param; + } + + while (p && *p && isspace(*p)) { + p++; + } + + if (p && *p) { + dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL); + } else { + dp->bsize = 1024; + } + + TALLOC_FREE(lines); + lines = NULL; + + DEBUG (3, ("Parsed output of get_quota, ...\n")); + + DEBUGADD (5,( + "qflags:%"PRIu32" curblocks:%"PRIu64" softlimit:%"PRIu64" hardlimit:%"PRIu64"\n" + "curinodes:%"PRIu64" isoftlimit:%"PRIu64" ihardlimit:%"PRIu64" bsize:%"PRIu64"\n", + dp->qflags,dp->curblocks, + dp->softlimit,dp->hardlimit, + dp->curinodes, + dp->isoftlimit,dp->ihardlimit, + dp->bsize)); + return 0; + } + + DEBUG (0, ("get_quota_command failed!\n")); + return -1; + } + + errno = ENOSYS; + return -1; + +invalid_param: + + TALLOC_FREE(lines); + DEBUG(0,("The output of get_quota_command is invalid!\n")); + return -1; +} + +static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char *set_quota_command; + + set_quota_command = lp_set_quota_command(talloc_tos(), lp_sub); + if (set_quota_command && *set_quota_command) { + char **lines = NULL; + int _id = -1; + char **argl = NULL; + + switch(qtype) { + case SMB_USER_QUOTA_TYPE: + case SMB_USER_FS_QUOTA_TYPE: + _id = id.uid; + break; + case SMB_GROUP_QUOTA_TYPE: + case SMB_GROUP_FS_QUOTA_TYPE: + _id = id.gid; + break; + default: + return -1; + } + + argl = str_list_make_empty(talloc_tos()); + str_list_add_printf(&argl, "%s", set_quota_command); + str_list_add_printf(&argl, "%s", path); + str_list_add_printf(&argl, "%d", qtype); + str_list_add_printf(&argl, "%d", _id); + str_list_add_printf(&argl, "%u", dp->qflags); + str_list_add_printf( + &argl, "%"PRIu64, dp->softlimit); + str_list_add_printf( + &argl, "%"PRIu64, dp->hardlimit); + str_list_add_printf( + &argl, "%"PRIu64, dp->isoftlimit); + str_list_add_printf( + &argl, "%"PRIu64, dp->ihardlimit); + str_list_add_printf( + &argl, "%"PRIu64, dp->bsize); + if (argl == NULL) { + return -1; + } + + DBG_NOTICE("Running command " + "%s %s %d %d " + "%"PRIu32" %"PRIu64" %"PRIu64" " + "%"PRIu64" %"PRIu64" %"PRIu64"\n", + set_quota_command, + path, + qtype, + _id, + dp->qflags, + dp->softlimit, + dp->hardlimit, + dp->isoftlimit, + dp->ihardlimit, + dp->bsize); + + lines = file_lines_ploadv(talloc_tos(), argl, NULL); + TALLOC_FREE(argl); + if (lines) { + char *line = lines[0]; + + DEBUG (3, ("Read output from set_quota, \"%s\"\n", line)); + + TALLOC_FREE(lines); + + return 0; + } + DEBUG (0, ("set_quota_command failed!\n")); + return -1; + } + + errno = ENOSYS; + return -1; +} + +int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + int i; + bool ready = False; + char *mntpath = NULL; + char *bdev = NULL; + char *fs = NULL; + + if (!path||!dp) + smb_panic("sys_get_quota: called with NULL pointer"); + + if (command_get_quota(path, qtype, id, dp)==0) { + return 0; + } else if (errno != ENOSYS) { + return -1; + } + + if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) { + DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path)); + return ret; + } + + errno = 0; + DEBUG(10,("sys_get_quota() uid(%u, %u), fs(%s)\n", (unsigned)getuid(), (unsigned)geteuid(), fs)); + + for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) { + if (strcmp(fs,sys_quota_backends[i].name)==0) { + ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp); + if (ret!=0) { + DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n", + fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno))); + } else { + DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n", + fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid))); + } + ready = True; + break; + } + } + + if (!ready) { + /* use the default vfs quota functions */ + ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp); + if (ret!=0) { + DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n", + "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno))); + } else { + DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n", + "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid))); + } + } + + SAFE_FREE(mntpath); + SAFE_FREE(bdev); + SAFE_FREE(fs); + + return ret; +} + +int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + int i; + bool ready = False; + char *mntpath = NULL; + char *bdev = NULL; + char *fs = NULL; + + /* find the block device file */ + + if (!path||!dp) + smb_panic("get_smb_quota: called with NULL pointer"); + + if (command_set_quota(path, qtype, id, dp)==0) { + return 0; + } else if (errno != ENOSYS) { + return -1; + } + + if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) { + DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path)); + return ret; + } + + errno = 0; + DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid())); + + for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) { + if (strcmp(fs,sys_quota_backends[i].name)==0) { + ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp); + if (ret!=0) { + DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n", + fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno))); + } else { + DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n", + fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid))); + } + ready = True; + break; + } + } + + if (!ready) { + /* use the default vfs quota functions */ + ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp); + if (ret!=0) { + DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n", + "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno))); + } else { + DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n", + "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid))); + } + } + + SAFE_FREE(mntpath); + SAFE_FREE(bdev); + SAFE_FREE(fs); + + return ret; +} + +#else /* HAVE_SYS_QUOTAS */ + void dummy_sysquotas_c(void); + + void dummy_sysquotas_c(void) +{ + return; +} +#endif /* HAVE_SYS_QUOTAS */ + diff --git a/source3/lib/sysquotas_4A.c b/source3/lib/sysquotas_4A.c new file mode 100644 index 0000000..674c4ee --- /dev/null +++ b/source3/lib/sysquotas_4A.c @@ -0,0 +1,334 @@ +/* + Unix SMB/CIFS implementation. + System QUOTA function wrappers for QUOTACTL_4A + Copyright (C) Stefan (metze) Metzmacher 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#ifndef HAVE_SYS_QUOTAS +#ifdef HAVE_QUOTACTL_4A +#undef HAVE_QUOTACTL_4A +#endif +#endif + +#ifdef HAVE_QUOTACTL_4A +/* long quotactl(int cmd, char *special, qid_t id, caddr_t addr) */ +/* this is used by: HPUX,IRIX */ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_ASM_TYPES_H +#include <asm/types.h> +#endif + +#ifdef HAVE_SYS_QUOTA_H +#include <sys/quota.h> +#endif + +#ifndef Q_SETQLIM +#define Q_SETQLIM Q_SETQUOTA +#endif + +#ifndef QCMD +#define QCMD(x,y) x +#endif + +#ifndef QCMD +#define QCMD(x,y) x +#endif + +#ifdef GRPQUOTA +#define HAVE_GROUP_QUOTA +#endif + +#ifndef QUOTABLOCK_SIZE +#define QUOTABLOCK_SIZE DEV_BSIZE +#endif + +#ifdef HAVE_DQB_FSOFTLIMIT +#define dqb_isoftlimit dqb_fsoftlimit +#define dqb_ihardlimit dqb_fhardlimit +#define dqb_curinodes dqb_curfiles +#endif + +#ifdef INITQFNAMES +#define USERQUOTAFILE_EXTENSION ".user" +#else +#define USERQUOTAFILE_EXTENSION "" +#endif + +#if !defined(QUOTAFILENAME) && defined(QFILENAME) +#define QUOTAFILENAME QFILENAME +#endif + +/**************************************************************************** + Abstract out the quotactl_4A get calls. +****************************************************************************/ +int sys_get_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + uint32_t qflags = 0; + struct dqblk D; + uint64_t bsize = (uint64_t)QUOTABLOCK_SIZE; + + ZERO_STRUCT(D); + ZERO_STRUCT(*dp); + dp->qtype = qtype; + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n", + path, bdev, (unsigned)id.uid)); + + if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D))&&errno != EDQUOT) { + return ret; + } + + ret = 0; + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_QUOTA_TYPE: + DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n", + path, bdev, (unsigned)id.gid)); + + if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D))&&errno != EDQUOT) { + return ret; + } + + ret = 0; + break; +#endif /* HAVE_GROUP_QUOTA */ + case SMB_USER_FS_QUOTA_TYPE: + id.uid = getuid(); + + DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n", + path, (caddr_t)bdev, (unsigned)id.uid)); + + if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D))==0) { + qflags |= QUOTAS_DENY_DISK; + } + + ret = 0; + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_FS_QUOTA_TYPE: + id.gid = getgid(); + + DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n", + path, bdev, (unsigned)id.gid)); + + if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D))==0) { + qflags |= QUOTAS_DENY_DISK; + } + + ret = 0; + break; +#endif /* HAVE_GROUP_QUOTA */ + default: + errno = ENOSYS; + return -1; + } + + dp->bsize = bsize; + dp->softlimit = (uint64_t)D.dqb_bsoftlimit; + dp->hardlimit = (uint64_t)D.dqb_bhardlimit; + dp->ihardlimit = (uint64_t)D.dqb_ihardlimit; + dp->isoftlimit = (uint64_t)D.dqb_isoftlimit; + dp->curinodes = (uint64_t)D.dqb_curinodes; + dp->curblocks = (uint64_t)D.dqb_curblocks; + + + dp->qflags = qflags; + + return ret; +} + +/**************************************************************************** + Abstract out the quotactl_4A set calls. +****************************************************************************/ +int sys_set_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + uint32_t qflags = 0; + uint32_t oldqflags = 0; + struct dqblk D; + uint64_t bsize = (uint64_t)QUOTABLOCK_SIZE; + + ZERO_STRUCT(D); + + if (bsize == dp->bsize) { + D.dqb_bsoftlimit = dp->softlimit; + D.dqb_bhardlimit = dp->hardlimit; + D.dqb_ihardlimit = dp->ihardlimit; + D.dqb_isoftlimit = dp->isoftlimit; + } else { + D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize; + D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize; + D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize; + D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize; + } + + qflags = dp->qflags; + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n", + path, bdev, (unsigned)id.uid)); + + ret = quotactl(QCMD(Q_SETQLIM,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D); + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_QUOTA_TYPE: + DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n", + path, bdev, (unsigned)id.gid)); + + ret = quotactl(QCMD(Q_SETQLIM,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D); + break; +#endif /* HAVE_GROUP_QUOTA */ + case SMB_USER_FS_QUOTA_TYPE: + /* this stuff didn't work as it should: + * switching on/off quota via quotactl() + * didn't work! + * So we just return 0 + * --metze + * + * On HPUX we didn't have the mount path, + * we need to fix sys_path_to_bdev() + * + */ + id.uid = getuid(); + DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n", + path, bdev, (unsigned)id.uid)); + +#if 0 + ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D); + + if ((qflags"AS_DENY_DISK)||(qflags"AS_ENABLED)) { + if (ret == 0) { + char *quota_file = NULL; + + asprintf("a_file,"/%s/%s%s",path, QUOTAFILENAME,USERQUOTAFILE_EXTENSION); + if (quota_file == NULL) { + DEBUG(0,("asprintf() failed!\n")); + errno = ENOMEM; + return -1; + } + + ret = quotactl(QCMD(Q_QUOTAON,USRQUOTA), (caddr_t)bdev, -1,(void *)quota_file); + } else { + ret = 0; + } + } else { + if (ret != 0) { + /* turn off */ + ret = quotactl(QCMD(Q_QUOTAOFF,USRQUOTA), (caddr_t)bdev, -1, (void *)0); + } else { + ret = 0; + } + } + + DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n", + ret,errno,strerror(errno),id.uid,bdev)); +#else + if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D))==0) { + oldqflags |= QUOTAS_DENY_DISK; + } + + if (oldqflags == qflags) { + ret = 0; + } else { + ret = -1; + } +#endif + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_FS_QUOTA_TYPE: + /* this stuff didn't work as it should: + * switching on/off quota via quotactl() + * didn't work! + * So we just return 0 + * --metze + * + * On HPUX we didn't have the mount path, + * we need to fix sys_path_to_bdev() + * + */ + id.gid = getgid(); + DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n", + path, bdev, (unsigned)id.gid)); + +#if 0 + ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id, (void *)&D); + + if ((qflags"AS_DENY_DISK)||(qflags"AS_ENABLED)) { + if (ret == 0) { + char *quota_file = NULL; + + asprintf("a_file,"/%s/%s%s",path, QUOTAFILENAME,GROUPQUOTAFILE_EXTENSION); + if (quota_file == NULL) { + DEBUG(0,("asprintf() failed!\n")); + errno = ENOMEM; + return -1; + } + + ret = quotactl(QCMD(Q_QUOTAON,GRPQUOTA), (caddr_t)bdev, -1,(void *)quota_file); + } else { + ret = 0; + } + } else { + if (ret != 0) { + /* turn off */ + ret = quotactl(QCMD(Q_QUOTAOFF,GRPQUOTA), (caddr_t)bdev, -1, (void *)0); + } else { + ret = 0; + } + } + + DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n", + ret,errno,strerror(errno),id.gid,bdev)); +#else + if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D))==0) { + oldqflags |= QUOTAS_DENY_DISK; + } + + if (oldqflags == qflags) { + ret = 0; + } else { + ret = -1; + } +#endif + break; +#endif /* HAVE_GROUP_QUOTA */ + default: + errno = ENOSYS; + return -1; + } + + return ret; +} + +#else /* HAVE_QUOTACTL_4A */ + void dummy_sysquotas_4A(void); + + void dummy_sysquotas_4A(void){} +#endif /* HAVE_QUOTACTL_4A */ diff --git a/source3/lib/sysquotas_4B.c b/source3/lib/sysquotas_4B.c new file mode 100644 index 0000000..d9beb92 --- /dev/null +++ b/source3/lib/sysquotas_4B.c @@ -0,0 +1,243 @@ +/* + * Unix SMB/CIFS implementation. + * System QUOTA function wrappers for QUOTACTL_4B + + * Copyright (C) 2011 James Peach. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#ifndef HAVE_SYS_QUOTAS +#undef HAVE_QUOTACTL_4B +#endif + +#ifdef HAVE_QUOTACTL_4B +/* int quotactl(const char *path, int cmd, int id, char *addr) + * + * This is used by many (all?) BSD-derived systems. This implementation has + * been developed and tested on Darwin, but may also work on other BSD systems. + */ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_QUOTA_H +#include <sys/quota.h> +#endif + +#ifdef HAVE_UFS_UFS_QUOTA_H +#include <ufs/ufs/quota.h> +#endif + +#ifdef HAVE_JFS_QUOTA_H +#include <jfs/quota.h> +#endif + +#if defined(DARWINOS) +/* WorkARound broken HFS access checks in hfs_quotactl. Darwin only(?) */ +#define HFS_QUOTACTL_WAR 1 +#endif + +#ifdef HAVE_STRUCT_DQBLK_DQB_CURBYTES +/* we handle the byte vs. block count dynamically via QUOTABLOCK_SIZE 1 */ +#define dqb_curblocks dqb_curbytes +#endif + +static void xlate_qblk_to_smb(const struct dqblk * const qblk, + SMB_DISK_QUOTA *dp) +{ + ZERO_STRUCTP(dp); + + DEBUG(10, ("unix softlimit=%u hardlimit=%u curblock=%u\n", + (unsigned)qblk->dqb_bsoftlimit, (unsigned)qblk->dqb_bhardlimit, + (unsigned)qblk->dqb_curblocks)); + + DEBUGADD(10, ("unix softinodes=%u hardinodes=%u curinodes=%u\n", + (unsigned)qblk->dqb_isoftlimit, (unsigned)qblk->dqb_ihardlimit, + (unsigned)qblk->dqb_curinodes)); + + dp->bsize = QUOTABLOCK_SIZE; + + dp->softlimit = qblk->dqb_bsoftlimit; + dp->hardlimit = qblk->dqb_bhardlimit; + dp->curblocks = qblk->dqb_curblocks; +/* our Darwin quotas used to never return 0byte usage but this is probably not needed, + * let's comment this out for now +#ifdef HAVE_STRUCT_DQBLK_DQB_CURBYTES + if (dp->curblocks == 0) { + dp->curblocks = 1; + } +#endif + */ + + dp->ihardlimit = qblk->dqb_ihardlimit; + dp->isoftlimit = qblk->dqb_isoftlimit; + dp->curinodes = qblk->dqb_curinodes; + + dp->qflags = QUOTAS_ENABLED | QUOTAS_DENY_DISK; + + DEBUG(10, ("softlimit=%u hardlimit=%u curblock=%u\n", + (unsigned)dp->softlimit, (unsigned)dp->hardlimit, + (unsigned)dp->curblocks)); + + DEBUGADD(10, ("softinodes=%u hardinodes=%u curinodes=%u\n", + (unsigned)dp->isoftlimit, (unsigned)dp->ihardlimit, + (unsigned)dp->curinodes)); + +} + +static void xlate_smb_to_qblk(const SMB_DISK_QUOTA * const dp, + struct dqblk *qblk) +{ + ZERO_STRUCTP(qblk); + + if (dp->bsize == QUOTABLOCK_SIZE) { + qblk->dqb_bsoftlimit = dp->softlimit; + qblk->dqb_bhardlimit = dp->hardlimit; + } else { + qblk->dqb_bsoftlimit = dp->softlimit * dp->bsize / QUOTABLOCK_SIZE; + qblk->dqb_bhardlimit = dp->hardlimit * dp->bsize / QUOTABLOCK_SIZE; + } + qblk->dqb_ihardlimit = dp->ihardlimit; + qblk->dqb_isoftlimit = dp->isoftlimit; +} + +static int sys_quotactl_4B(const char * path, int cmd, + int id, struct dqblk *qblk) +{ + int ret; + + /* NB: We must test GRPQUOTA here, because USRQUOTA is 0. */ + DEBUG(10, ("%s quota for %s ID %u on %s\n", + (cmd & QCMD(Q_GETQUOTA, 0)) ? "getting" : "setting", + (cmd & QCMD(0, GRPQUOTA)) ? "group" : "user", + (unsigned)id, path)); + +#ifdef HFS_QUOTACTL_WAR + become_root(); +#endif /* HFS_QUOTACTL_WAR */ + + ret = quotactl(path, cmd, id, qblk); + if (ret == -1) { + /* ENOTSUP means quota support is not compiled in. EINVAL + * means that quotas are not configured (commonly). + */ + if (errno != ENOTSUP && errno != EINVAL) { + DEBUG(5, ("failed to %s quota for %s ID %u on %s: %s\n", + (cmd & QCMD(Q_GETQUOTA, 0)) ? "get" : "set", + (cmd & QCMD(0, GRPQUOTA)) ? "group" : "user", + (unsigned)id, path, strerror(errno))); + } + +#ifdef HFS_QUOTACTL_WAR + unbecome_root(); +#endif /* HFS_QUOTACTL_WAR */ + + + return -1; + } + +#ifdef HFS_QUOTACTL_WAR + unbecome_root(); +#endif /* HFS_QUOTACTL_WAR */ + + return 0; +} + +int sys_get_vfs_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret; + struct dqblk qblk; + + ZERO_STRUCT(qblk); + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + /* Get quota for provided UID. */ + ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, USRQUOTA), + id.uid, &qblk); + break; + case SMB_USER_FS_QUOTA_TYPE: + /* Get quota for current UID. */ + ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, USRQUOTA), + geteuid(), &qblk); + break; + case SMB_GROUP_QUOTA_TYPE: + /* Get quota for provided GID. */ + ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, GRPQUOTA), + id.gid, &qblk); + break; + case SMB_GROUP_FS_QUOTA_TYPE: + /* Get quota for current GID. */ + ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, GRPQUOTA), + getegid(), &qblk); + break; + default: + DEBUG(0, ("cannot get unsupported quota type: %u\n", + (unsigned)qtype)); + errno = ENOSYS; + return -1; + } + + if (ret == -1) { + return -1; + } + + xlate_qblk_to_smb(&qblk, dp); + dp->qtype = qtype; + + return ret; +} + +int sys_set_vfs_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + struct dqblk qblk; + + xlate_smb_to_qblk(dp, &qblk); + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + /* Set quota for provided UID. */ + return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, USRQUOTA), + id.uid, &qblk); + case SMB_USER_FS_QUOTA_TYPE: + /* Set quota for current UID. */ + return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, USRQUOTA), + geteuid(), &qblk); + case SMB_GROUP_QUOTA_TYPE: + /* Set quota for provided GID. */ + return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, GRPQUOTA), + id.gid, &qblk); + case SMB_GROUP_FS_QUOTA_TYPE: + /* Set quota for current GID. */ + return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, GRPQUOTA), + getegid(), &qblk); + default: + DEBUG(0, ("cannot set unsupported quota type: %u\n", + (unsigned)qtype)); + errno = ENOSYS; + return -1; + } +} + +#endif /* HAVE_QUOTACTL_4B */ diff --git a/source3/lib/sysquotas_jfs2.c b/source3/lib/sysquotas_jfs2.c new file mode 100644 index 0000000..999b7e0 --- /dev/null +++ b/source3/lib/sysquotas_jfs2.c @@ -0,0 +1,150 @@ +/* + * Unix SMB/CIFS implementation. + * System QUOTA function wrappers for JFS2 on AIX + + * Copyright (C) 2019 Bjoern Jacke + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#if defined(HAVE_JFS_QUOTA_H) +#include <jfs/quota.h> + +#if defined(Q_J2GETQUOTA) /* when have JFS2 */ + +/* int quotactl(const char *path, int cmd, int id, char *addr) + * + * This is very similar to sysquotas_4B but JFS2 has different quota cmds + * (why?) and for some reason wants root even for querying your own quota, + * which seems to be an AIX bug because the docs say root is only + * required for querying other users' quota + */ + + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_JFS_QUOTA_H +#include <jfs/quota.h> +#endif + + +static int sys_quotactl_JFS2(const char * path, int cmd, + int id, quota64_t *quota) +{ + int ret; + + /* NB: We must test GRPQUOTA here, because USRQUOTA is 0. */ + DEBUG(10, ("%s quota for %s ID %u on %s\n", + (cmd & QCMD(Q_J2GETQUOTA, 0)) ? "getting" : "setting", + (cmd & QCMD(0, GRPQUOTA)) ? "group" : "user", + (unsigned)id, path)); + + become_root(); + + ret = quotactl((char *) path, cmd, id, (char *) quota); + if (ret == -1) { + /* ENOTSUP means quota support is not compiled in. EINVAL + * means that quotas are not configured (commonly). + */ + if (errno != ENOTSUP && errno != EINVAL) { + DEBUG(0, ("failed to %s quota for %s ID %u on %s: %s\n", + (cmd & QCMD(Q_J2GETQUOTA, 0)) ? "get" : "set", + (cmd & QCMD(0, GRPQUOTA)) ? "group" : "user", + (unsigned)id, path, strerror(errno))); + } + } + + unbecome_root(); + + return ret; +} + + +int sys_get_jfs2_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret; + quota64_t quota; + + ZERO_STRUCT(quota); + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + /* Get quota for provided UID. */ + ret = sys_quotactl_JFS2(path, QCMD(Q_J2GETQUOTA, USRQUOTA), + id.uid, "a); + break; + case SMB_USER_FS_QUOTA_TYPE: + /* Get quota for current UID. */ + ret = sys_quotactl_JFS2(path, QCMD(Q_J2GETQUOTA, USRQUOTA), + geteuid(), "a); + break; + case SMB_GROUP_QUOTA_TYPE: + /* Get quota for provided GID. */ + ret = sys_quotactl_JFS2(path, QCMD(Q_J2GETQUOTA, GRPQUOTA), + id.gid, "a); + break; + case SMB_GROUP_FS_QUOTA_TYPE: + /* Get quota for current GID. */ + ret = sys_quotactl_JFS2(path, QCMD(Q_J2GETQUOTA, GRPQUOTA), + getegid(), "a); + break; + default: + DEBUG(0, ("cannot get unsupported quota type: %u\n", + (unsigned)qtype)); + errno = ENOSYS; + return -1; + } + + if (ret == -1) { + return -1; + } + + dp->softlimit = quota.bsoft; + dp->hardlimit = quota.bhard; + dp->ihardlimit = quota.ihard; + dp->isoftlimit = quota.isoft; + dp->curinodes = quota.iused; + dp->curblocks = quota.bused; + + dp->qflags = QUOTAS_ENABLED | QUOTAS_DENY_DISK; + dp->qtype = qtype; + dp->bsize = QUOTABLOCK_SIZE; + + return ret; +} + +int sys_set_jfs2_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + /* JFS2 supports fancy quota limit classes for setting user quota. + * Unfortunately, this makes them quite unmanagable for Samba. + */ + DEBUG(1, ("cannot set quota on JFS2!\n")); + errno = ENOSYS; + return -1; +} + + +#endif /* JFS2 */ +#endif /* AIX quotas */ diff --git a/source3/lib/sysquotas_linux.c b/source3/lib/sysquotas_linux.c new file mode 100644 index 0000000..4415f51 --- /dev/null +++ b/source3/lib/sysquotas_linux.c @@ -0,0 +1,220 @@ +/* + Unix SMB/CIFS implementation. + System QUOTA function wrappers for LINUX + Copyright (C) Stefan (metze) Metzmacher 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#ifndef HAVE_SYS_QUOTAS +#ifdef HAVE_QUOTACTL_LINUX +#undef HAVE_QUOTACTL_LINUX +#endif +#endif + +#ifdef HAVE_QUOTACTL_LINUX + +#include <sys/quota.h> + +/**************************************************************************** + Linux quota get calls. +****************************************************************************/ +int sys_get_vfs_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + uint32_t qflags = 0; + struct dqblk D; + uint64_t bsize = (uint64_t)QUOTABLOCK_SIZE; + + if (!path || !bdev || !dp) { + smb_panic("sys_get_vfs_quota: called with NULL pointer"); + } + + ZERO_STRUCT(*dp); + dp->qtype = qtype; + + ZERO_STRUCT(D); + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + DEBUG(10, ("sys_get_vfs_quota: path[%s] bdev[%s] " + "SMB_USER_QUOTA_TYPE uid[%u]\n", + path, bdev, (unsigned)id.uid)); + + if ((ret = quotactl(QCMD(Q_GETQUOTA, USRQUOTA), bdev, + id.uid, (caddr_t)&D))) { + return ret; + } + + break; + case SMB_GROUP_QUOTA_TYPE: + DEBUG(10, ("sys_get_vfs_quota: path[%s] bdev[%s] " + "SMB_GROUP_QUOTA_TYPE gid[%u]\n", + path, bdev, (unsigned)id.gid)); + + if ((ret = quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), bdev, + id.gid, (caddr_t)&D))) { + return ret; + } + + break; + case SMB_USER_FS_QUOTA_TYPE: + DEBUG(10, ("sys_get_vfs_quota: path[%s] bdev[%s] " + "SMB_USER_FS_QUOTA_TYPE (uid[%u])\n", + path, bdev, (unsigned)geteuid())); + + if ((ret = quotactl(QCMD(Q_GETQUOTA, USRQUOTA), bdev, + geteuid(), (caddr_t)&D)) == 0) { + qflags |= QUOTAS_DENY_DISK; + } + + ret = 0; + + break; + case SMB_GROUP_FS_QUOTA_TYPE: + DEBUG(10, ("sys_get_vfs_quota: path[%s] bdev[%s] " + "SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n", + path, bdev, (unsigned)getegid())); + + if ((ret = quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), bdev, + getegid(), (caddr_t)&D)) == 0) { + qflags |= QUOTAS_DENY_DISK; + } + + ret = 0; + break; + default: + errno = ENOSYS; + return -1; + } + + dp->bsize = bsize; + dp->softlimit = (uint64_t)D.dqb_bsoftlimit; + dp->hardlimit = (uint64_t)D.dqb_bhardlimit; + dp->ihardlimit = (uint64_t)D.dqb_ihardlimit; + dp->isoftlimit = (uint64_t)D.dqb_isoftlimit; + dp->curinodes = (uint64_t)D.dqb_curinodes; + dp->curblocks = (uint64_t)D.dqb_curspace/bsize; + + + dp->qflags = qflags; + + return ret; +} + +/**************************************************************************** + Linux quota set calls. +****************************************************************************/ +int sys_set_vfs_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + struct dqblk D; + uint64_t bsize = (uint64_t)QUOTABLOCK_SIZE; + bool cur_enf, new_enf; + + if (!path || !bdev || !dp) { + smb_panic("sys_set_vfs_quota: called with NULL pointer"); + } + + ZERO_STRUCT(D); + + if (bsize == dp->bsize) { + D.dqb_bsoftlimit = dp->softlimit; + D.dqb_bhardlimit = dp->hardlimit; + } else { + D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize; + D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize; + } + D.dqb_ihardlimit = dp->ihardlimit; + D.dqb_isoftlimit = dp->isoftlimit; + D.dqb_valid = QIF_LIMITS; + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + DEBUG(10, ("sys_set_vfs_quota: path[%s] bdev[%s] " + "SMB_USER_QUOTA_TYPE uid[%u]\n", + path, bdev, (unsigned)id.uid)); + + ret = quotactl(QCMD(Q_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D); + break; + case SMB_GROUP_QUOTA_TYPE: + DEBUG(10, ("sys_set_vfs_quota: path[%s] bdev[%s] " + "SMB_GROUP_QUOTA_TYPE gid[%u]\n", + path, bdev, (unsigned)id.gid)); + + ret = quotactl(QCMD(Q_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D); + break; + case SMB_USER_FS_QUOTA_TYPE: + DEBUG(10, ("sys_set_vfs_quota: path[%s] bdev[%s] " + "SMB_USER_FS_QUOTA_TYPE (uid[%u])\n", + path, bdev, (unsigned)geteuid())); + + ret = quotactl(QCMD(Q_GETQUOTA, USRQUOTA), bdev, + geteuid(), (caddr_t)&D); + cur_enf = (ret == 0); + new_enf = ((dp->qflags & QUOTAS_DENY_DISK) != 0); + /* We're not changing quota enforcement, so return + * success + * IFF the wanted state is identical to the current + * state */ + if (cur_enf == new_enf) { + ret = 0; + } else { + errno = EPERM; + ret = -1; + } + + break; + case SMB_GROUP_FS_QUOTA_TYPE: + DEBUG(10, ("sys_set_vfs_quota: path[%s] bdev[%s] " + "SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n", + path, bdev, (unsigned)getegid())); + + ret = quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), bdev, + getegid(), (caddr_t)&D); + cur_enf = (ret == 0); + new_enf = ((dp->qflags & QUOTAS_DENY_DISK) != 0); + /* We're not changing quota enforcement, so return + * success + * IFF the wanted state is identical to the current + * state */ + if (cur_enf == new_enf) { + ret = 0; + } else { + errno = EPERM; + ret = -1; + } + + break; + default: + errno = ENOSYS; + return -1; + } + + return ret; +} + +#else /* HAVE_QUOTACTL_LINUX */ + void dummy_sysquotas_linux(void); + + void dummy_sysquotas_linux(void){} +#endif /* HAVE_QUOTACTL_LINUX */ diff --git a/source3/lib/sysquotas_nfs.c b/source3/lib/sysquotas_nfs.c new file mode 100644 index 0000000..14dcb02 --- /dev/null +++ b/source3/lib/sysquotas_nfs.c @@ -0,0 +1,303 @@ +/* + Unix SMB/CIFS implementation. + System QUOTA function wrappers for NFS + Copyright (C) Michael Adam 2010 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#ifndef HAVE_SYS_QUOTAS +#ifdef HAVE_NFS_QUOTAS +#undef HAVE_NFS_QUOTAS +#endif +#endif + +#ifdef HAVE_NFS_QUOTAS + +/* + * nfs quota support + * This is based on the FreeBSD / SUNOS5 section of quotas.c + */ + +/* <rpc/xdr.h> uses TRUE and FALSE */ +#ifdef TRUE +#undef TRUE +#endif + +#ifdef FALSE +#undef FALSE +#endif + +#include <rpc/rpc.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpcsvc/rquota.h> +#ifdef HAVE_RPC_NETTYPE_H +#include <rpc/nettype.h> +#endif + +#ifndef RQ_PATHLEN +#define RQ_PATHLEN 1024 +#endif + +#ifdef HAVE_GETQUOTA_RSLT_GETQUOTA_RSLT_U +#define GQR_RQUOTA getquota_rslt_u.gqr_rquota +#define GQR_STATUS status +#else +#define GQR_RQUOTA gqr_rquota +#define GQR_STATUS gqr_status +#endif + +static int my_xdr_getquota_args(XDR *xdrsp, struct getquota_args *args) +{ + if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN )) + return(0); + if (!xdr_int(xdrsp, &args->gqa_uid)) + return(0); + return (1); +} + +static int my_xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr) +{ + int quotastat; + + if (!xdr_int(xdrsp, "astat)) { + DEBUG(6,("nfs_quotas: Status bad or zero\n")); + return 0; + } + gqr->GQR_STATUS = quotastat; + + if (!xdr_int(xdrsp, &gqr->GQR_RQUOTA.rq_bsize)) { + DEBUG(6,("nfs_quotas: Block size bad or zero\n")); + return 0; + } + if (!xdr_bool(xdrsp, &gqr->GQR_RQUOTA.rq_active)) { + DEBUG(6,("nfs_quotas: Active bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, (int *)&gqr->GQR_RQUOTA.rq_bhardlimit)) { + DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, (int *)&gqr->GQR_RQUOTA.rq_bsoftlimit)) { + DEBUG(6,("nfs_quotas: Softlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, (int *)&gqr->GQR_RQUOTA.rq_curblocks)) { + DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n")); + return 0; + } + return (1); +} + + +int sys_get_nfs_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, + unid_t id, SMB_DISK_QUOTA *dp) +{ + CLIENT *clnt = NULL; + struct getquota_rslt gq_rslt; + struct getquota_args gq_args; + const char *mnttype; + char *cutstr, *host, *testpath; + int len; + static struct timeval timeout = {2,0}; + enum clnt_stat clnt_stat; + + int ret = -1; + uint32_t qflags = 0; + + if (!path || !bdev || !dp) { + smb_panic("sys_get_nfs_quota: called with NULL pointer"); + } + + DEBUG(10, ("sys_get_nfs_quota: path[%s] bdev[%s] qtype[%d]\n", + path, bdev, qtype)); + + ZERO_STRUCT(*dp); + + dp->qtype = qtype; + + if (qtype != SMB_USER_QUOTA_TYPE) { + DEBUG(3, ("sys_get_nfs_quota: got unsupported quota type '%d', " + "only supported type is '%d' (SMB_USER_QUOTA_TYPE)\n", + qtype, SMB_USER_QUOTA_TYPE)); + errno = ENOSYS; + return -1; + } + + mnttype = bdev; + len = strcspn(mnttype, ":"); + cutstr = (char *) SMB_MALLOC(len+1); + if (cutstr == NULL) { + errno = ENOMEM; + return -1; + } + + memset(cutstr, '\0', len+1); + host = strncat(cutstr, mnttype, sizeof(char) * len); + testpath = strchr_m(mnttype, ':'); + if (testpath == NULL) { + errno = EINVAL; + goto out; + } + testpath++; + gq_args.gqa_pathp = testpath; + gq_args.gqa_uid = id.uid; + + DEBUG(10, ("sys_get_nfs_quotas: Asking for quota of path '%s' on " + "host '%s', rpcprog '%i', rpcvers '%i', network '%s'\n", + host, testpath+1, (int)RQUOTAPROG, (int)RQUOTAVERS, "udp")); + + clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp"); + if (clnt == NULL) { + ret = -1; + goto out; + } + + clnt->cl_auth = authunix_create_default(); + if (clnt->cl_auth == NULL) { + DEBUG(3, ("sys_get_nfs_quotas: authunix_create_default " + "failed\n")); + ret = -1; + goto out; + } + + clnt_stat = clnt_call(clnt, + RQUOTAPROC_GETQUOTA, + (const xdrproc_t) my_xdr_getquota_args, + (caddr_t)&gq_args, + (const xdrproc_t) my_xdr_getquota_rslt, + (caddr_t)&gq_rslt, + timeout); + + if (clnt_stat != RPC_SUCCESS) { + if (errno == ECONNREFUSED) { + /* If we cannot connect with rpc.quotad, it may + * simply be because there's no quota on the remote + * system + */ + DBG_INFO("clnt_call failed with ECONNREFUSED - " + "assuming no quotas on server\n"); + ret = 0; + } else { + int save_errno = errno; + DBG_NOTICE("clnt_call failed - %s\n", strerror(errno)); + errno = save_errno; + ret = -1; + } + goto out; + } + + DEBUG(10, ("sys_get_nfs_quotas: getquota_rslt:\n" + "status : '%i'\n" + "bsize : '%i'\n" + "active : '%s'\n" + "bhardlimit : '%u'\n" + "bsoftlimit : '%u'\n" + "curblocks : '%u'\n" + "fhardlimit : '%u'\n" + "fsoftlimit : '%u'\n" + "curfiles : '%u'\n" + "btimeleft : '%u'\n" + "ftimeleft : '%u'\n", + gq_rslt.GQR_STATUS, + gq_rslt.GQR_RQUOTA.rq_bsize, + gq_rslt.GQR_RQUOTA.rq_active?"yes":"no", + gq_rslt.GQR_RQUOTA.rq_bhardlimit, + gq_rslt.GQR_RQUOTA.rq_bsoftlimit, + gq_rslt.GQR_RQUOTA.rq_curblocks, + gq_rslt.GQR_RQUOTA.rq_fhardlimit, + gq_rslt.GQR_RQUOTA.rq_fsoftlimit, + gq_rslt.GQR_RQUOTA.rq_curfiles, + gq_rslt.GQR_RQUOTA.rq_btimeleft, + gq_rslt.GQR_RQUOTA.rq_ftimeleft)); + + /* + * gqr.status returns + * 1 if quotas exist, + * 2 if there is no quota set, and + * 3 if no permission to get the quota. + */ + + switch (gq_rslt.GQR_STATUS) { + case 1: + DEBUG(10, ("sys_get_nfs_quotas: Good quota data\n")); + dp->bsize = (uint64_t)gq_rslt.GQR_RQUOTA.rq_bsize; + dp->softlimit = gq_rslt.GQR_RQUOTA.rq_bsoftlimit; + dp->hardlimit = gq_rslt.GQR_RQUOTA.rq_bhardlimit; + dp->curblocks = gq_rslt.GQR_RQUOTA.rq_curblocks; + dp->isoftlimit = gq_rslt.GQR_RQUOTA.rq_fsoftlimit; + dp->ihardlimit = gq_rslt.GQR_RQUOTA.rq_fhardlimit; + dp->curinodes = gq_rslt.GQR_RQUOTA.rq_curfiles; + break; + + case 2: + DEBUG(5, ("sys_get_nfs_quotas: No quota set\n")); + SMB_QUOTAS_SET_NO_LIMIT(dp); + break; + + case 3: + DEBUG(3, ("sys_get_nfs_quotas: no permission to get quota\n")); + errno = EPERM; + ret = -1; + goto out; + + default: + DEBUG(5, ("sys_get_nfs_quotas: Unknown remote quota status " + "code '%i'\n", gq_rslt.GQR_STATUS)); + ret = -1; + goto out; + break; + } + + dp->qflags = qflags; + + ret = 0; + +out: + if (clnt) { + if (clnt->cl_auth) { + auth_destroy(clnt->cl_auth); + } + clnt_destroy(clnt); + } + + SAFE_FREE(cutstr); + + DEBUG(10, ("sys_get_nfs_quotas: finished\n" )); + return ret; +} + +int sys_set_nfs_quota(const char *path, const char *bdev, + enum SMB_QUOTA_TYPE qtype, + unid_t id, SMB_DISK_QUOTA *dp) +{ + DEBUG(1, ("sys_set_nfs_quota : not supported\n")); + errno = ENOSYS; + return -1; +} + +#else /* HAVE_NFS_QUOTAS */ + +void dummy_sysquotas_nfs(void); +void dummy_sysquotas_nfs(void) {} + +#endif /* HAVE_NFS_QUOTAS */ diff --git a/source3/lib/sysquotas_xfs.c b/source3/lib/sysquotas_xfs.c new file mode 100644 index 0000000..6b18487 --- /dev/null +++ b/source3/lib/sysquotas_xfs.c @@ -0,0 +1,345 @@ +/* + Unix SMB/CIFS implementation. + System QUOTA function wrappers for XFS + Copyright (C) Stefan (metze) Metzmacher 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_QUOTA + +#if defined(HAVE_SYS_QUOTAS) && defined(HAVE_XFS_QUOTAS) + +#ifdef HAVE_SYS_QUOTA_H +#include <sys/quota.h> +#endif + +/* this one should actually come from glibc: */ +/* #include "samba_linux_quota.h" */ + +#ifdef HAVE_XFS_XQM_H +#include <xfs/xqm.h> +#endif + +#define HAVE_GROUP_QUOTA + +/* on IRIX */ +#ifndef Q_XQUOTAON +#define Q_XQUOTAON Q_QUOTAON +#endif /* Q_XQUOTAON */ +#ifndef Q_XQUOTAOFF +#define Q_XQUOTAOFF Q_QUOTAOFF +#endif /* Q_XQUOTAOFF */ +#ifndef Q_XGETQSTAT +#define Q_XGETQSTAT Q_GETQSTAT +#endif /* Q_XGETQSTAT */ + +/* currently doesn't support Group and Project quotas on IRIX + */ + +#ifndef QCMD +#define QCMD(x,y) x +#endif + +/* + * IRIX has BBSIZE in <sys/param.h> + */ +#ifndef BBSHIFT +#define BBSHIFT 9 +#endif /* BBSHIFT */ +#ifndef BBSIZE +#define BBSIZE (1<<BBSHIFT) +#endif /* BBSIZE */ + +/**************************************************************************** + Abstract out the XFS Quota Manager quota get call. +****************************************************************************/ +int sys_get_xfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + uint32_t qflags = 0; + uint64_t bsize = (uint64_t)BBSIZE; + struct fs_disk_quota D; + struct fs_quota_stat F; + ZERO_STRUCT(D); + ZERO_STRUCT(F); + + if (!bdev||!dp) + smb_panic("sys_get_xfs_quota: called with NULL pointer"); + + ZERO_STRUCT(*dp); + dp->qtype = qtype; + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n", + path, bdev, (unsigned)id.uid)); + + ret=quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D); + /* XFS fails with ENOENT if the user has no + * quota. Our protocol in that case is to + * succeed and return 0 as quota. + */ + if (ret != 0 && errno != ENOENT) { + return ret; + } + + ret = 0; + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_QUOTA_TYPE: + DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n", + path, bdev, (unsigned)id.gid)); + + ret=quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D); + /* XFS fails with ENOENT if the user has no + * quota. Our protocol in that case is to + * succeed and return 0 as quota. + */ + if (ret != 0 && errno != ENOENT) { + return ret; + } + break; +#endif /* HAVE_GROUP_QUOTA */ + case SMB_USER_FS_QUOTA_TYPE: + DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n", + path, bdev, (unsigned)id.uid)); + + quotactl(QCMD(Q_XGETQSTAT,USRQUOTA), bdev, -1, (caddr_t)&F); + + if (F.qs_flags & XFS_QUOTA_UDQ_ENFD) { + qflags |= QUOTAS_DENY_DISK; + } + else if (F.qs_flags & XFS_QUOTA_UDQ_ACCT) { + qflags |= QUOTAS_ENABLED; + } + + ret = 0; + + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_FS_QUOTA_TYPE: + DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n", + path, bdev, (unsigned)id.gid)); + + quotactl(QCMD(Q_XGETQSTAT,GRPQUOTA), bdev, -1, (caddr_t)&F); + + if (F.qs_flags & XFS_QUOTA_GDQ_ENFD) { + qflags |= QUOTAS_DENY_DISK; + } + else if (F.qs_flags & XFS_QUOTA_GDQ_ACCT) { + qflags |= QUOTAS_ENABLED; + } + + ret = 0; + + break; +#endif /* HAVE_GROUP_QUOTA */ + default: + errno = ENOSYS; + return -1; + } + + dp->bsize = bsize; + dp->softlimit = (uint64_t)D.d_blk_softlimit; + dp->hardlimit = (uint64_t)D.d_blk_hardlimit; + dp->ihardlimit = (uint64_t)D.d_ino_hardlimit; + dp->isoftlimit = (uint64_t)D.d_ino_softlimit; + dp->curinodes = (uint64_t)D.d_icount; + dp->curblocks = (uint64_t)D.d_bcount; + dp->qflags = qflags; + + return ret; +} + +/**************************************************************************** + Abstract out the XFS Quota Manager quota set call. +****************************************************************************/ +int sys_set_xfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp) +{ + int ret = -1; + uint32_t qflags = 0; + uint64_t bsize = (uint64_t)BBSIZE; + struct fs_disk_quota D; + struct fs_quota_stat F; + int q_on = 0; + int q_off = 0; + ZERO_STRUCT(D); + ZERO_STRUCT(F); + + if (!bdev||!dp) + smb_panic("sys_set_xfs_quota: called with NULL pointer"); + + if (bsize == dp->bsize) { + D.d_blk_softlimit = dp->softlimit; + D.d_blk_hardlimit = dp->hardlimit; + } else { + D.d_blk_softlimit = (dp->softlimit*dp->bsize)/bsize; + D.d_blk_hardlimit = (dp->hardlimit*dp->bsize)/bsize; + } + D.d_ino_hardlimit = dp->ihardlimit; + D.d_ino_softlimit = dp->isoftlimit; + + qflags = dp->qflags; + + switch (qtype) { + case SMB_USER_QUOTA_TYPE: + DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n", + path, bdev, (unsigned)id.uid)); + + D.d_fieldmask |= FS_DQ_LIMIT_MASK; + ret = quotactl(QCMD(Q_XSETQLIM,USRQUOTA), bdev, id.uid, (caddr_t)&D); + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_QUOTA_TYPE: + DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n", + path, bdev, (unsigned)id.gid)); + + D.d_fieldmask |= FS_DQ_LIMIT_MASK; + ret = quotactl(QCMD(Q_XSETQLIM,GRPQUOTA), bdev, id.gid, (caddr_t)&D); + break; +#endif /* HAVE_GROUP_QUOTA */ + case SMB_USER_FS_QUOTA_TYPE: + DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n", + path, bdev, (unsigned)id.uid)); + + quotactl(QCMD(Q_XGETQSTAT,USRQUOTA), bdev, -1, (caddr_t)&F); + + if (qflags & QUOTAS_DENY_DISK) { + if (!(F.qs_flags & XFS_QUOTA_UDQ_ENFD)) + q_on |= XFS_QUOTA_UDQ_ENFD; + if (!(F.qs_flags & XFS_QUOTA_UDQ_ACCT)) + q_on |= XFS_QUOTA_UDQ_ACCT; + + if (q_on != 0) { + ret = quotactl(QCMD(Q_XQUOTAON,USRQUOTA),bdev, -1, (caddr_t)&q_on); + } else { + ret = 0; + } + + } else if (qflags & QUOTAS_ENABLED) { + if (F.qs_flags & XFS_QUOTA_UDQ_ENFD) + q_off |= XFS_QUOTA_UDQ_ENFD; + + if (q_off != 0) { + ret = quotactl(QCMD(Q_XQUOTAOFF,USRQUOTA),bdev, -1, (caddr_t)&q_off); + } else { + ret = 0; + } + + if (!(F.qs_flags & XFS_QUOTA_UDQ_ACCT)) + q_on |= XFS_QUOTA_UDQ_ACCT; + + if (q_on != 0) { + ret = quotactl(QCMD(Q_XQUOTAON,USRQUOTA),bdev, -1, (caddr_t)&q_on); + } else { + ret = 0; + } + } else { +#if 0 + /* Switch on XFS_QUOTA_UDQ_ACCT didn't work! + * only swittching off XFS_QUOTA_UDQ_ACCT work + */ + if (F.qs_flags & XFS_QUOTA_UDQ_ENFD) + q_off |= XFS_QUOTA_UDQ_ENFD; + if (F.qs_flags & XFS_QUOTA_UDQ_ACCT) + q_off |= XFS_QUOTA_UDQ_ACCT; + + if (q_off !=0) { + ret = quotactl(QCMD(Q_XQUOTAOFF,USRQUOTA),bdev, -1, (caddr_t)&q_off); + } else { + ret = 0; + } +#else + ret = -1; +#endif + } + + break; +#ifdef HAVE_GROUP_QUOTA + case SMB_GROUP_FS_QUOTA_TYPE: + DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n", + path, bdev, (unsigned)id.gid)); + + quotactl(QCMD(Q_XGETQSTAT,GRPQUOTA), bdev, -1, (caddr_t)&F); + + if (qflags & QUOTAS_DENY_DISK) { + if (!(F.qs_flags & XFS_QUOTA_GDQ_ENFD)) + q_on |= XFS_QUOTA_GDQ_ENFD; + if (!(F.qs_flags & XFS_QUOTA_GDQ_ACCT)) + q_on |= XFS_QUOTA_GDQ_ACCT; + + if (q_on != 0) { + ret = quotactl(QCMD(Q_XQUOTAON,GRPQUOTA),bdev, -1, (caddr_t)&q_on); + } else { + ret = 0; + } + + } else if (qflags & QUOTAS_ENABLED) { + if (F.qs_flags & XFS_QUOTA_GDQ_ENFD) + q_off |= XFS_QUOTA_GDQ_ENFD; + + if (q_off != 0) { + ret = quotactl(QCMD(Q_XQUOTAOFF,GRPQUOTA),bdev, -1, (caddr_t)&q_off); + } else { + ret = 0; + } + + if (!(F.qs_flags & XFS_QUOTA_GDQ_ACCT)) + q_on |= XFS_QUOTA_GDQ_ACCT; + + if (q_on != 0) { + ret = quotactl(QCMD(Q_XQUOTAON,GRPQUOTA),bdev, -1, (caddr_t)&q_on); + } else { + ret = 0; + } + } else { +#if 0 + /* Switch on XFS_QUOTA_UDQ_ACCT didn't work! + * only swittching off XFS_QUOTA_UDQ_ACCT work + */ + if (F.qs_flags & XFS_QUOTA_GDQ_ENFD) + q_off |= XFS_QUOTA_GDQ_ENFD; + if (F.qs_flags & XFS_QUOTA_GDQ_ACCT) + q_off |= XFS_QUOTA_GDQ_ACCT; + + if (q_off !=0) { + ret = quotactl(QCMD(Q_XQUOTAOFF,GRPQUOTA),bdev, -1, (caddr_t)&q_off); + } else { + ret = 0; + } +#else + ret = -1; +#endif + } + + break; +#endif /* HAVE_GROUP_QUOTA */ + default: + errno = ENOSYS; + return -1; + } + + return ret; +} + +#else /* HAVE_XFS_QUOTAS */ + void dummy_sysquotas_xfs(void); + + void dummy_sysquotas_xfs(void){} +#endif /* HAVE_XFS_QUOTAS */ diff --git a/source3/lib/system.c b/source3/lib/system.c new file mode 100644 index 0000000..1ec0ae9 --- /dev/null +++ b/source3/lib/system.c @@ -0,0 +1,1076 @@ +/* + Unix SMB/CIFS implementation. + Samba system utilities + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1998-2005 + Copyright (C) Timur Bakeyev 2005 + Copyright (C) Bjoern Jacke 2006-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/syslog.h" +#include "system/capability.h" +#include "system/passwd.h" +#include "system/filesys.h" +#include "lib/util/setid.h" +#include "lib/util/time.h" + +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif + +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + +/* + The idea is that this file will eventually have wrappers around all + important system calls in samba. The aims are: + + - to enable easier porting by putting OS dependent stuff in here + + - to allow for hooks into other "pseudo-filesystems" + + - to allow easier integration of things like the japanese extensions + + - to support the philosophy of Samba to expose the features of + the OS within the SMB model. In general whatever file/printer/variable + expansions/etc make sense to the OS should be acceptable to Samba. +*/ + +/******************************************************************* +A send wrapper that will deal with EINTR or EAGAIN or EWOULDBLOCK. +********************************************************************/ + +ssize_t sys_send(int s, const void *msg, size_t len, int flags) +{ + ssize_t ret; + + do { + ret = send(s, msg, len, flags); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + + return ret; +} + +/******************************************************************* +A recvfrom wrapper that will deal with EINTR. +NB. As used with non-blocking sockets, return on EAGAIN/EWOULDBLOCK +********************************************************************/ + +ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) +{ + ssize_t ret; + + do { + ret = recvfrom(s, buf, len, flags, from, fromlen); + } while (ret == -1 && (errno == EINTR)); + return ret; +} + +/******************************************************************* +A fcntl wrapper that will deal with EINTR. +********************************************************************/ + +int sys_fcntl_ptr(int fd, int cmd, void *arg) +{ + int ret; + + do { + ret = fcntl(fd, cmd, arg); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A fcntl wrapper that will deal with EINTR. +********************************************************************/ + +int sys_fcntl_long(int fd, int cmd, long arg) +{ + int ret; + + do { + ret = fcntl(fd, cmd, arg); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A fcntl wrapper that will deal with EINTR. +********************************************************************/ + +int sys_fcntl_int(int fd, int cmd, int arg) +{ + int ret; + + do { + ret = fcntl(fd, cmd, arg); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/**************************************************************************** + Return the best approximation to a 'create time' under UNIX from a stat + structure. +****************************************************************************/ + +static struct timespec calc_create_time_stat(const struct stat *st) +{ + struct timespec ret, ret1; + struct timespec c_time = get_ctimespec(st); + struct timespec m_time = get_mtimespec(st); + struct timespec a_time = get_atimespec(st); + + ret = timespec_compare(&c_time, &m_time) < 0 ? c_time : m_time; + ret1 = timespec_compare(&ret, &a_time) < 0 ? ret : a_time; + + if(!null_timespec(ret1)) { + return ret1; + } + + /* + * One of ctime, mtime or atime was zero (probably atime). + * Just return MIN(ctime, mtime). + */ + return ret; +} + +/**************************************************************************** + Return the best approximation to a 'create time' under UNIX from a stat_ex + structure. +****************************************************************************/ + +static struct timespec calc_create_time_stat_ex(const struct stat_ex *st) +{ + struct timespec ret, ret1; + struct timespec c_time = st->st_ex_ctime; + struct timespec m_time = st->st_ex_mtime; + struct timespec a_time = st->st_ex_atime; + + ret = timespec_compare(&c_time, &m_time) < 0 ? c_time : m_time; + ret1 = timespec_compare(&ret, &a_time) < 0 ? ret : a_time; + + if(!null_timespec(ret1)) { + return ret1; + } + + /* + * One of ctime, mtime or atime was zero (probably atime). + * Just return MIN(ctime, mtime). + */ + return ret; +} + +/**************************************************************************** + Return the 'create time' from a stat struct if it exists (birthtime) or else + use the best approximation. +****************************************************************************/ + +static void make_create_timespec(const struct stat *pst, struct stat_ex *dst, + bool fake_dir_create_times) +{ + if (S_ISDIR(pst->st_mode) && fake_dir_create_times) { + dst->st_ex_btime.tv_sec = 315493200L; /* 1/1/1980 */ + dst->st_ex_btime.tv_nsec = 0; + return; + } + + dst->st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_BTIME; + +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC) + dst->st_ex_btime = pst->st_birthtimespec; +#elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) + dst->st_ex_btime.tv_sec = pst->st_birthtime; + dst->st_ex_btime.tv_nsec = pst->st_birthtimenspec; +#elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + dst->st_ex_btime.tv_sec = pst->st_birthtime; + dst->st_ex_btime.tv_nsec = 0; +#else + dst->st_ex_btime = calc_create_time_stat(pst); + dst->st_ex_iflags |= ST_EX_IFLAG_CALCULATED_BTIME; +#endif + + /* Deal with systems that don't initialize birthtime correctly. + * Pointed out by SATOH Fumiyasu <fumiyas@osstech.jp>. + */ + if (null_timespec(dst->st_ex_btime)) { + dst->st_ex_btime = calc_create_time_stat(pst); + dst->st_ex_iflags |= ST_EX_IFLAG_CALCULATED_BTIME; + } +} + +/**************************************************************************** + If we update a timestamp in a stat_ex struct we may have to recalculate + the birthtime. For now only implement this for write time, but we may + also need to do it for atime and ctime. JRA. +****************************************************************************/ + +void update_stat_ex_mtime(struct stat_ex *dst, + struct timespec write_ts) +{ + dst->st_ex_mtime = write_ts; + + /* We may have to recalculate btime. */ + if (dst->st_ex_iflags & ST_EX_IFLAG_CALCULATED_BTIME) { + dst->st_ex_btime = calc_create_time_stat_ex(dst); + } +} + +void update_stat_ex_create_time(struct stat_ex *dst, + struct timespec create_time) +{ + dst->st_ex_btime = create_time; + dst->st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_BTIME; +} + +void update_stat_ex_from_saved_stat(struct stat_ex *dst, + const struct stat_ex *src) +{ + if (!VALID_STAT(*src)) { + return; + } + + if (!(src->st_ex_iflags & ST_EX_IFLAG_CALCULATED_BTIME)) { + update_stat_ex_create_time(dst, src->st_ex_btime); + } +} + +void init_stat_ex_from_stat (struct stat_ex *dst, + const struct stat *src, + bool fake_dir_create_times) +{ + dst->st_ex_dev = src->st_dev; + dst->st_ex_ino = src->st_ino; + dst->st_ex_mode = src->st_mode; + dst->st_ex_nlink = src->st_nlink; + dst->st_ex_uid = src->st_uid; + dst->st_ex_gid = src->st_gid; + dst->st_ex_rdev = src->st_rdev; + dst->st_ex_size = src->st_size; + dst->st_ex_atime = get_atimespec(src); + dst->st_ex_mtime = get_mtimespec(src); + dst->st_ex_ctime = get_ctimespec(src); + dst->st_ex_iflags = 0; + make_create_timespec(src, dst, fake_dir_create_times); +#ifdef HAVE_STAT_ST_BLKSIZE + dst->st_ex_blksize = src->st_blksize; +#else + dst->st_ex_blksize = STAT_ST_BLOCKSIZE; +#endif + +#ifdef HAVE_STAT_ST_BLOCKS + dst->st_ex_blocks = src->st_blocks; +#else + dst->st_ex_blocks = src->st_size / dst->st_ex_blksize + 1; +#endif + +#ifdef HAVE_STAT_ST_FLAGS + dst->st_ex_flags = src->st_flags; +#else + dst->st_ex_flags = 0; +#endif +} + +/******************************************************************* +A stat() wrapper. +********************************************************************/ + +int sys_stat(const char *fname, SMB_STRUCT_STAT *sbuf, + bool fake_dir_create_times) +{ + int ret; + struct stat statbuf; + ret = stat(fname, &statbuf); + if (ret == 0) { + /* we always want directories to appear zero size */ + if (S_ISDIR(statbuf.st_mode)) { + statbuf.st_size = 0; + } + init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); + } + return ret; +} + +/******************************************************************* + An fstat() wrapper. +********************************************************************/ + +int sys_fstat(int fd, SMB_STRUCT_STAT *sbuf, bool fake_dir_create_times) +{ + int ret; + struct stat statbuf; + ret = fstat(fd, &statbuf); + if (ret == 0) { + /* we always want directories to appear zero size */ + if (S_ISDIR(statbuf.st_mode)) { + statbuf.st_size = 0; + } + init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); + } + return ret; +} + +/******************************************************************* + An lstat() wrapper. +********************************************************************/ + +int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf, + bool fake_dir_create_times) +{ + int ret; + struct stat statbuf; + ret = lstat(fname, &statbuf); + if (ret == 0) { + /* we always want directories to appear zero size */ + if (S_ISDIR(statbuf.st_mode)) { + statbuf.st_size = 0; + } + init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); + } + return ret; +} + +/******************************************************************* + An fstatat() wrapper. +********************************************************************/ + +int sys_fstatat(int fd, + const char *pathname, + SMB_STRUCT_STAT *sbuf, + int flags, + bool fake_dir_create_times) +{ + int ret; + struct stat statbuf; + + ret = fstatat(fd, pathname, &statbuf, flags); + if (ret != 0) { + return -1; + } + + /* we always want directories to appear zero size */ + if (S_ISDIR(statbuf.st_mode)) { + statbuf.st_size = 0; + } + init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); + return 0; +} + +/******************************************************************* + An posix_fallocate() wrapper. +********************************************************************/ +int sys_posix_fallocate(int fd, off_t offset, off_t len) +{ +#if defined(HAVE_POSIX_FALLOCATE) + return posix_fallocate(fd, offset, len); +#elif defined(F_RESVSP64) + /* this handles XFS on IRIX */ + struct flock64 fl; + off_t new_len = offset + len; + int ret; + struct stat64 sbuf; + + /* unlikely to get a too large file on a 64bit system but ... */ + if (new_len < 0) + return EFBIG; + + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = len; + + ret=fcntl(fd, F_RESVSP64, &fl); + + if (ret != 0) + return errno; + + /* Make sure the file gets enlarged after we allocated space: */ + fstat64(fd, &sbuf); + if (new_len > sbuf.st_size) + ftruncate64(fd, new_len); + return 0; +#else + return ENOSYS; +#endif +} + +/******************************************************************* + An fallocate() function that matches the semantics of the Linux one. +********************************************************************/ + +#ifdef HAVE_LINUX_FALLOC_H +#include <linux/falloc.h> +#endif + +int sys_fallocate(int fd, uint32_t mode, off_t offset, off_t len) +{ +#if defined(HAVE_LINUX_FALLOCATE) + int lmode = 0; + + if (mode & VFS_FALLOCATE_FL_KEEP_SIZE) { + lmode |= FALLOC_FL_KEEP_SIZE; + mode &= ~VFS_FALLOCATE_FL_KEEP_SIZE; + } + +#if defined(HAVE_FALLOC_FL_PUNCH_HOLE) + if (mode & VFS_FALLOCATE_FL_PUNCH_HOLE) { + lmode |= FALLOC_FL_PUNCH_HOLE; + mode &= ~VFS_FALLOCATE_FL_PUNCH_HOLE; + } +#endif /* HAVE_FALLOC_FL_PUNCH_HOLE */ + + if (mode != 0) { + DEBUG(2, ("unmapped fallocate flags: %lx\n", + (unsigned long)mode)); + errno = EINVAL; + return -1; + } + return fallocate(fd, lmode, offset, len); +#else /* HAVE_LINUX_FALLOCATE */ + /* TODO - plumb in fallocate from other filesysetms like VXFS etc. JRA. */ + errno = ENOSYS; + return -1; +#endif /* HAVE_LINUX_FALLOCATE */ +} + +/******************************************************************* + An fdopendir wrapper. +********************************************************************/ + +DIR *sys_fdopendir(int fd) +{ +#if defined(HAVE_FDOPENDIR) + return fdopendir(fd); +#else + errno = ENOSYS; + return NULL; +#endif +} + +/******************************************************************* + An mknod() wrapper. +********************************************************************/ + +int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev) +{ +#if defined(HAVE_MKNOD) + return mknod(path, mode, dev); +#else + /* No mknod system call. */ + errno = ENOSYS; + return -1; +#endif +} + +/******************************************************************* + A mknodat() wrapper. +********************************************************************/ + +int sys_mknodat(int dirfd, const char *path, mode_t mode, SMB_DEV_T dev) +{ +#if defined(HAVE_MKNODAT) + return mknodat(dirfd, path, mode, dev); +#else + /* No mknod system call. */ + errno = ENOSYS; + return -1; +#endif +} + +/******************************************************************* + System wrapper for getwd. Always returns MALLOC'ed memory, or NULL + on error (malloc fail usually). +********************************************************************/ + +char *sys_getwd(void) +{ +#ifdef GETCWD_TAKES_NULL + return getcwd(NULL, 0); +#elif defined(HAVE_GETCWD) + char *wd = NULL, *s = NULL; + size_t allocated = PATH_MAX; + + while (1) { + s = SMB_REALLOC_ARRAY(s, char, allocated); + if (s == NULL) { + return NULL; + } + wd = getcwd(s, allocated); + if (wd) { + break; + } + if (errno != ERANGE) { + int saved_errno = errno; + SAFE_FREE(s); + errno = saved_errno; + break; + } + allocated *= 2; + if (allocated < PATH_MAX) { + SAFE_FREE(s); + break; + } + } + return wd; +#else + char *wd = NULL; + char *s = SMB_MALLOC_ARRAY(char, PATH_MAX); + if (s == NULL) { + return NULL; + } + wd = getwd(s); + if (wd == NULL) { + int saved_errno = errno; + SAFE_FREE(s); + errno = saved_errno; + } + return wd; +#endif +} + +#if defined(HAVE_POSIX_CAPABILITIES) + +/************************************************************************** + Try and abstract process capabilities (for systems that have them). +****************************************************************************/ + +/* Set the POSIX capabilities needed for the given purpose into the effective + * capability set of the current process. Make sure they are always removed + * from the inheritable set, because there is no circumstance in which our + * children should inherit our elevated privileges. + */ +static bool set_process_capability(enum smbd_capability capability, + bool enable) +{ + /* "5" is the number of "num_cap_vals++" below */ + cap_value_t cap_vals[5] = {0}; + size_t num_cap_vals = 0; + + cap_t cap; + +#if defined(HAVE_PRCTL) && defined(PR_GET_KEEPCAPS) && defined(PR_SET_KEEPCAPS) + /* On Linux, make sure that any capabilities we grab are sticky + * across UID changes. We expect that this would allow us to keep both + * the effective and permitted capability sets, but as of circa 2.6.16, + * only the permitted set is kept. It is a bug (which we work around) + * that the effective set is lost, but we still require the effective + * set to be kept. + */ + if (!prctl(PR_GET_KEEPCAPS)) { + prctl(PR_SET_KEEPCAPS, 1); + } +#endif + + cap = cap_get_proc(); + if (cap == NULL) { + DEBUG(0,("set_process_capability: cap_get_proc failed: %s\n", + strerror(errno))); + return False; + } + + switch (capability) { + /* + * WARNING: If you add any #ifdef for a fresh + * capability, bump up the array size in the + * declaration of cap_vals[] above just to be + * trivially safe to never overwrite cap_vals[]. + */ + case KERNEL_OPLOCK_CAPABILITY: +#ifdef CAP_NETWORK_MGT + /* IRIX has CAP_NETWORK_MGT for oplocks. */ + cap_vals[num_cap_vals++] = CAP_NETWORK_MGT; +#endif + break; + case DMAPI_ACCESS_CAPABILITY: +#ifdef CAP_DEVICE_MGT + /* IRIX has CAP_DEVICE_MGT for DMAPI access. */ + cap_vals[num_cap_vals++] = CAP_DEVICE_MGT; +#elif CAP_MKNOD + /* Linux has CAP_MKNOD for DMAPI access. */ + cap_vals[num_cap_vals++] = CAP_MKNOD; +#endif + break; + case LEASE_CAPABILITY: +#ifdef CAP_LEASE + cap_vals[num_cap_vals++] = CAP_LEASE; +#endif + break; + case DAC_OVERRIDE_CAPABILITY: +#ifdef CAP_DAC_OVERRIDE + cap_vals[num_cap_vals++] = CAP_DAC_OVERRIDE; +#endif + } + + if (num_cap_vals == 0) { + cap_free(cap); + return True; + } + + cap_set_flag(cap, CAP_EFFECTIVE, num_cap_vals, cap_vals, + enable ? CAP_SET : CAP_CLEAR); + + /* We never want to pass capabilities down to our children, so make + * sure they are not inherited. + */ + cap_set_flag(cap, CAP_INHERITABLE, num_cap_vals, cap_vals, CAP_CLEAR); + + if (cap_set_proc(cap) == -1) { + DBG_ERR("%s capability %d: cap_set_proc failed: %s\n", + enable ? "adding" : "dropping", + capability, strerror(errno)); + cap_free(cap); + return False; + } + DBG_INFO("%s capability %d\n", + enable ? "added" : "dropped", capability); + + cap_free(cap); + return True; +} + +#endif /* HAVE_POSIX_CAPABILITIES */ + +/**************************************************************************** + Gain the oplock capability from the kernel if possible. +****************************************************************************/ + +#if defined(HAVE_POSIX_CAPABILITIES) && defined(CAP_DAC_OVERRIDE) +static bool have_cap_dac_override = true; +#else +static bool have_cap_dac_override = false; +#endif + +void set_effective_capability(enum smbd_capability capability) +{ + bool ret = false; + + if (capability != DAC_OVERRIDE_CAPABILITY || have_cap_dac_override) { +#if defined(HAVE_POSIX_CAPABILITIES) + ret = set_process_capability(capability, True); +#endif /* HAVE_POSIX_CAPABILITIES */ + } + + /* + * Fallback to become_root() if CAP_DAC_OVERRIDE is not + * available. + */ + if (capability == DAC_OVERRIDE_CAPABILITY) { + if (!ret) { + have_cap_dac_override = false; + } + if (!have_cap_dac_override) { + become_root(); + } + } +} + +void drop_effective_capability(enum smbd_capability capability) +{ + if (capability != DAC_OVERRIDE_CAPABILITY || have_cap_dac_override) { +#if defined(HAVE_POSIX_CAPABILITIES) + set_process_capability(capability, False); +#endif /* HAVE_POSIX_CAPABILITIES */ + } else { + unbecome_root(); + } +} + +/************************************************************************** + Wrapper for random(). +****************************************************************************/ + +long sys_random(void) +{ +#if defined(HAVE_RANDOM) + return (long)random(); +#elif defined(HAVE_RAND) + return (long)rand(); +#else + DEBUG(0,("Error - no random function available !\n")); + exit(1); +#endif +} + +/************************************************************************** + Wrapper for srandom(). +****************************************************************************/ + +void sys_srandom(unsigned int seed) +{ +#if defined(HAVE_SRANDOM) + srandom(seed); +#elif defined(HAVE_SRAND) + srand(seed); +#else + DEBUG(0,("Error - no srandom function available !\n")); + exit(1); +#endif +} + +#ifndef NGROUPS_MAX +#define NGROUPS_MAX 32 /* Guess... */ +#endif + +/************************************************************************** + Returns equivalent to NGROUPS_MAX - using sysconf if needed. +****************************************************************************/ + +int setgroups_max(void) +{ +#if defined(SYSCONF_SC_NGROUPS_MAX) + int ret = sysconf(_SC_NGROUPS_MAX); + return (ret == -1) ? NGROUPS_MAX : ret; +#else + return NGROUPS_MAX; +#endif +} + +int getgroups_max(void) +{ +#if defined(DARWINOS) + /* + * On MacOS sysconf(_SC_NGROUPS_MAX) returns 16 due to MacOS's group + * nesting. However, The initgroups() manpage states the following: + * "Note that OS X supports group membership in an unlimited number + * of groups. The OS X kernel uses the group list stored in the process + * credentials only as an initial cache. Additional group memberships + * are determined by communication between the operating system and the + * opendirectoryd daemon." + */ + return INT_MAX; +#else + return setgroups_max(); +#endif +} + +/************************************************************************** + Wrap setgroups and getgroups for systems that declare getgroups() as + returning an array of gid_t, but actually return an array of int. +****************************************************************************/ + +#if defined(HAVE_BROKEN_GETGROUPS) + +#ifdef HAVE_BROKEN_GETGROUPS +#define GID_T int +#else +#define GID_T gid_t +#endif + +static int sys_broken_getgroups(int setlen, gid_t *gidset) +{ + GID_T *group_list; + int i, ngroups; + + if(setlen == 0) { + return getgroups(0, NULL); + } + + /* + * Broken case. We need to allocate a + * GID_T array of size setlen. + */ + + if(setlen < 0) { + errno = EINVAL; + return -1; + } + + if((group_list = SMB_MALLOC_ARRAY(GID_T, setlen)) == NULL) { + DEBUG(0,("sys_getgroups: Malloc fail.\n")); + return -1; + } + + if((ngroups = getgroups(setlen, group_list)) < 0) { + int saved_errno = errno; + SAFE_FREE(group_list); + errno = saved_errno; + return -1; + } + + /* + * We're safe here as if ngroups > setlen then + * getgroups *must* return EINVAL. + * pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html + */ + + for(i = 0; i < ngroups; i++) + gidset[i] = (gid_t)group_list[i]; + + SAFE_FREE(group_list); + return ngroups; +} + +static int sys_broken_setgroups(int setlen, gid_t *gidset) +{ + GID_T *group_list; + int i ; + + if (setlen == 0) + return 0 ; + + if (setlen < 0 || setlen > setgroups_max()) { + errno = EINVAL; + return -1; + } + + /* + * Broken case. We need to allocate a + * GID_T array of size setlen. + */ + + if((group_list = SMB_MALLOC_ARRAY(GID_T, setlen)) == NULL) { + DEBUG(0,("sys_setgroups: Malloc fail.\n")); + return -1; + } + + for(i = 0; i < setlen; i++) + group_list[i] = (GID_T) gidset[i]; + + if(samba_setgroups(setlen, group_list) != 0) { + int saved_errno = errno; + SAFE_FREE(group_list); + errno = saved_errno; + return -1; + } + + SAFE_FREE(group_list); + return 0 ; +} + +#endif /* HAVE_BROKEN_GETGROUPS */ + +/* This is a list of systems that require the first GID passed to setgroups(2) + * to be the effective GID. If your system is one of these, add it here. + */ +#if defined (FREEBSD) || defined (DARWINOS) +#define USE_BSD_SETGROUPS +#endif + +#if defined(USE_BSD_SETGROUPS) +/* Depending on the particular BSD implementation, the first GID that is + * passed to setgroups(2) will either be ignored or will set the credential's + * effective GID. In either case, the right thing to do is to guarantee that + * gidset[0] is the effective GID. + */ +static int sys_bsd_setgroups(gid_t primary_gid, int setlen, const gid_t *gidset) +{ + gid_t *new_gidset = NULL; + int max; + int ret; + + /* setgroups(2) will fail with EINVAL if we pass too many groups. */ + max = setgroups_max(); + + /* No group list, just make sure we are setting the effective GID. */ + if (setlen == 0) { + return samba_setgroups(1, &primary_gid); + } + + /* If the primary gid is not the first array element, grow the array + * and insert it at the front. + */ + if (gidset[0] != primary_gid) { + new_gidset = SMB_MALLOC_ARRAY(gid_t, setlen + 1); + if (new_gidset == NULL) { + return -1; + } + + memcpy(new_gidset + 1, gidset, (setlen * sizeof(gid_t))); + new_gidset[0] = primary_gid; + setlen++; + } + + if (setlen > max) { + DEBUG(3, ("forced to truncate group list from %d to %d\n", + setlen, max)); + setlen = max; + } + +#if defined(HAVE_BROKEN_GETGROUPS) + ret = sys_broken_setgroups(setlen, new_gidset ? new_gidset : gidset); +#else + ret = samba_setgroups(setlen, new_gidset ? new_gidset : gidset); +#endif + + if (new_gidset) { + int errsav = errno; + SAFE_FREE(new_gidset); + errno = errsav; + } + + return ret; +} + +#endif /* USE_BSD_SETGROUPS */ + +/************************************************************************** + Wrapper for getgroups. Deals with broken (int) case. +****************************************************************************/ + +int sys_getgroups(int setlen, gid_t *gidset) +{ +#if defined(HAVE_BROKEN_GETGROUPS) + return sys_broken_getgroups(setlen, gidset); +#else + return getgroups(setlen, gidset); +#endif +} + +/************************************************************************** + Wrapper for setgroups. Deals with broken (int) case and BSD case. +****************************************************************************/ + +int sys_setgroups(gid_t UNUSED(primary_gid), int setlen, gid_t *gidset) +{ +#if !defined(HAVE_SETGROUPS) + errno = ENOSYS; + return -1; +#endif /* HAVE_SETGROUPS */ + +#if defined(USE_BSD_SETGROUPS) + return sys_bsd_setgroups(primary_gid, setlen, gidset); +#elif defined(HAVE_BROKEN_GETGROUPS) + return sys_broken_setgroups(setlen, gidset); +#else + return samba_setgroups(setlen, gidset); +#endif +} + +/**************************************************************************** + Return the major devicenumber for UNIX extensions. +****************************************************************************/ + +uint32_t unix_dev_major(SMB_DEV_T dev) +{ +#if defined(HAVE_DEVICE_MAJOR_FN) + return (uint32_t)major(dev); +#else + return (uint32_t)(dev >> 8); +#endif +} + +/**************************************************************************** + Return the minor devicenumber for UNIX extensions. +****************************************************************************/ + +uint32_t unix_dev_minor(SMB_DEV_T dev) +{ +#if defined(HAVE_DEVICE_MINOR_FN) + return (uint32_t)minor(dev); +#else + return (uint32_t)(dev & 0xff); +#endif +} + +/************************************************************************** + Wrapper for realpath. +****************************************************************************/ + +char *sys_realpath(const char *path) +{ + char *result; + +#ifdef REALPATH_TAKES_NULL + result = realpath(path, NULL); +#else + result = SMB_MALLOC_ARRAY(char, PATH_MAX + 1); + if (result) { + char *resolved_path = realpath(path, result); + if (!resolved_path) { + SAFE_FREE(result); + } else { + /* SMB_ASSERT(result == resolved_path) ? */ + result = resolved_path; + } + } +#endif + return result; +} + +#if 0 +/******************************************************************* + Return the number of CPUs. +********************************************************************/ + +int sys_get_number_of_cores(void) +{ + int ret = -1; + +#if defined(HAVE_SYSCONF) +#if defined(_SC_NPROCESSORS_ONLN) + ret = (int)sysconf(_SC_NPROCESSORS_ONLN); +#endif +#if defined(_SC_NPROCESSORS_CONF) + if (ret < 1) { + ret = (int)sysconf(_SC_NPROCESSORS_CONF); + } +#endif +#elif defined(HAVE_SYSCTL) && defined(CTL_HW) + int name[2]; + unsigned int len = sizeof(ret); + + name[0] = CTL_HW; +#if defined(HW_AVAILCPU) + name[1] = HW_AVAILCPU; + + if (sysctl(name, 2, &ret, &len, NULL, 0) == -1) { + ret = -1; + } +#endif +#if defined(HW_NCPU) + if(ret < 1) { + name[0] = CTL_HW; + name[1] = HW_NCPU; + if (sysctl(nm, 2, &count, &len, NULL, 0) == -1) { + ret = -1; + } + } +#endif +#endif + if (ret < 1) { + ret = 1; + } + return ret; +} +#endif + +bool sys_have_proc_fds(void) +{ + static bool checked = false; + static bool have_proc_fds = false; + struct stat sb; + int ret; + + if (checked) { + return have_proc_fds; + } + + ret = stat("/proc/self/fd/0", &sb); + have_proc_fds = (ret == 0); + checked = true; + + return have_proc_fds; +} + +char *sys_proc_fd_path(int fd, struct sys_proc_fd_path_buf *buf) +{ + int written = + snprintf(buf->buf, sizeof(buf->buf), "/proc/self/fd/%d", fd); + + SMB_ASSERT(sys_have_proc_fds() && (written >= 0)); + + return buf->buf; +} diff --git a/source3/lib/system_smbd.c b/source3/lib/system_smbd.c new file mode 100644 index 0000000..4a14744 --- /dev/null +++ b/source3/lib/system_smbd.c @@ -0,0 +1,253 @@ +/* + Unix SMB/CIFS implementation. + system call wrapper interface. + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Andrew Barteltt 2002 + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + This file may assume linkage with smbd - for things like become_root() + etc. +*/ + +#include "includes.h" +#include "system/passwd.h" +#include "nsswitch/winbind_client.h" +#include "../lib/util/setid.h" + +#ifndef HAVE_GETGROUPLIST + +#ifdef HAVE_GETGRSET +static int getgrouplist_getgrset(const char *user, gid_t gid, gid_t *groups, + int *grpcnt) +{ + char *grplist; + char *grp; + gid_t temp_gid; + int num_gids = 1; + int ret = 0; + long l; + + grplist = getgrset(user); + + DEBUG(10, ("getgrset returned %s\n", grplist)); + + if (grplist == NULL) { + return -1; + } + + if (*grpcnt > 0) { + groups[0] = gid; + } + + while ((grp = strsep(&grplist, ",")) != NULL) { + l = strtol(grp, NULL, 10); + temp_gid = (gid_t) l; + if (temp_gid == gid) { + continue; + } + + if (num_gids + 1 > *grpcnt) { + num_gids++; + continue; + } + groups[num_gids++] = temp_gid; + } + free(grplist); + + if (num_gids > *grpcnt) { + ret = -1; + } + *grpcnt = num_gids; + + DEBUG(10, ("Found %d groups for user %s\n", *grpcnt, user)); + + return ret; +} + +#else /* HAVE_GETGRSET */ + +/* + This is a *much* faster way of getting the list of groups for a user + without changing the current supplementary group list. The old + method used getgrent() which could take 20 minutes on a really big + network with hundeds of thousands of groups and users. The new method + takes a couple of seconds. + + NOTE!! this function only works if it is called as root! + */ + +static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, + int *grpcnt) +{ + gid_t *gids_saved; + int ret, ngrp_saved, num_gids; + + if (non_root_mode()) { + *grpcnt = 0; + return 0; + } + + /* work out how many groups we need to save */ + ngrp_saved = getgroups(0, NULL); + if (ngrp_saved == -1) { + /* this shouldn't happen */ + return -1; + } + + gids_saved = SMB_MALLOC_ARRAY(gid_t, ngrp_saved+1); + if (!gids_saved) { + errno = ENOMEM; + return -1; + } + + ngrp_saved = getgroups(ngrp_saved, gids_saved); + if (ngrp_saved == -1) { + SAFE_FREE(gids_saved); + /* very strange! */ + return -1; + } + + if (initgroups(user, gid) == -1) { + DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n")); + SAFE_FREE(gids_saved); + return -1; + } + + /* this must be done to cope with systems that put the current egid in the + return from getgroups() */ + save_re_gid(); + set_effective_gid(gid); + samba_setgid(gid); + + num_gids = getgroups(0, NULL); + if (num_gids == -1) { + SAFE_FREE(gids_saved); + /* very strange! */ + return -1; + } + + if (num_gids + 1 > *grpcnt) { + *grpcnt = num_gids + 1; + ret = -1; + } else { + ret = getgroups(*grpcnt - 1, &groups[1]); + if (ret < 0) { + SAFE_FREE(gids_saved); + /* very strange! */ + return -1; + } + groups[0] = gid; + *grpcnt = ret + 1; + } + + restore_re_gid(); + + if (sys_setgroups(gid, ngrp_saved, gids_saved) != 0) { + /* yikes! */ + DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n")); + smb_panic("getgrouplist: failed to reset group list!"); + } + + free(gids_saved); + return ret; +} +#endif /* HAVE_GETGRSET */ +#endif /* HAVE_GETGROUPLIST */ + +static int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) +{ + int retval; + bool winbind_env; + + DEBUG(10,("sys_getgrouplist: user [%s]\n", user)); + + /* This is only ever called for Unix users, remote memberships are + * always determined by the info3 coming back from auth3 or the + * PAC. */ + winbind_env = winbind_env_set(); + (void)winbind_off(); + +#ifdef HAVE_GETGROUPLIST + retval = getgrouplist(user, gid, groups, grpcnt); +#else +#ifdef HAVE_GETGRSET + retval = getgrouplist_getgrset(user, gid, groups, grpcnt); +#else + become_root(); + retval = getgrouplist_internals(user, gid, groups, grpcnt); + unbecome_root(); +#endif /* HAVE_GETGRSET */ +#endif /* HAVE_GETGROUPLIST */ + + /* allow winbindd lookups, but only if they were not already disabled */ + if (!winbind_env) { + (void)winbind_on(); + } + + return retval; +} + +bool getgroups_unix_user(TALLOC_CTX *mem_ctx, const char *user, + gid_t primary_gid, + gid_t **ret_groups, uint32_t *p_ngroups) +{ + int max_grp = MIN(128, getgroups_max()); + gid_t stack_groups[max_grp]; + uint32_t ngrp; + gid_t *temp_groups = stack_groups; + gid_t *to_free = NULL; + gid_t *groups; + int i; + + if (sys_getgrouplist(user, primary_gid, temp_groups, &max_grp) == -1) { + to_free = talloc_array(mem_ctx, gid_t, max_grp); + if (!to_free) { + return False; + } + temp_groups = to_free; + + if (sys_getgrouplist(user, primary_gid, + temp_groups, &max_grp) == -1) { + DEBUG(0, ("get_user_groups: failed to get the unix " + "group list\n")); + TALLOC_FREE(to_free); + return False; + } + } + + ngrp = 0; + groups = NULL; + + /* Add in primary group first */ + if (!add_gid_to_array_unique(mem_ctx, primary_gid, &groups, &ngrp)) { + TALLOC_FREE(to_free); + return False; + } + + for (i=0; i<max_grp; i++) { + if (!add_gid_to_array_unique(mem_ctx, temp_groups[i], + &groups, &ngrp)) { + TALLOC_FREE(to_free); + return False; + } + } + + *p_ngroups = ngrp; + *ret_groups = groups; + TALLOC_FREE(to_free); + return True; +} diff --git a/source3/lib/tallocmsg.c b/source3/lib/tallocmsg.c new file mode 100644 index 0000000..af5a90b --- /dev/null +++ b/source3/lib/tallocmsg.c @@ -0,0 +1,85 @@ +/* + samba -- Unix SMB/CIFS implementation. + Copyright (C) 2001, 2002 by Martin Pool + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "messages.h" +#include "lib/util/talloc_report_printf.h" + +static bool pool_usage_filter(struct messaging_rec *rec, void *private_data) +{ + FILE *f = NULL; + int fd; + + if (rec->msg_type != MSG_REQ_POOL_USAGE) { + return false; + } + + DBG_DEBUG("Got MSG_REQ_POOL_USAGE\n"); + + if (rec->num_fds != 1) { + DBG_DEBUG("Got %"PRIu8" fds, expected one\n", rec->num_fds); + return false; + } + + fd = dup(rec->fds[0]); + if (fd == -1) { + DBG_DEBUG("dup(%"PRIi64") failed: %s\n", + rec->fds[0], + strerror(errno)); + return false; + } + + f = fdopen(fd, "w"); + if (f == NULL) { + DBG_DEBUG("fdopen failed: %s\n", strerror(errno)); + close(fd); + return false; + } + + talloc_full_report_printf(NULL, f); + + fclose(f); + /* + * Returning false, means messaging_dispatch_waiters() + * won't call messaging_filtered_read_done() and + * our messaging_filtered_read_send() stays alive + * and will get messages. + */ + return false; +} + +/** + * Register handler for MSG_REQ_POOL_USAGE + **/ +void register_msg_pool_usage( + TALLOC_CTX *mem_ctx, struct messaging_context *msg_ctx) +{ + struct tevent_req *req = NULL; + + req = messaging_filtered_read_send( + mem_ctx, + messaging_tevent_context(msg_ctx), + msg_ctx, + pool_usage_filter, + NULL); + if (req == NULL) { + DBG_WARNING("messaging_filtered_read_send failed\n"); + return; + } + DBG_INFO("Registered MSG_REQ_POOL_USAGE\n"); +} diff --git a/source3/lib/tdb_validate.c b/source3/lib/tdb_validate.c new file mode 100644 index 0000000..78bd824 --- /dev/null +++ b/source3/lib/tdb_validate.c @@ -0,0 +1,538 @@ +/* + * Unix SMB/CIFS implementation. + * + * A general tdb content validation mechanism + * + * Copyright (C) Michael Adam 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "util_tdb.h" +#include "tdb_validate.h" + +/* + * internal validation function, executed by the child. + */ +static int tdb_validate_child(struct tdb_context *tdb, + tdb_validate_data_func validate_fn) +{ + int ret = 1; + int check_rc; + int num_entries = 0; + struct tdb_validation_status v_status; + + v_status.tdb_error = False; + v_status.bad_freelist = False; + v_status.bad_entry = False; + v_status.unknown_key = False; + v_status.success = True; + + if (!tdb) { + v_status.tdb_error = True; + v_status.success = False; + goto out; + } + + /* + * we can simplify this by passing a check function, + * but I don't want to change all the callers... + */ + check_rc = tdb_check(tdb, NULL, NULL); + if (check_rc != 0) { + v_status.tdb_error = True; + v_status.success = False; + goto out; + } + + /* Check if the tdb's freelist is good. */ + if (tdb_validate_freelist(tdb, &num_entries) == -1) { + v_status.bad_freelist = True; + v_status.success = False; + goto out; + } + + DEBUG(10,("tdb_validate_child: tdb %s freelist has %d entries\n", + tdb_name(tdb), num_entries)); + + /* Now traverse the tdb to validate it. */ + num_entries = tdb_traverse(tdb, validate_fn, (void *)&v_status); + if (!v_status.success) { + goto out; + } else if (num_entries < 0) { + v_status.tdb_error = True; + v_status.success = False; + goto out; + } + + DEBUG(10,("tdb_validate_child: tdb %s is good with %d entries\n", + tdb_name(tdb), num_entries)); + ret = 0; /* Cache is good. */ + +out: + DEBUG(10, ("tdb_validate_child: summary of validation status:\n")); + DEBUGADD(10,(" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no")); + DEBUGADD(10,(" * bad freelist: %s\n",v_status.bad_freelist?"yes":"no")); + DEBUGADD(10,(" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no")); + DEBUGADD(10,(" * unknown key: %s\n", v_status.unknown_key?"yes":"no")); + DEBUGADD(10,(" => overall success: %s\n", v_status.success?"yes":"no")); + + return ret; +} + +/* + * tdb validation function. + * returns 0 if tdb is ok, != 0 if it isn't. + * this function expects an opened tdb. + */ +int tdb_validate(struct tdb_context *tdb, tdb_validate_data_func validate_fn) +{ + pid_t child_pid = -1; + int child_status = 0; + int wait_pid = 0; + int ret = 1; + + if (tdb == NULL) { + DEBUG(1, ("Error: tdb_validate called with tdb == NULL\n")); + return ret; + } + + DEBUG(5, ("tdb_validate called for tdb '%s'\n", tdb_name(tdb))); + + /* fork and let the child do the validation. + * benefit: no need to twist signal handlers and panic functions. + * just let the child panic. we catch the signal. */ + + DEBUG(10, ("tdb_validate: forking to let child do validation.\n")); + child_pid = fork(); + if (child_pid == 0) { + /* child code */ + DEBUG(10, ("tdb_validate (validation child): created\n")); + DEBUG(10, ("tdb_validate (validation child): " + "calling tdb_validate_child\n")); + exit(tdb_validate_child(tdb, validate_fn)); + } + else if (child_pid < 0) { + DEBUG(1, ("tdb_validate: fork for validation failed.\n")); + goto done; + } + + /* parent */ + + DEBUG(10, ("tdb_validate: fork succeeded, child PID = %u\n", + (unsigned int)child_pid)); + + DEBUG(10, ("tdb_validate: waiting for child to finish...\n")); + while ((wait_pid = waitpid(child_pid, &child_status, 0)) < 0) { + if (errno == EINTR) { + DEBUG(10, ("tdb_validate: got signal during waitpid, " + "retrying\n")); + errno = 0; + continue; + } + DEBUG(1, ("tdb_validate: waitpid failed with error '%s'.\n", + strerror(errno))); + goto done; + } + if (wait_pid != child_pid) { + DEBUG(1, ("tdb_validate: waitpid returned pid %d, " + "but %u was expected\n", wait_pid, (unsigned int)child_pid)); + goto done; + } + + DEBUG(10, ("tdb_validate: validating child returned.\n")); + if (WIFEXITED(child_status)) { + DEBUG(10, ("tdb_validate: child exited, code %d.\n", + WEXITSTATUS(child_status))); + ret = WEXITSTATUS(child_status); + } + if (WIFSIGNALED(child_status)) { + DEBUG(10, ("tdb_validate: child terminated by signal %d\n", + WTERMSIG(child_status))); +#ifdef WCOREDUMP + if (WCOREDUMP(child_status)) { + DEBUGADD(10, ("core dumped\n")); + } +#endif + ret = WTERMSIG(child_status); + } + if (WIFSTOPPED(child_status)) { + DEBUG(10, ("tdb_validate: child was stopped by signal %d\n", + WSTOPSIG(child_status))); + ret = WSTOPSIG(child_status); + } + +done: + DEBUG(5, ("tdb_validate returning code '%d' for tdb '%s'\n", ret, + tdb_name(tdb))); + + return ret; +} + +/* + * tdb validation function. + * returns 0 if tdb is ok, != 0 if it isn't. + * this is a wrapper around the actual validation function that opens and closes + * the tdb. + */ +int tdb_validate_open(const char *tdb_path, tdb_validate_data_func validate_fn) +{ + TDB_CONTEXT *tdb = NULL; + int ret = 1; + + DEBUG(5, ("tdb_validate_open called for tdb '%s'\n", tdb_path)); + + tdb = tdb_open_log(tdb_path, 0, TDB_DEFAULT, O_RDWR, 0); + if (!tdb) { + DEBUG(1, ("Error opening tdb %s\n", tdb_path)); + return ret; + } + + ret = tdb_validate(tdb, validate_fn); + tdb_close(tdb); + return ret; +} + +/* + * tdb backup function and helpers for tdb_validate wrapper with backup + * handling. + */ + +/* this structure eliminates the need for a global overall status for + * the traverse-copy */ +struct tdb_copy_data { + struct tdb_context *dst; + bool success; +}; + +static int traverse_copy_fn(struct tdb_context *tdb, TDB_DATA key, + TDB_DATA dbuf, void *private_data) +{ + struct tdb_copy_data *data = (struct tdb_copy_data *)private_data; + + if (tdb_store(data->dst, key, dbuf, TDB_INSERT) != 0) { + DEBUG(4, ("Failed to insert into %s: %s\n", tdb_name(data->dst), + strerror(errno))); + data->success = False; + return 1; + } + return 0; +} + +static int tdb_copy(struct tdb_context *src, struct tdb_context *dst) +{ + struct tdb_copy_data data; + int count; + + data.dst = dst; + data.success = True; + + count = tdb_traverse(src, traverse_copy_fn, (void *)(&data)); + if ((count < 0) || (data.success == False)) { + return -1; + } + return count; +} + +static int tdb_verify_basic(struct tdb_context *tdb) +{ + return tdb_traverse(tdb, NULL, NULL); +} + +/* this backup function is essentially taken from lib/tdb/tools/tdbbackup.tdb + */ +static int tdb_backup(TALLOC_CTX *ctx, const char *src_path, + const char *dst_path, int hash_size) +{ + struct tdb_context *src_tdb = NULL; + struct tdb_context *dst_tdb = NULL; + char *tmp_path = NULL; + struct stat st; + int count1, count2; + int saved_errno = 0; + int ret = -1; + + if (stat(src_path, &st) != 0) { + DEBUG(3, ("Could not stat '%s': %s\n", src_path, + strerror(errno))); + goto done; + } + + /* open old tdb RDWR - so we can lock it */ + src_tdb = tdb_open_log(src_path, 0, TDB_DEFAULT, O_RDWR, 0); + if (src_tdb == NULL) { + DEBUG(3, ("Failed to open tdb '%s'\n", src_path)); + goto done; + } + + if (tdb_lockall(src_tdb) != 0) { + DEBUG(3, ("Failed to lock tdb '%s'\n", src_path)); + goto done; + } + + tmp_path = talloc_asprintf(ctx, "%s%s", dst_path, ".tmp"); + if (!tmp_path) { + DEBUG(3, ("talloc fail\n")); + goto done; + } + + unlink(tmp_path); + + if (!hash_size) { + hash_size = tdb_hash_size(src_tdb); + } + + dst_tdb = tdb_open_log(tmp_path, hash_size, + TDB_DEFAULT, O_RDWR | O_CREAT | O_EXCL, + st.st_mode & 0777); + if (dst_tdb == NULL) { + DEBUG(3, ("Error creating tdb '%s': %s\n", tmp_path, + strerror(errno))); + saved_errno = errno; + unlink(tmp_path); + goto done; + } + + count1 = tdb_copy(src_tdb, dst_tdb); + if (count1 < 0) { + DEBUG(3, ("Failed to copy tdb '%s': %s\n", src_path, + strerror(errno))); + tdb_close(dst_tdb); + goto done; + } + + /* reopen ro and do basic verification */ + tdb_close(dst_tdb); + dst_tdb = tdb_open_log(tmp_path, 0, TDB_DEFAULT, O_RDONLY, 0); + if (!dst_tdb) { + DEBUG(3, ("Failed to reopen tdb '%s': %s\n", tmp_path, + strerror(errno))); + goto done; + } + count2 = tdb_verify_basic(dst_tdb); + if (count2 != count1) { + DEBUG(3, ("Failed to verify result of copying tdb '%s'.\n", + src_path)); + tdb_close(dst_tdb); + goto done; + } + + DEBUG(10, ("tdb_backup: successfully copied %d entries\n", count1)); + + /* make sure the new tdb has reached stable storage + * then rename it to its destination */ + fsync(tdb_fd(dst_tdb)); + tdb_close(dst_tdb); + unlink(dst_path); + if (rename(tmp_path, dst_path) != 0) { + DEBUG(3, ("Failed to rename '%s' to '%s': %s\n", + tmp_path, dst_path, strerror(errno))); + goto done; + } + + /* success */ + ret = 0; + +done: + if (src_tdb != NULL) { + tdb_close(src_tdb); + } + if (tmp_path != NULL) { + unlink(tmp_path); + TALLOC_FREE(tmp_path); + } + if (saved_errno != 0) { + errno = saved_errno; + } + return ret; +} + +static int rename_file_with_suffix(TALLOC_CTX *ctx, const char *path, + const char *suffix) +{ + int ret = -1; + char *dst_path; + + dst_path = talloc_asprintf(ctx, "%s%s", path, suffix); + if (dst_path == NULL) { + DEBUG(3, ("error out of memory\n")); + return ret; + } + + ret = (rename(path, dst_path) != 0); + + if (ret == 0) { + DEBUG(5, ("moved '%s' to '%s'\n", path, dst_path)); + } else if (errno == ENOENT) { + DEBUG(3, ("file '%s' does not exist - so not moved\n", path)); + ret = 0; + } else { + DEBUG(3, ("error renaming %s to %s: %s\n", path, dst_path, + strerror(errno))); + } + + TALLOC_FREE(dst_path); + return ret; +} + +/* + * do a backup of a tdb, moving the destination out of the way first + */ +static int tdb_backup_with_rotate(TALLOC_CTX *ctx, const char *src_path, + const char *dst_path, int hash_size, + const char *rotate_suffix, + bool retry_norotate_if_nospc, + bool rename_as_last_resort_if_nospc) +{ + int ret; + + rename_file_with_suffix(ctx, dst_path, rotate_suffix); + + ret = tdb_backup(ctx, src_path, dst_path, hash_size); + + if (ret != 0) { + DEBUG(10, ("backup of %s failed: %s\n", src_path, strerror(errno))); + } + if ((ret != 0) && (errno == ENOSPC) && retry_norotate_if_nospc) + { + char *rotate_path = talloc_asprintf(ctx, "%s%s", dst_path, + rotate_suffix); + if (rotate_path == NULL) { + DEBUG(10, ("talloc fail\n")); + return -1; + } + DEBUG(10, ("backup of %s failed due to lack of space\n", + src_path)); + DEBUGADD(10, ("trying to free some space by removing rotated " + "dst %s\n", rotate_path)); + if (unlink(rotate_path) == -1) { + DEBUG(10, ("unlink of %s failed: %s\n", rotate_path, + strerror(errno))); + } else { + ret = tdb_backup(ctx, src_path, dst_path, hash_size); + } + TALLOC_FREE(rotate_path); + } + + if ((ret != 0) && (errno == ENOSPC) && rename_as_last_resort_if_nospc) + { + DEBUG(10, ("backup of %s failed due to lack of space\n", + src_path)); + DEBUGADD(10, ("using 'rename' as a last resort\n")); + ret = rename(src_path, dst_path); + } + + return ret; +} + +/* + * validation function with backup handling: + * + * - calls tdb_validate + * - if the tdb is ok, create a backup "name.bak", possibly moving + * existing backup to name.bak.old, + * return 0 (success) even if the backup fails + * - if the tdb is corrupt: + * - move the tdb to "name.corrupt" + * - check if there is valid backup. + * if so, restore the backup. + * if restore is successful, return 0 (success), + * - otherwise return -1 (failure) + */ +int tdb_validate_and_backup(const char *tdb_path, + tdb_validate_data_func validate_fn) +{ + int ret = -1; + const char *backup_suffix = ".bak"; + const char *corrupt_suffix = ".corrupt"; + const char *rotate_suffix = ".old"; + char *tdb_path_backup; + struct stat st; + TALLOC_CTX *ctx = NULL; + + ctx = talloc_new(NULL); + if (ctx == NULL) { + DEBUG(0, ("tdb_validate_and_backup: out of memory\n")); + goto done; + } + + tdb_path_backup = talloc_asprintf(ctx, "%s%s", tdb_path, backup_suffix); + if (!tdb_path_backup) { + DEBUG(0, ("tdb_validate_and_backup: out of memory\n")); + goto done; + } + + ret = tdb_validate_open(tdb_path, validate_fn); + + if (ret == 0) { + DEBUG(1, ("tdb '%s' is valid\n", tdb_path)); + ret = tdb_backup_with_rotate(ctx, tdb_path, tdb_path_backup, 0, + rotate_suffix, True, False); + if (ret != 0) { + DEBUG(1, ("Error creating backup of tdb '%s'\n", + tdb_path)); + /* the actual validation was successful: */ + ret = 0; + } else { + DEBUG(1, ("Created backup '%s' of tdb '%s'\n", + tdb_path_backup, tdb_path)); + } + } else { + DEBUG(1, ("tdb '%s' is invalid\n", tdb_path)); + + ret =stat(tdb_path_backup, &st); + if (ret != 0) { + DEBUG(5, ("Could not stat '%s': %s\n", tdb_path_backup, + strerror(errno))); + DEBUG(1, ("No backup found.\n")); + } else { + DEBUG(1, ("backup '%s' found.\n", tdb_path_backup)); + ret = tdb_validate_open(tdb_path_backup, validate_fn); + if (ret != 0) { + DEBUG(1, ("Backup '%s' is invalid.\n", + tdb_path_backup)); + } + } + + if (ret != 0) { + int renamed = rename_file_with_suffix(ctx, tdb_path, + corrupt_suffix); + if (renamed != 0) { + DEBUG(1, ("Error moving tdb to '%s%s'\n", + tdb_path, corrupt_suffix)); + } else { + DEBUG(1, ("Corrupt tdb stored as '%s%s'\n", + tdb_path, corrupt_suffix)); + } + goto done; + } + + DEBUG(1, ("valid backup '%s' found\n", tdb_path_backup)); + ret = tdb_backup_with_rotate(ctx, tdb_path_backup, tdb_path, 0, + corrupt_suffix, True, True); + if (ret != 0) { + DEBUG(1, ("Error restoring backup from '%s'\n", + tdb_path_backup)); + } else { + DEBUG(1, ("Restored tdb backup from '%s'\n", + tdb_path_backup)); + } + } + +done: + TALLOC_FREE(ctx); + return ret; +} diff --git a/source3/lib/tdb_validate.h b/source3/lib/tdb_validate.h new file mode 100644 index 0000000..3e7c20d --- /dev/null +++ b/source3/lib/tdb_validate.h @@ -0,0 +1,79 @@ +/* + * Unix SMB/CIFS implementation. + * + * A general tdb content validation mechanism + * + * Copyright (C) Michael Adam 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __TDB_VALIDATE_H__ +#define __TDB_VALIDATE_H__ + +#include "lib/replace/replace.h" +#include <tdb.h> + +/** + * Flag field for keeping track of the status of a validation. + */ +struct tdb_validation_status { + bool tdb_error; + bool bad_freelist; + bool bad_entry; + bool unknown_key; + bool success; +}; + +/** + * Callback function type for the validation mechanism. + */ +typedef int (*tdb_validate_data_func)(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, + TDB_DATA dbuf, void *state); + +/** + * tdb validation function. + * returns 0 if tdb is ok, != 0 if it isn't. + * this function expects an opened tdb. + */ +int tdb_validate(struct tdb_context *tdb, + tdb_validate_data_func validate_fn); + +/** + * tdb validation function. + * returns 0 if tdb is ok, != 0 if it isn't. + * This is a wrapper around the actual validation function that + * opens and closes the tdb. + */ +int tdb_validate_open(const char *tdb_path, + tdb_validate_data_func validate_fn); + +/** + * validation function with backup handling: + * + * - calls tdb_validate + * - if the tdb is ok, create a backup "name.bak", possibly moving + * existing backup to name.bak.old, + * return 0 (success) even if the backup fails + * - if the tdb is corrupt: + * - move the tdb to "name.corrupt" + * - check if there is valid backup. + * if so, restore the backup. + * if restore is successful, return 0 (success), + * - otherwise return -1 (failure) + */ +int tdb_validate_and_backup(const char *tdb_path, + tdb_validate_data_func validate_fn); + +#endif /* __TDB_VALIDATE_H__ */ diff --git a/source3/lib/test_adouble.c b/source3/lib/test_adouble.c new file mode 100644 index 0000000..615c224 --- /dev/null +++ b/source3/lib/test_adouble.c @@ -0,0 +1,389 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2021 Ralph Boehme <slow@samba.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "adouble.c" +#include <cmocka.h> + +static int setup_talloc_context(void **state) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + *state = frame; + return 0; +} + +static int teardown_talloc_context(void **state) +{ + TALLOC_CTX *frame = *state; + + TALLOC_FREE(frame); + return 0; +} + +/* + * Basic and sane buffer. + */ +static uint8_t ad_basic[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x32, /* offset */ + 0x00, 0x00, 0x00, 0x20, /* length */ + /* adentry 2: Resourcefork */ + 0x00, 0x00, 0x00, 0x02, /* eid: Resourcefork */ + 0x00, 0x00, 0x00, 0x52, /* offset */ + 0xff, 0xff, 0xff, 0x00, /* length */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* + * An empty FinderInfo entry. + */ +static uint8_t ad_finderinfo1[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x52, /* off: points at end of buffer */ + 0x00, 0x00, 0x00, 0x00, /* len: 0, so off+len don't exceed bufferlen */ + /* adentry 2: Resourcefork */ + 0x00, 0x00, 0x00, 0x02, /* eid: Resourcefork */ + 0x00, 0x00, 0x00, 0x52, /* offset */ + 0xff, 0xff, 0xff, 0x00, /* length */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* + * A dangerous FinderInfo with correct length exceeding buffer by one byte. + */ +static uint8_t ad_finderinfo2[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x33, /* off: points at beginng of data + 1 */ + 0x00, 0x00, 0x00, 0x20, /* len: 32, so off+len exceeds bufferlen by 1 */ + /* adentry 2: Resourcefork */ + 0x00, 0x00, 0x00, 0x02, /* eid: Resourcefork */ + 0x00, 0x00, 0x00, 0x52, /* offset */ + 0xff, 0xff, 0xff, 0x00, /* length */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static uint8_t ad_finderinfo3[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x33, /* off: points at beginng of data + 1 */ + 0x00, 0x00, 0x00, 0x1f, /* len: 31, so off+len don't exceed buf */ + /* adentry 2: Resourcefork */ + 0x00, 0x00, 0x00, 0x02, /* eid: Resourcefork */ + 0x00, 0x00, 0x00, 0x52, /* offset */ + 0xff, 0xff, 0xff, 0x00, /* length */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* + * A dangerous name entry. + */ +static uint8_t ad_name[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x32, /* offset */ + 0x00, 0x00, 0x00, 0x20, /* length */ + /* adentry 2: Name */ + 0x00, 0x00, 0x00, 0x03, /* eid: Name */ + 0x00, 0x00, 0x00, 0x52, /* off: points at end of buffer */ + 0x00, 0x00, 0x00, 0x01, /* len: 1, so off+len exceeds bufferlen */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* + * A empty ADEID_FILEDATESI entry. + */ +static uint8_t ad_date1[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x32, /* offset */ + 0x00, 0x00, 0x00, 0x20, /* length */ + /* adentry 2: Dates */ + 0x00, 0x00, 0x00, 0x08, /* eid: dates */ + 0x00, 0x00, 0x00, 0x52, /* off: end of buffer */ + 0x00, 0x00, 0x00, 0x00, /* len: 0, empty entry, valid */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* + * A dangerous ADEID_FILEDATESI entry, invalid length. + */ +static uint8_t ad_date2[] = { + 0x00, 0x05, 0x16, 0x07, /* Magic */ + 0x00, 0x02, 0x00, 0x00, /* Version */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x00, 0x00, 0x00, /* Filler */ + 0x00, 0x02, /* Count */ + /* adentry 1: FinderInfo */ + 0x00, 0x00, 0x00, 0x09, /* eid: FinderInfo */ + 0x00, 0x00, 0x00, 0x32, /* offset */ + 0x00, 0x00, 0x00, 0x20, /* length */ + /* adentry 2: Dates */ + 0x00, 0x00, 0x00, 0x08, /* eid: dates */ + 0x00, 0x00, 0x00, 0x43, /* off: FinderInfo buf but one byte short */ + 0x00, 0x00, 0x00, 0x0f, /* len: 15, so off+len don't exceed bufferlen */ + /* FinderInfo data: 32 bytes */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static struct adouble *parse_adouble(TALLOC_CTX *mem_ctx, + uint8_t *adbuf, + size_t adsize, + off_t filesize) +{ + struct adouble *ad = NULL; + bool ok; + + ad = talloc_zero(mem_ctx, struct adouble); + ad->ad_data = talloc_zero_size(ad, adsize); + assert_non_null(ad); + + memcpy(ad->ad_data, adbuf, adsize); + + ok = ad_unpack(ad, 2, filesize); + if (!ok) { + return NULL; + } + + return ad; +} + +static void parse_abouble_basic(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + char *p = NULL; + + ad = parse_adouble(frame, ad_basic, sizeof(ad_basic), 0xffffff52); + assert_non_null(ad); + + p = ad_get_entry(ad, ADEID_FINDERI); + assert_non_null(p); + + return; +} + +static void parse_abouble_finderinfo1(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + char *p = NULL; + + ad = parse_adouble(frame, + ad_finderinfo1, + sizeof(ad_finderinfo1), + 0xffffff52); + assert_non_null(ad); + + p = ad_get_entry(ad, ADEID_FINDERI); + assert_null(p); + + return; +} + +static void parse_abouble_finderinfo2(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + + ad = parse_adouble(frame, + ad_finderinfo2, + sizeof(ad_finderinfo2), + 0xffffff52); + assert_null(ad); + + return; +} + +static void parse_abouble_finderinfo3(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + + ad = parse_adouble(frame, + ad_finderinfo3, + sizeof(ad_finderinfo3), + 0xffffff52); + assert_null(ad); + + return; +} + +static void parse_abouble_name(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + + ad = parse_adouble(frame, ad_name, sizeof(ad_name), 0x52); + assert_null(ad); + + return; +} + +static void parse_abouble_date1(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + char *p = NULL; + + ad = parse_adouble(frame, ad_date1, sizeof(ad_date1), 0x52); + assert_non_null(ad); + + p = ad_get_entry(ad, ADEID_FILEDATESI); + assert_null(p); + + return; +} + +static void parse_abouble_date2(void **state) +{ + TALLOC_CTX *frame = *state; + struct adouble *ad = NULL; + + ad = parse_adouble(frame, ad_date2, sizeof(ad_date2), 0x52); + assert_null(ad); + + return; +} + +int main(int argc, char *argv[]) +{ + int rc; + const struct CMUnitTest tests[] = { + cmocka_unit_test(parse_abouble_basic), + cmocka_unit_test(parse_abouble_finderinfo1), + cmocka_unit_test(parse_abouble_finderinfo2), + cmocka_unit_test(parse_abouble_finderinfo3), + cmocka_unit_test(parse_abouble_name), + cmocka_unit_test(parse_abouble_date1), + cmocka_unit_test(parse_abouble_date2), + }; + + if (argc == 2) { + cmocka_set_test_filter(argv[1]); + } + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + + rc = cmocka_run_group_tests(tests, + setup_talloc_context, + teardown_talloc_context); + + return rc; +} diff --git a/source3/lib/test_tldap.c b/source3/lib/test_tldap.c new file mode 100644 index 0000000..659c5a7 --- /dev/null +++ b/source3/lib/test_tldap.c @@ -0,0 +1,63 @@ +/* + * Unix SMB/CIFS implementation. + * Test suite for ldap client + * + * Copyright (C) 2018 Andreas Schneider <asn@samba.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "source3/lib/tldap.c" + +static void test_tldap_unescape_ldapv3(void **state) +{ + const char *unescaped_dn = "(&(objectclass=group)(cn=Samba*))"; + char dn[] = "\\28&\\28objectclass=group\\29\\28cn=Samba\\2a\\29\\29"; + size_t dnlen = sizeof(dn); + bool ok; + + ok = tldap_unescape_inplace(dn, &dnlen); + assert_true(ok); + + assert_string_equal(dn, unescaped_dn); +} + +static void test_tldap_unescape_ldapv2(void **state) +{ + const char *unescaped_dn = "(&(objectclass=group)(cn=Samba*))"; + char dn[] = "\\(&\\(objectclass=group\\)\\(cn=Samba\\*\\)\\)"; + size_t dnlen = sizeof(dn); + bool ok; + + ok = tldap_unescape_inplace(dn, &dnlen); + assert_true(ok); + + assert_string_equal(dn, unescaped_dn); +} + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_tldap_unescape_ldapv3), + cmocka_unit_test(test_tldap_unescape_ldapv2) + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/source3/lib/tevent_barrier.c b/source3/lib/tevent_barrier.c new file mode 100644 index 0000000..0fc0a31 --- /dev/null +++ b/source3/lib/tevent_barrier.c @@ -0,0 +1,192 @@ +/* + Unix SMB/CIFS implementation. + Implement a barrier + Copyright (C) Volker Lendecke 2012 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "tevent_barrier.h" +#include "lib/util/tevent_unix.h" + +struct tevent_barrier_waiter { + struct tevent_immediate *im; + struct tevent_context *ev; + struct tevent_req *req; +}; + +struct tevent_barrier { + unsigned count; + struct tevent_barrier_waiter *waiters; + void (*trigger_cb)(void *private_data); + void *private_data; +}; + +static int tevent_barrier_destructor(struct tevent_barrier *b); +static void tevent_barrier_release(struct tevent_barrier *b); +static void tevent_barrier_release_one(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data); +static void tevent_barrier_release_trigger(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data); + +struct tevent_barrier *tevent_barrier_init( + TALLOC_CTX *mem_ctx, unsigned count, + void (*trigger_cb)(void *private_data), void *private_data) +{ + struct tevent_barrier *b; + unsigned i; + + if (count == 0) { + return NULL; + } + + b = talloc(mem_ctx, struct tevent_barrier); + if (b == NULL) { + return NULL; + } + b->count = 0; + b->trigger_cb = trigger_cb; + b->private_data = private_data; + + b->waiters = talloc_array(b, struct tevent_barrier_waiter, count); + if (b->waiters == NULL) { + goto fail; + } + for (i=0; i<count; i++) { + struct tevent_barrier_waiter *w = &b->waiters[i]; + + w->im = tevent_create_immediate(b->waiters); + if (w->im == NULL) { + goto fail; + } + w->req = NULL; + } + talloc_set_destructor(b, tevent_barrier_destructor); + return b; +fail: + TALLOC_FREE(b); + return NULL; +} + +static int tevent_barrier_destructor(struct tevent_barrier *b) +{ + tevent_barrier_release(b); + return 0; +} + +struct tevent_barrier_wait_state { + struct tevent_barrier *b; + int index; +}; + +static void tevent_barrier_release(struct tevent_barrier *b) +{ + unsigned i; + + for (i=0; i<b->count; i++) { + struct tevent_barrier_waiter *w = &b->waiters[i]; + struct tevent_barrier_wait_state *state; + + if (w->req == NULL) { + continue; + } + tevent_schedule_immediate( + w->im, w->ev, tevent_barrier_release_one, w->req); + + state = tevent_req_data( + w->req, struct tevent_barrier_wait_state); + talloc_set_destructor(state, NULL); + + w->req = NULL; + w->ev = NULL; + } + b->count = 0; + if (b->trigger_cb != NULL) { + b->trigger_cb(b->private_data); + } +} + +static void tevent_barrier_release_one(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + tevent_req_done(req); +} + +static int tevent_barrier_wait_state_destructor( + struct tevent_barrier_wait_state *s); + +struct tevent_req *tevent_barrier_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tevent_barrier *b) +{ + struct tevent_req *req; + struct tevent_barrier_wait_state *state; + struct tevent_barrier_waiter *w; + struct tevent_immediate *im; + + req = tevent_req_create(mem_ctx, &state, + struct tevent_barrier_wait_state); + if (req == NULL) { + return NULL; + } + state->b = b; + state->index = b->count; + + w = &b->waiters[b->count]; + w->ev = ev; + w->req = req; + b->count += 1; + + talloc_set_destructor(state, tevent_barrier_wait_state_destructor); + + if (b->count < talloc_array_length(b->waiters)) { + return req; + } + + im = tevent_create_immediate(req); + if (tevent_req_nomem(im, req)) { + return tevent_req_post(req, ev); + } + tevent_schedule_immediate(im, ev, tevent_barrier_release_trigger, b); + return req; +} + +static int tevent_barrier_wait_state_destructor( + struct tevent_barrier_wait_state *s) +{ + struct tevent_barrier *b = s->b; + b->waiters[s->index].req = b->waiters[b->count-1].req; + b->count -= 1; + return 0; +} + +static void tevent_barrier_release_trigger(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct tevent_barrier *b = talloc_get_type_abort( + private_data, struct tevent_barrier); + tevent_barrier_release(b); +} + +int tevent_barrier_wait_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_unix(req); +} diff --git a/source3/lib/tevent_barrier.h b/source3/lib/tevent_barrier.h new file mode 100644 index 0000000..2b35852 --- /dev/null +++ b/source3/lib/tevent_barrier.h @@ -0,0 +1,37 @@ +/* + Unix SMB/CIFS implementation. + Implement a barrier + Copyright (C) Volker Lendecke 2012 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _TEVENT_BARRIER_H +#define _TEVENT_BARRIER_H + +#include "talloc.h" +#include "tevent.h" + +struct tevent_barrier; + +struct tevent_barrier *tevent_barrier_init( + TALLOC_CTX *mem_ctx, unsigned count, + void (*trigger_cb)(void *private_data), void *private_data); + +struct tevent_req *tevent_barrier_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tevent_barrier *b); +int tevent_barrier_wait_recv(struct tevent_req *req); + +#endif /* _TEVENT_BARRIER_H */ diff --git a/source3/lib/tevent_glib_glue.c b/source3/lib/tevent_glib_glue.c new file mode 100644 index 0000000..3be08fc --- /dev/null +++ b/source3/lib/tevent_glib_glue.c @@ -0,0 +1,881 @@ +/* + Unix SMB/CIFS implementation. + Integration of a glib g_main_context into a tevent_context + Copyright (C) Stefan Metzmacher 2016 + Copyright (C) Ralph Boehme 2016 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "lib/util/debug.h" +#include "lib/util/select.h" +#include <tevent.h> + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_TEVENT + +#ifdef HAVE_GLIB +#include <glib.h> +#include "tevent_glib_glue.h" + +struct fd_map { + struct tevent_glib_glue *glue; + int fd; + struct tevent_fd *fd_event; +}; + +struct tevent_glib_glue { + /* + * The tevent context we're feeding. + */ + struct tevent_context *ev; + + /* + * The glib gmain context we're polling and whether we're currently + * owning it by virtue of g_main_context_acquire(). + */ + GMainContext *gmain_ctx; + bool gmain_owner; + + /* + * Set by samba_tevent_glib_glue_quit(). + */ + bool quit; + + /* + * tevent trace callback and data we got from tevent_get_trace_callback() + * before installing our own trace callback. + */ + tevent_trace_callback_t prev_tevent_trace_cb; + void *prev_tevent_trace_data; + + /* + * Don't call tevent_glib_prepare() in the tevent tracepoint handler if + * explicitly told so. This is an optimisation for the case that glib + * event sources are created from glib event callbacks. + */ + bool skip_glib_refresh; + + /* + * Used when acquiring the glib gmain context failed. + */ + struct tevent_timer *acquire_retry_timer; + + /* + * glib gmain context timeout and priority for the current event look + * iteration. gtimeout is translated to a tevent timer event, unless it + * is 0 which signals some event source is pending. In that case we + * dispatch an immediate event. gpriority is ignored by us, just passed + * to the glib relevant functions. + */ + gint gtimeout; + gint gpriority; + struct tevent_timer *timer; + struct tevent_immediate *im; + bool scheduled_im; + + /* + * glib gmain context fds returned from g_main_context_query(). These + * get translated to tevent fd events. + */ + GPollFD *gpollfds; + gint num_gpollfds; + + /* + * A copy of gpollfds and num_gpollfds from the previous event loop + * iteration, used to detect changes in the set of fds. + */ + GPollFD *prev_gpollfds; + gint num_prev_gpollfds; + + /* + * An array of pointers to fd_map's. The fd_map'd contain the tevent + * event fd as well as a pointer to the corresponding glib GPollFD. + */ + struct fd_map **fd_map; + size_t num_maps; +}; + +static bool tevent_glib_prepare(struct tevent_glib_glue *glue); +static bool tevent_glib_process(struct tevent_glib_glue *glue); +static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue); +static void tevent_glib_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data); + +typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2); +typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue, + const GPollFD *new, + const GPollFD *old); +typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue, + const GPollFD *fd); +typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue, + const GPollFD *fd); + +/** + * Compare two sorted GPollFD arrays + * + * For every element that exists in gfds and prev_gfds found_fn() is called. + * For every element in gfds but not in prev_gfds, new_fn() is called. + * For every element in prev_gfds but not in gfds removed_fn() is called. + **/ +static bool cmp_gfds(struct tevent_glib_glue *glue, + GPollFD *gfds, + GPollFD *prev_gfds, + size_t num_gfds, + size_t num_prev_gfds, + gfds_cmp_cb cmp_cb, + gfds_found_cb found_cb, + gfds_new_cb new_cb, + gfds_removed_cb removed_cb) +{ + bool ok; + size_t i = 0, j = 0; + int cmp; + + while (i < num_gfds && j < num_prev_gfds) { + cmp = cmp_cb(&gfds[i], &prev_gfds[j]); + if (cmp == 0) { + ok = found_cb(glue, &gfds[i], &prev_gfds[j]); + if (!ok) { + return false; + } + i++; + j++; + } else if (cmp < 0) { + ok = new_cb(glue, &gfds[i]); + if (!ok) { + return false; + } + i++; + } else { + ok = removed_cb(glue, &prev_gfds[j]); + if (!ok) { + return false; + } + j++; + } + } + + while (i < num_gfds) { + ok = new_cb(glue, &gfds[i++]); + if (!ok) { + return false; + } + } + + while (j < num_prev_gfds) { + ok = removed_cb(glue, &prev_gfds[j++]); + if (!ok) { + return false; + } + } + + return true; +} + +static int glib_fd_cmp_func(const void *p1, const void *p2) +{ + const GPollFD *lhs = p1; + const GPollFD *rhs = p2; + + if (lhs->fd < rhs->fd) { + return -1; + } else if (lhs->fd > rhs->fd) { + return 1; + } + + return 0; +} + +/* + * We already have a tevent fd event for the glib GPollFD, but we may have to + * update flags. + */ +static bool match_gfd_cb(struct tevent_glib_glue *glue, + const GPollFD *new_gfd, + const GPollFD *old_gfd) +{ + size_t i; + struct fd_map *fd_map = NULL; + struct tevent_fd *fd_event = NULL; + + if (new_gfd->events == old_gfd->events) { + return true; + } + + for (i = 0; i < glue->num_maps; i++) { + if (glue->fd_map[i]->fd == new_gfd->fd) { + break; + } + } + + if (i == glue->num_maps) { + DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd); + return false; + } + + fd_map = glue->fd_map[i]; + if (fd_map == NULL) { + DBG_ERR("fd_map for fd %d is NULL\n", new_gfd->fd); + return false; + } + + fd_event = fd_map->fd_event; + if (fd_event == NULL) { + DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd); + return false; + } + + tevent_fd_set_flags(fd_event, 0); + + if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { + TEVENT_FD_READABLE(fd_event); + } + if (new_gfd->events & G_IO_OUT) { + TEVENT_FD_WRITEABLE(fd_event); + } + + return true; +} + +static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd) +{ + struct tevent_fd *fd_event = NULL; + struct fd_map *fd_map = NULL; + uint16_t events = 0; + bool revent; + bool wevent; + + revent = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)); + wevent = (gfd->events & G_IO_OUT); + if (revent) { + events |= TEVENT_FD_READ; + } + if (wevent) { + events |= TEVENT_FD_WRITE; + } + + glue->fd_map = talloc_realloc(glue, + glue->fd_map, + struct fd_map *, + glue->num_maps + 1); + if (glue->fd_map == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + fd_map = talloc_zero(glue->fd_map, struct fd_map); + if (fd_map == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + glue->fd_map[glue->num_maps] = fd_map; + glue->num_maps++; + + fd_event = tevent_add_fd(glue->ev, + glue->fd_map, + gfd->fd, + events, + tevent_glib_fd_handler, + fd_map); + if (fd_event == NULL) { + DBG_ERR("tevent_add_fd failed\n"); + return false; + } + + *fd_map = (struct fd_map) { + .glue = glue, + .fd = gfd->fd, + .fd_event = fd_event, + }; + + DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd); + + return true; +} + +static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd) +{ + size_t i; + + for (i = 0; i < glue->num_maps; i++) { + if (glue->fd_map[i]->fd == gfd->fd) { + break; + } + } + + if (i == glue->num_maps) { + DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd); + return false; + } + + TALLOC_FREE(glue->fd_map[i]->fd_event); + TALLOC_FREE(glue->fd_map[i]); + + if (i + 1 < glue->num_maps) { + memmove(&glue->fd_map[i], + &glue->fd_map[i+1], + (glue->num_maps - (i + 1)) * sizeof(struct fd_map *)); + } + + glue->fd_map = talloc_realloc(glue, + glue->fd_map, + struct fd_map *, + glue->num_maps - 1); + if (glue->num_maps > 0 && glue->fd_map == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + glue->num_maps--; + + return true; +} + +static short gpoll_to_poll_event(gushort gevent) +{ + short pevent = 0; + + if (gevent & G_IO_IN) { + pevent |= POLLIN; + } + if (gevent & G_IO_OUT) { + pevent |= POLLOUT; + } + if (gevent & G_IO_HUP) { + pevent |= POLLHUP; + } + if (gevent & G_IO_ERR) { + pevent |= POLLERR; + } + + return pevent; +} + +static gushort poll_to_gpoll_event(short pevent) +{ + gushort gevent = 0; + + if (pevent & POLLIN) { + gevent |= G_IO_IN; + } + if (pevent & POLLOUT) { + gevent |= G_IO_OUT; + } + if (pevent & POLLHUP) { + gevent |= G_IO_HUP; + } + if (pevent & POLLERR) { + gevent |= G_IO_ERR; + } + + return gevent; +} + +static void tevent_glib_fd_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct fd_map *fd_map = talloc_get_type_abort( + private_data, struct fd_map); + struct tevent_glib_glue *glue = NULL; + GPollFD *gpollfd = NULL; + struct pollfd fd; + int ret; + int i; + + glue = fd_map->glue; + + for (i = 0; i < glue->num_gpollfds; i++) { + if (glue->gpollfds[i].fd != fd_map->fd) { + continue; + } + gpollfd = &glue->gpollfds[i]; + break; + } + if (gpollfd == NULL) { + DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n", + fd_map, fd_map->fd); + return; + } + /* + * We have to poll() the fd to get the correct fd event for glib. tevent + * only tells us about readable/writable in flags, but we need the full + * glory for glib. + */ + + fd = (struct pollfd) { + .fd = gpollfd->fd, + .events = gpoll_to_poll_event(gpollfd->events), + }; + + ret = sys_poll_intr(&fd, 1, 0); + if (ret == -1) { + DBG_ERR("poll: %s\n", strerror(errno)); + return; + } + if (ret == 0) { + return; + } + + gpollfd->revents = poll_to_gpoll_event(fd.revents); + + tevent_glib_process(glue); + return; +} + +static void tevent_glib_timer_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + glue->timer = NULL; + tevent_glib_process(glue); + return; +} + +static void tevent_glib_im_handler(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + glue->scheduled_im = false; + tevent_glib_process(glue); + return; +} + +static bool save_current_fdset(struct tevent_glib_glue *glue) +{ + /* + * Save old glib fds. We only grow the prev array. + */ + + if (glue->num_prev_gpollfds < glue->num_gpollfds) { + glue->prev_gpollfds = talloc_realloc(glue, + glue->prev_gpollfds, + GPollFD, + glue->num_gpollfds); + if (glue->prev_gpollfds == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + } + glue->num_prev_gpollfds = glue->num_gpollfds; + if (glue->num_gpollfds > 0) { + memcpy(glue->prev_gpollfds, glue->gpollfds, + sizeof(GPollFD) * glue->num_gpollfds); + memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds); + } + + return true; +} + +static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue) +{ + bool ok; + gint num_fds; + + ok = save_current_fdset(glue); + if (!ok) { + return false; + } + + while (true) { + num_fds = g_main_context_query(glue->gmain_ctx, + glue->gpriority, + &glue->gtimeout, + glue->gpollfds, + glue->num_gpollfds); + if (num_fds == glue->num_gpollfds) { + break; + } + glue->gpollfds = talloc_realloc(glue, + glue->gpollfds, + GPollFD, + num_fds); + if (num_fds > 0 && glue->gpollfds == NULL) { + DBG_ERR("talloc_realloc failed\n"); + return false; + } + glue->num_gpollfds = num_fds; + }; + + if (glue->num_gpollfds > 0) { + qsort(glue->gpollfds, + num_fds, + sizeof(GPollFD), + glib_fd_cmp_func); + } + + DBG_DEBUG("num fds: %d, timeout: %d ms\n", + num_fds, glue->gtimeout); + + return true; +} + +static bool tevent_glib_update_events(struct tevent_glib_glue *glue) +{ + struct timeval tv; + bool ok; + + ok = cmp_gfds(glue, + glue->gpollfds, + glue->prev_gpollfds, + glue->num_gpollfds, + glue->num_prev_gpollfds, + glib_fd_cmp_func, + match_gfd_cb, + new_gfd_cb, + remove_gfd_cb); + if (!ok) { + return false; + } + + TALLOC_FREE(glue->timer); + + if (glue->gtimeout == -1) { + return true; + } + + if (glue->gtimeout == 0) { + /* + * glue->gtimeout is 0 if g_main_context_query() returned + * timeout=0. That means there are pending events ready to be + * dispatched. We only want to run one event handler per loop + * iteration, so we schedule an immediate event to run it in the + * next iteration. + */ + if (glue->scheduled_im) { + return true; + } + tevent_schedule_immediate(glue->im, + glue->ev, + tevent_glib_im_handler, + glue); + glue->scheduled_im = true; + return true; + } + + tv = tevent_timeval_current_ofs(glue->gtimeout / 1000, + (glue->gtimeout % 1000) * 1000); + + glue->timer = tevent_add_timer(glue->ev, + glue, + tv, + tevent_glib_timer_handler, + glue); + if (glue->timer == NULL) { + DBG_ERR("tevent_add_timer failed\n"); + return false; + } + + return true; +} + +static void tevent_glib_retry_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + glue->acquire_retry_timer = NULL; + (void)tevent_glib_prepare(glue); +} + +/** + * Fetch glib event sources and add them to tevent + * + * Fetch glib event sources and attach corresponding tevent events to our tevent + * context. get_glib_fds_and_timeout() gets the relevant glib event sources: the + * set of active fds and the next timer. tevent_glib_update_events() then + * translates those to tevent and creates tevent events. + * + * When called, the thread must NOT be the owner to the glib main + * context. tevent_glib_prepare() is either the first function when the + * tevent_glib_glue is created, or after tevent_glib_process() has been called + * to process pending event, which will have ceased ownership. + **/ +static bool tevent_glib_prepare(struct tevent_glib_glue *glue) +{ + bool ok; + gboolean gok; + + if (glue->quit) { + /* Set via samba_tevent_glib_glue_quit() */ + return true; + } + + if (glue->acquire_retry_timer != NULL) { + /* + * We're still waiting on the below g_main_context_acquire() to + * succeed, just return. + */ + return true; + } + + if (glue->gmain_owner) { + g_main_context_release(glue->gmain_ctx); + glue->gmain_owner = false; + } + + gok = g_main_context_acquire(glue->gmain_ctx); + if (!gok) { + DBG_ERR("couldn't acquire g_main_context\n"); + + /* + * Ensure no tevent event fires while we're not the gmain + * context owner. The event handler would call + * tevent_glib_process() and that expects being the owner of the + * context. + */ + ok = tevent_glib_glue_reinit(glue); + if (!ok) { + DBG_ERR("tevent_glib_glue_reinit failed\n"); + samba_tevent_glib_glue_quit(glue); + return false; + } + + glue->acquire_retry_timer = tevent_add_timer( + glue->ev, + glue, + tevent_timeval_current_ofs(0, 1000), + tevent_glib_retry_timer, + glue); + if (glue->acquire_retry_timer == NULL) { + DBG_ERR("tevent_add_timer failed\n"); + samba_tevent_glib_glue_quit(glue); + return false; + } + return true; + } + glue->gmain_owner = true; + + /* + * Discard "ready" return value from g_main_context_prepare(). We don't + * want to dispatch events here, that's only done in from the tevent loop. + */ + (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority); + + ok = get_glib_fds_and_timeout(glue); + if (!ok) { + DBG_ERR("get_glib_fds_and_timeout failed\n"); + samba_tevent_glib_glue_quit(glue); + return false; + } + + ok = tevent_glib_update_events(glue); + if (!ok) { + DBG_ERR("tevent_glib_update_events failed\n"); + samba_tevent_glib_glue_quit(glue); + return false; + } + + return true; +} + +/** + * Process pending glib events + * + * tevent_glib_process() gets called to process pending glib events via + * g_main_context_check() and then g_main_context_dispatch(). + * + * After pending event handlers are dispatched, we rearm the glib glue event + * handlers in tevent by calling tevent_glib_prepare(). + * + * When tevent_glib_process() is called the thread must own the glib + * gmain_ctx. That is achieved by tevent_glib_prepare() being the only function + * that acquires context ownership. + * + * To give other threads that are blocked on g_main_context_acquire(gmain_ctx) a + * chance to acquire context ownership (eg needed to attach event sources), we + * release context ownership before calling tevent_glib_prepare() which will + * acquire it again. + */ +static bool tevent_glib_process(struct tevent_glib_glue *glue) +{ + bool ok; + + DBG_DEBUG("tevent_glib_process\n"); + + /* + * Ignore the "sources_ready" return from g_main_context_check(). glib + * itself also ignores it in g_main_context_iterate(). In theory only + * calling g_main_context_dispatch() if g_main_context_check() returns + * true should work, but older glib versions had a bug where + * g_main_context_check() returns false even though events are pending. + * + * https://bugzilla.gnome.org/show_bug.cgi?id=11059 + */ + (void)g_main_context_check(glue->gmain_ctx, + glue->gpriority, + glue->gpollfds, + glue->num_gpollfds); + + g_main_context_dispatch(glue->gmain_ctx); + + ok = tevent_glib_prepare(glue); + if (!ok) { + return false; + } + glue->skip_glib_refresh = true; + return true; +} + +static void tevent_glib_glue_trace_callback(enum tevent_trace_point point, + void *private_data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + private_data, struct tevent_glib_glue); + + if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) { + if (!glue->skip_glib_refresh) { + tevent_glib_prepare(glue); + } + glue->skip_glib_refresh = false; + } + + /* chain previous handler */ + if (glue->prev_tevent_trace_cb != NULL) { + glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data); + } +} + +static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue) +{ + size_t n = talloc_array_length(glue->fd_map); + size_t i; + + for (i = 0; i < n; i++) { + TALLOC_FREE(glue->fd_map[i]->fd_event); + TALLOC_FREE(glue->fd_map[i]); + } + + tevent_set_trace_callback(glue->ev, + glue->prev_tevent_trace_cb, + glue->prev_tevent_trace_data); + glue->prev_tevent_trace_cb = NULL; + glue->prev_tevent_trace_data = NULL; + + TALLOC_FREE(glue->fd_map); + glue->num_maps = 0; + + TALLOC_FREE(glue->gpollfds); + glue->num_gpollfds = 0; + + TALLOC_FREE(glue->prev_gpollfds); + glue->num_prev_gpollfds = 0; + + TALLOC_FREE(glue->timer); + TALLOC_FREE(glue->acquire_retry_timer); + TALLOC_FREE(glue->im); + + /* + * These are not really needed, but let's wipe the slate clean. + */ + glue->skip_glib_refresh = false; + glue->gtimeout = 0; + glue->gpriority = 0; +} + +static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue) +{ + tevent_glib_glue_cleanup(glue); + + glue->im = tevent_create_immediate(glue); + if (glue->im == NULL) { + return false; + } + + tevent_get_trace_callback(glue->ev, + &glue->prev_tevent_trace_cb, + &glue->prev_tevent_trace_data); + tevent_set_trace_callback(glue->ev, + tevent_glib_glue_trace_callback, + glue); + + return true; +} + +void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue) +{ + tevent_glib_glue_cleanup(glue); + glue->quit = true; + return; +} + +struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + GMainContext *gmain_ctx) +{ + bool ok; + struct tevent_glib_glue *glue = NULL; + + glue = talloc_zero(mem_ctx, struct tevent_glib_glue); + if (glue == NULL) { + DBG_ERR("talloc_zero failed\n"); + return NULL; + } + + *glue = (struct tevent_glib_glue) { + .ev = ev, + .gmain_ctx = gmain_ctx, + }; + + glue->im = tevent_create_immediate(glue); + + tevent_get_trace_callback(glue->ev, + &glue->prev_tevent_trace_cb, + &glue->prev_tevent_trace_data); + tevent_set_trace_callback(glue->ev, + tevent_glib_glue_trace_callback, + glue); + + ok = tevent_glib_prepare(glue); + if (!ok) { + TALLOC_FREE(glue); + return NULL; + } + + return glue; +} + +#else /* HAVE_GLIB */ + +struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + GMainContext *gmain_ctx) +{ + errno = ENOSYS; + return NULL; +} + +void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue) +{ + return; +} +#endif /* HAVE_GLIB */ diff --git a/source3/lib/tevent_glib_glue.h b/source3/lib/tevent_glib_glue.h new file mode 100644 index 0000000..0d001fa --- /dev/null +++ b/source3/lib/tevent_glib_glue.h @@ -0,0 +1,68 @@ +/* + Unix SMB/CIFS implementation. + Poll glib event loop from tevent + + Copyright (C) Ralph Boehme 2016 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _TEVENT_GLIB_GLUE_H +#define _TEVENT_GLIB_GLUE_H + +#include <talloc.h> +#include <tevent.h> + +/** + * @brief Add a glib GmainContext to a tevent context + * + * tevent will poll the glib event sources and run handlers for + * pending events as detailed in the glib documentation: + * + * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html + * + * If tevent was built without glib support, this function will always return + * NULL with an error number ENOSYS. + * + * @param[in] mem_ctx Memory context to use + * + * @param[in] ev Event context to use + * + * @param[in] gmain_ctx GMainContext that will be added to tevent + * + * @return A handle on the glue context that binds the + * the GMainContext to tevent. Pass the glue handle to + * tevent_glib_glue_quit() in a callback when you want + * stop processing glib events. + * You must not call talloc_free() on the handle while + * the loop is still in use and attached to tevent. + */ +struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + GMainContext *gmain_ctx); + +/** + * @brief Stop polling a GMainContext + * + * Used in a callback when you want to stop processing glib events. + * + * @param[in] glue And tevent_glib_glue handle + */ +void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue); + +#endif diff --git a/source3/lib/tevent_glib_glue_tests.c b/source3/lib/tevent_glib_glue_tests.c new file mode 100644 index 0000000..d6cf664 --- /dev/null +++ b/source3/lib/tevent_glib_glue_tests.c @@ -0,0 +1,397 @@ +/* + Unix SMB/CIFS implementation. + + testing of the tevent glib glue subsystem + + Copyright (C) Ralph Boehme 2016 + + glib tests adapted from glib2 glib/tests/mainloop.c + Copyright (C) 2011 Red Hat Inc., Matthias Clasen + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" + +/* + * glib uses TRUE and FALSE which may have redefined by "includes.h" to be + * unusable. Unndefine so glib can establish its own working replacement. + */ +#undef TRUE +#undef FALSE +#include <glib.h> +#include <glib-unix.h> +#include "lib/tevent_glib_glue.h" + +/* + * Unfortunately the glib test suite runner doesn't pass args to tests + * so we must keep a few globals here. + */ +static struct tevent_context *ev; + +static gboolean count_calls(gpointer data) +{ + gint *i = (gint *)data; + + (*i)++; + + return TRUE; +} + +static gboolean quit_loop(gpointer data) +{ + struct tevent_glib_glue *glue = talloc_get_type_abort( + data, struct tevent_glib_glue); + + samba_tevent_glib_glue_quit(glue); + + return G_SOURCE_REMOVE; +} + +static void test_timeouts(void) +{ + GMainContext *ctx = NULL; + struct tevent_glib_glue *glue = NULL; + GSource *source = NULL; + gint a; + gint b; + gint c; + + a = b = c = 0; + + ctx = g_main_context_new(); + glue = samba_tevent_glib_glue_create(ev, ev, ctx); + g_assert(glue != NULL); + + source = g_timeout_source_new(100); + g_source_set_callback(source, count_calls, &a, NULL); + g_source_attach(source, ctx); + g_source_unref(source); + + source = g_timeout_source_new(250); + g_source_set_callback(source, count_calls, &b, NULL); + g_source_attach(source, ctx); + g_source_unref(source); + + source = g_timeout_source_new(330); + g_source_set_callback(source, count_calls, &c, NULL); + g_source_attach(source, ctx); + g_source_unref(source); + + source = g_timeout_source_new(1050); + g_source_set_callback(source, quit_loop, glue, NULL); + g_source_attach(source, ctx); + g_source_unref(source); + + g_assert(tevent_loop_wait(ev) == 0); + + /* We may be delayed for an arbitrary amount of time - for example, + * it's possible for all timeouts to fire exactly once. + */ + g_assert_cmpint(a, >, 0); + g_assert_cmpint(a, >=, b); + g_assert_cmpint(b, >=, c); + + g_assert_cmpint(a, <=, 10); + g_assert_cmpint(b, <=, 4); + g_assert_cmpint(c, <=, 3); + + samba_tevent_glib_glue_quit(glue); + TALLOC_FREE(glue); + g_main_context_unref(ctx); +} + +struct test_glib_ev_source_data { + GMainContext *ctx; + struct tevent_glib_glue *glue; +}; + +static gboolean test_glib_ev_source_quit_loop(gpointer data); + +static gboolean test_glib_ev_source_timeout_cb(gpointer data) +{ + struct test_glib_ev_source_data *state = talloc_get_type_abort( + data, struct test_glib_ev_source_data); + GSource *source = NULL; + + source = g_timeout_source_new(100); + g_source_set_callback(source, + test_glib_ev_source_quit_loop, + state, + NULL); + g_source_attach(source, state->ctx); + g_source_unref(source); + + return TRUE; +} + +static gboolean test_glib_ev_source_quit_loop(gpointer data) +{ + struct test_glib_ev_source_data *state = talloc_get_type_abort( + data, struct test_glib_ev_source_data); + + samba_tevent_glib_glue_quit(state->glue); + + return G_SOURCE_REMOVE; +} + +static void test_glib_ev_source(void) +{ + GMainContext *ctx = NULL; + struct tevent_glib_glue *glue = NULL; + struct test_glib_ev_source_data *state = NULL; + GSource *source = NULL; + + ctx = g_main_context_new(); + g_assert(ctx != NULL); + + glue = samba_tevent_glib_glue_create(ev, ev, ctx); + g_assert(glue != NULL); + + state = talloc_zero(glue, struct test_glib_ev_source_data); + g_assert(state != NULL); + + state->ctx = ctx; + state->glue = glue; + + source = g_timeout_source_new(100); + g_source_set_callback(source, + test_glib_ev_source_timeout_cb, + state, + NULL); + g_source_attach(source, ctx); + g_source_unref(source); + + g_assert(tevent_loop_wait(ev) == 0); + + TALLOC_FREE(glue); + g_main_context_unref(ctx); +} + +struct test_tevent_ev_source_data { + GMainContext *ctx; + struct tevent_glib_glue *glue; +}; + +static gboolean test_tevent_ev_source_quit_loop(gpointer data); + +static void test_tevent_ev_source_timeout_cb(struct tevent_context *_ev, + struct tevent_timer *te, + struct timeval current_time, + void *data) +{ + struct test_tevent_ev_source_data *state = talloc_get_type_abort( + data, struct test_tevent_ev_source_data); + GSource *source = NULL; + + source = g_timeout_source_new(100); + g_source_set_callback(source, + test_tevent_ev_source_quit_loop, + state, + NULL); + g_source_attach(source, state->ctx); + g_source_unref(source); + + return; +} + +static gboolean test_tevent_ev_source_quit_loop(gpointer data) +{ + struct test_tevent_ev_source_data *state = talloc_get_type_abort( + data, struct test_tevent_ev_source_data); + + samba_tevent_glib_glue_quit(state->glue); + + return G_SOURCE_REMOVE; +} + +static void test_tevent_ev_source(void) +{ + GMainContext *ctx = NULL; + struct tevent_glib_glue *glue = NULL; + struct test_tevent_ev_source_data *state = NULL; + struct tevent_timer *timer = NULL; + + ctx = g_main_context_new(); + g_assert(ctx != NULL); + + glue = samba_tevent_glib_glue_create(ev, ev, ctx); + g_assert(glue != NULL); + + state = talloc_zero(glue, struct test_tevent_ev_source_data); + g_assert(state != NULL); + + state->ctx = ctx; + state->glue = glue; + + timer = tevent_add_timer(ev, + state, + tevent_timeval_current_ofs(0, 1000), + test_tevent_ev_source_timeout_cb, + state); + g_assert(timer != NULL); + + g_assert(tevent_loop_wait(ev) == 0); + + TALLOC_FREE(glue); + g_main_context_unref(ctx); +} + +static gchar zeros[1024]; + +static gsize fill_a_pipe(gint fd) +{ + gsize written = 0; + GPollFD pfd; + + pfd.fd = fd; + pfd.events = G_IO_OUT; + while (g_poll(&pfd, 1, 0) == 1) + /* we should never see -1 here */ + written += write(fd, zeros, sizeof zeros); + + return written; +} + +static gboolean write_bytes(gint fd, + GIOCondition condition, + gpointer user_data) +{ + gssize *to_write = user_data; + gint limit; + + if (*to_write == 0) + return FALSE; + + /* Detect if we run before we should */ + g_assert(*to_write >= 0); + + limit = MIN(*to_write, sizeof zeros); + *to_write -= write(fd, zeros, limit); + + return TRUE; +} + +static gboolean read_bytes(gint fd, + GIOCondition condition, + gpointer user_data) +{ + static gchar buffer[1024]; + gssize *to_read = user_data; + + *to_read -= read(fd, buffer, sizeof buffer); + + /* The loop will exit when there is nothing else to read, then we will + * use g_source_remove() to destroy this source. + */ + return TRUE; +} + +static void test_unix_fd(void) +{ + gssize to_write = -1; + gssize to_read; + gint fds[2]; + gint a, b; + gint s; + GSource *source_a = NULL; + GSource *source_b = NULL; + struct tevent_glib_glue *glue = NULL; + + glue = samba_tevent_glib_glue_create(ev, ev, g_main_context_default()); + g_assert(glue != NULL); + + s = pipe(fds); + g_assert(s == 0); + + to_read = fill_a_pipe(fds[1]); + /* write at higher priority to keep the pipe full... */ + a = g_unix_fd_add_full(G_PRIORITY_HIGH, + fds[1], + G_IO_OUT, + write_bytes, + &to_write, + NULL); + source_a = g_source_ref(g_main_context_find_source_by_id(NULL, a)); + /* make sure no 'writes' get dispatched yet */ + while (tevent_loop_once(ev)); + + to_read += 128 * 1024 * 1024; + to_write = 128 * 1024 * 1024; + b = g_unix_fd_add(fds[0], G_IO_IN, read_bytes, &to_read); + source_b = g_source_ref(g_main_context_find_source_by_id(NULL, b)); + + /* Assuming the kernel isn't internally 'laggy' then there will always + * be either data to read or room in which to write. That will keep + * the loop running until all data has been read and written. + */ + while (to_write > 0 || to_read > 0) + { + gssize to_write_was = to_write; + gssize to_read_was = to_read; + + if (tevent_loop_once(ev) != 0) + break; + + /* Since the sources are at different priority, only one of them + * should possibly have run. + */ + g_assert(to_write == to_write_was || to_read == to_read_was); + } + + g_assert(to_write == 0); + g_assert(to_read == 0); + + /* 'a' is already removed by itself */ + g_assert(g_source_is_destroyed(source_a)); + g_source_unref(source_a); + g_source_remove(b); + g_assert(g_source_is_destroyed(source_b)); + g_source_unref(source_b); + + samba_tevent_glib_glue_quit(glue); + TALLOC_FREE(glue); + + close(fds[1]); + close(fds[0]); +} + +int main(int argc, const char *argv[]) +{ + int test_argc = 3; + char *test_argv[] = { + discard_const("test_glib_glue"), + discard_const("-m"), + discard_const("no-undefined") + }; + char **argvp = test_argv; + + g_test_init(&test_argc, &argvp, NULL); + + ev = tevent_context_init(NULL); + if (ev == NULL) { + exit(1); + } + + g_test_add_func("/mainloop/timeouts", test_timeouts); + g_test_add_func("/mainloop/glib_ev_source", test_glib_ev_source); + g_test_add_func("/mainloop/tevent_ev_source", test_tevent_ev_source); + g_test_add_func("/mainloop/unix-fd", test_unix_fd); + + return g_test_run(); +} diff --git a/source3/lib/time.c b/source3/lib/time.c new file mode 100644 index 0000000..0d44a08 --- /dev/null +++ b/source3/lib/time.c @@ -0,0 +1,458 @@ +/* + Unix SMB/CIFS implementation. + time handling functions + + Copyright (C) Andrew Tridgell 1992-2004 + Copyright (C) Stefan (metze) Metzmacher 2002 + Copyright (C) Jeremy Allison 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/** + * @file + * @brief time handling functions + */ + + +#define NTTIME_INFINITY (NTTIME)0x8000000000000000LL + +#define TIME_FIXUP_CONSTANT_INT INT64_C(11644473600) + +/************************************************************** + Handle conversions between time_t and uint32, taking care to + preserve the "special" values. +**************************************************************/ + +uint32_t convert_time_t_to_uint32_t(time_t t) +{ +#if (defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T == 8)) + /* time_t is 64-bit. */ + if (t == 0x8000000000000000LL) { + return 0x80000000; + } else if (t == 0x7FFFFFFFFFFFFFFFLL) { + return 0x7FFFFFFF; + } +#endif + return (uint32_t)t; +} + +time_t convert_uint32_t_to_time_t(uint32_t u) +{ +#if (defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T == 8)) + /* time_t is 64-bit. */ + if (u == 0x80000000) { + return (time_t)0x8000000000000000LL; + } else if (u == 0x7FFFFFFF) { + return (time_t)0x7FFFFFFFFFFFFFFFLL; + } +#endif + return (time_t)u; +} + +/**************************************************************************** + Check if NTTIME is 0. +****************************************************************************/ + +bool nt_time_is_zero(const NTTIME *nt) +{ + return (*nt == 0); +} + +/**************************************************************************** + Convert ASN.1 GeneralizedTime string to unix-time. + Returns 0 on failure; Currently ignores timezone. +****************************************************************************/ + +time_t generalized_to_unix_time(const char *str) +{ + struct tm tm; + + ZERO_STRUCT(tm); + + if (sscanf(str, "%4d%2d%2d%2d%2d%2d", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + return 0; + } + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + return timegm(&tm); +} + +/******************************************************************* + Accessor function for the server time zone offset. + set_server_zone_offset() must have been called first. +******************************************************************/ + +static int server_zone_offset; + +int get_server_zone_offset(void) +{ + return server_zone_offset; +} + +/******************************************************************* + Initialize the server time zone offset. Called when a client connects. +******************************************************************/ + +int set_server_zone_offset(time_t t) +{ + server_zone_offset = get_time_zone(t); + return server_zone_offset; +} + +/*************************************************************************** + Server versions of the above functions. +***************************************************************************/ + +void srv_put_dos_date(char *buf,int offset,time_t unixdate) +{ + push_dos_date((uint8_t *)buf, offset, unixdate, server_zone_offset); +} + +void srv_put_dos_date2_ts(char *buf, int offset, struct timespec unix_ts) +{ + time_t unixdate = convert_timespec_to_time_t(unix_ts); + push_dos_date2((uint8_t *)buf, offset, unixdate, server_zone_offset); +} + +void srv_put_dos_date3(char *buf,int offset,time_t unixdate) +{ + push_dos_date3((uint8_t *)buf, offset, unixdate, server_zone_offset); +} + +void round_timespec(enum timestamp_set_resolution res, struct timespec *ts) +{ + if (is_omit_timespec(ts)) { + return; + } + + switch (res) { + case TIMESTAMP_SET_SECONDS: + round_timespec_to_sec(ts); + break; + case TIMESTAMP_SET_MSEC: + round_timespec_to_usec(ts); + break; + case TIMESTAMP_SET_NT_OR_BETTER: + /* No rounding needed. */ + break; + } +} + +/**************************************************************************** + Take a Unix time and convert to an NTTIME structure and place in buffer + pointed to by p, rounded to the correct resolution. +****************************************************************************/ + +void put_long_date_timespec(enum timestamp_set_resolution res, char *p, struct timespec ts) +{ + NTTIME nt; + round_timespec(res, &ts); + nt = unix_timespec_to_nt_time(ts); + SBVAL(p, 0, nt); +} + +void put_long_date_full_timespec(enum timestamp_set_resolution res, + char *p, + const struct timespec *_ts) +{ + struct timespec ts = *_ts; + NTTIME nt; + + round_timespec(res, &ts); + nt = full_timespec_to_nt_time(&ts); + SBVAL(p, 0, nt); +} + +struct timespec pull_long_date_full_timespec(const char *p) +{ + NTTIME nt = BVAL(p, 0); + + return nt_time_to_full_timespec(nt); +} + +void put_long_date(char *p, time_t t) +{ + struct timespec ts; + ts.tv_sec = t; + ts.tv_nsec = 0; + put_long_date_timespec(TIMESTAMP_SET_SECONDS, p, ts); +} + +void dos_filetime_timespec(struct timespec *tsp) +{ + tsp->tv_sec &= ~1; + tsp->tv_nsec = 0; +} + +/******************************************************************* + Create a unix date (int GMT) from a dos date (which is actually in + localtime). +********************************************************************/ + +time_t make_unix_date(const void *date_ptr, int zone_offset) +{ + return pull_dos_date(date_ptr, zone_offset); +} + +/******************************************************************* + Like make_unix_date() but the words are reversed. +********************************************************************/ + +time_t make_unix_date2(const void *date_ptr, int zone_offset) +{ + return pull_dos_date2(date_ptr, zone_offset); +} + +/******************************************************************* + Create a unix GMT date from a dos date in 32 bit "unix like" format + these generally arrive as localtimes, with corresponding DST. +******************************************************************/ + +time_t make_unix_date3(const void *date_ptr, int zone_offset) +{ + return pull_dos_date3(date_ptr, zone_offset); +} + +time_t srv_make_unix_date(const void *date_ptr) +{ + return make_unix_date(date_ptr, server_zone_offset); +} + +time_t srv_make_unix_date2(const void *date_ptr) +{ + return make_unix_date2(date_ptr, server_zone_offset); +} + +time_t srv_make_unix_date3(const void *date_ptr) +{ + return make_unix_date3(date_ptr, server_zone_offset); +} + +/**************************************************************************** + Interprets an nt time into a unix struct timespec. + Differs from nt_time_to_unix in that an 8 byte value of 0xffffffffffffffff + will be returned as (time_t)-1, whereas nt_time_to_unix returns 0 in this case. +****************************************************************************/ + +struct timespec interpret_long_date(NTTIME nt) +{ + if (nt == (uint64_t)-1) { + struct timespec ret; + ret.tv_sec = (time_t)-1; + ret.tv_nsec = 0; + return ret; + } + return nt_time_to_full_timespec(nt); +} + +/******************************************************************* + Re-read the smb serverzone value. +******************************************************************/ + +static struct timeval start_time_hires; + +void TimeInit(void) +{ + set_server_zone_offset(time(NULL)); + + DEBUG(4,("TimeInit: Serverzone is %d\n", server_zone_offset)); + + /* Save the start time of this process. */ + if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0) { + GetTimeOfDay(&start_time_hires); + } +} + +/********************************************************************** + Return a timeval struct of the uptime of this process. As TimeInit is + done before a daemon fork then this is the start time from the parent + daemon start. JRA. +***********************************************************************/ + +void get_process_uptime(struct timeval *ret_time) +{ + struct timeval time_now_hires; + + GetTimeOfDay(&time_now_hires); + ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec; + if (time_now_hires.tv_usec < start_time_hires.tv_usec) { + ret_time->tv_sec -= 1; + ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec); + } else { + ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec; + } +} + +/** + * @brief Get the startup time of the server. + * + * @param[out] ret_time A pointer to a timveal structure to set the startup + * time. + */ +void get_startup_time(struct timeval *ret_time) +{ + ret_time->tv_sec = start_time_hires.tv_sec; + ret_time->tv_usec = start_time_hires.tv_usec; +} + + +/**************************************************************************** + Convert a NTTIME structure to a time_t. + It's originally in "100ns units". + + This is an absolute version of the one above. + By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970 + if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM +****************************************************************************/ + +time_t nt_time_to_unix_abs(const NTTIME *nt) +{ + uint64_t d; + + if (*nt == 0) { + return (time_t)0; + } + + if (*nt == (uint64_t)-1) { + return (time_t)-1; + } + + if (*nt == NTTIME_INFINITY) { + return (time_t)-1; + } + + /* reverse the time */ + /* it's a negative value, turn it to positive */ + d=~*nt; + + d += 1000*1000*10/2; + d /= 1000*1000*10; + + if (!(TIME_T_MIN <= ((time_t)d) && ((time_t)d) <= TIME_T_MAX)) { + return (time_t)0; + } + + return (time_t)d; +} + +/**************************************************************************** + Convert a time_t to a NTTIME structure + + This is an absolute version of the one above. + By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601 + If the time_t was 5 seconds, the NTTIME is 5 seconds. JFM +****************************************************************************/ + +void unix_to_nt_time_abs(NTTIME *nt, time_t t) +{ + double d; + + if (t==0) { + *nt = 0; + return; + } + + if (t == TIME_T_MAX) { + *nt = 0x7fffffffffffffffLL; + return; + } + + if (t == (time_t)-1) { + /* that's what NT uses for infinite */ + *nt = NTTIME_INFINITY; + return; + } + + d = (double)(t); + d *= 1.0e7; + + *nt = (NTTIME)d; + + /* convert to a negative value */ + *nt=~*nt; +} + + +/**************************************************************************** + Utility function that always returns a const string even if localtime + and asctime fail. +****************************************************************************/ + +const char *time_to_asc(const time_t t) +{ + const char *asct; + struct tm *lt = localtime(&t); + + if (!lt) { + return "unknown time\n"; + } + + asct = asctime(lt); + if (!asct) { + return "unknown time\n"; + } + return asct; +} + +const char *display_time(NTTIME nttime) +{ + float high; + float low; + int sec; + int days, hours, mins, secs; + + if (nttime==0) + return "Now"; + + if (nttime==NTTIME_INFINITY) + return "Never"; + + high = 65536; + high = high/10000; + high = high*65536; + high = high/1000; + high = high * (~(nttime >> 32)); + + low = ~(nttime & 0xFFFFFFFF); + low = low/(1000*1000*10); + + sec=(int)(high+low); + + days=sec/(60*60*24); + hours=(sec - (days*60*60*24)) / (60*60); + mins=(sec - (days*60*60*24) - (hours*60*60) ) / 60; + secs=sec - (days*60*60*24) - (hours*60*60) - (mins*60); + + return talloc_asprintf(talloc_tos(), "%u days, %u hours, %u minutes, " + "%u seconds", days, hours, mins, secs); +} + +bool nt_time_is_set(const NTTIME *nt) +{ + if (*nt == 0x7FFFFFFFFFFFFFFFLL) { + return false; + } + + if (*nt == NTTIME_INFINITY) { + return false; + } + + return true; +} diff --git a/source3/lib/tldap.c b/source3/lib/tldap.c new file mode 100644 index 0000000..ad3232b --- /dev/null +++ b/source3/lib/tldap.c @@ -0,0 +1,2726 @@ +/* + Unix SMB/CIFS implementation. + Infrastructure for async ldap client requests + Copyright (C) Volker Lendecke 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "tldap.h" +#include "system/network.h" +#include "system/locale.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/samba_util.h" +#include "lib/util_tsock.h" +#include "../lib/util/asn1.h" +#include "../lib/tsocket/tsocket.h" +#include "../lib/util/tevent_unix.h" + +static TLDAPRC tldap_simple_recv(struct tevent_req *req); +static bool tldap_msg_set_pending(struct tevent_req *req); + +#define TEVENT_TLDAP_RC_MAGIC (0x87bcd26e) + +bool tevent_req_ldap_error(struct tevent_req *req, TLDAPRC rc) +{ + uint64_t err; + + if (TLDAP_RC_IS_SUCCESS(rc)) { + return false; + } + + err = TEVENT_TLDAP_RC_MAGIC; + err <<= 32; + err |= TLDAP_RC_V(rc); + + return tevent_req_error(req, err); +} + +bool tevent_req_is_ldap_error(struct tevent_req *req, TLDAPRC *perr) +{ + enum tevent_req_state state; + uint64_t err; + + if (!tevent_req_is_error(req, &state, &err)) { + return false; + } + switch (state) { + case TEVENT_REQ_TIMED_OUT: + *perr = TLDAP_TIMEOUT; + break; + case TEVENT_REQ_NO_MEMORY: + *perr = TLDAP_NO_MEMORY; + break; + case TEVENT_REQ_USER_ERROR: + if ((err >> 32) != TEVENT_TLDAP_RC_MAGIC) { + abort(); + } + *perr = TLDAP_RC(err & 0xffffffff); + break; + default: + *perr = TLDAP_OPERATIONS_ERROR; + break; + } + return true; +} + +struct tldap_ctx_attribute { + char *name; + void *ptr; +}; + +struct tldap_context { + int ld_version; + struct tstream_context *conn; + int msgid; + struct tevent_queue *outgoing; + struct tevent_req **pending; + struct tevent_req *read_req; + + /* For the sync wrappers we need something like get_last_error... */ + struct tldap_message *last_msg; + + /* debug */ + void (*log_fn)(void *context, enum tldap_debug_level level, + const char *fmt, va_list ap); + void *log_private; + + struct tldap_ctx_attribute *ctx_attrs; +}; + +struct tldap_message { + struct asn1_data *data; + uint8_t *inbuf; + int type; + int id; + + /* RESULT_ENTRY */ + char *dn; + struct tldap_attribute *attribs; + + /* Error data sent by the server */ + TLDAPRC lderr; + char *res_matcheddn; + char *res_diagnosticmessage; + char *res_referral; + DATA_BLOB res_serverSaslCreds; + struct tldap_control *res_sctrls; + + /* Controls sent by the server */ + struct tldap_control *ctrls; +}; + +void tldap_set_debug(struct tldap_context *ld, + void (*log_fn)(void *log_private, + enum tldap_debug_level level, + const char *fmt, + va_list ap) PRINTF_ATTRIBUTE(3,0), + void *log_private) +{ + ld->log_fn = log_fn; + ld->log_private = log_private; +} + +static void tldap_debug( + struct tldap_context *ld, + enum tldap_debug_level level, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); + +static void tldap_debug(struct tldap_context *ld, + enum tldap_debug_level level, + const char *fmt, ...) +{ + va_list ap; + if (!ld) { + return; + } + if (ld->log_fn == NULL) { + return; + } + va_start(ap, fmt); + ld->log_fn(ld->log_private, level, fmt, ap); + va_end(ap); +} + +static int tldap_next_msgid(struct tldap_context *ld) +{ + int result; + + result = ld->msgid++; + if (ld->msgid == INT_MAX) { + ld->msgid = 1; + } + return result; +} + +struct tldap_context *tldap_context_create(TALLOC_CTX *mem_ctx, int fd) +{ + struct tldap_context *ctx; + int ret; + + ctx = talloc_zero(mem_ctx, struct tldap_context); + if (ctx == NULL) { + return NULL; + } + ret = tstream_bsd_existing_socket(ctx, fd, &ctx->conn); + if (ret == -1) { + TALLOC_FREE(ctx); + return NULL; + } + ctx->msgid = 1; + ctx->ld_version = 3; + ctx->outgoing = tevent_queue_create(ctx, "tldap_outgoing"); + if (ctx->outgoing == NULL) { + TALLOC_FREE(ctx); + return NULL; + } + return ctx; +} + +bool tldap_connection_ok(struct tldap_context *ld) +{ + int ret; + + if (ld == NULL) { + return false; + } + + if (ld->conn == NULL) { + return false; + } + + ret = tstream_pending_bytes(ld->conn); + if (ret == -1) { + return false; + } + + return true; +} + +static size_t tldap_pending_reqs(struct tldap_context *ld) +{ + return talloc_array_length(ld->pending); +} + +struct tstream_context *tldap_get_tstream(struct tldap_context *ld) +{ + return ld->conn; +} + +void tldap_set_tstream(struct tldap_context *ld, + struct tstream_context *stream) +{ + ld->conn = stream; +} + +static struct tldap_ctx_attribute *tldap_context_findattr( + struct tldap_context *ld, const char *name) +{ + size_t i, num_attrs; + + num_attrs = talloc_array_length(ld->ctx_attrs); + + for (i=0; i<num_attrs; i++) { + if (strcmp(ld->ctx_attrs[i].name, name) == 0) { + return &ld->ctx_attrs[i]; + } + } + return NULL; +} + +bool tldap_context_setattr(struct tldap_context *ld, + const char *name, const void *_pptr) +{ + struct tldap_ctx_attribute *tmp, *attr; + char *tmpname; + int num_attrs; + void **pptr = (void **)discard_const_p(void,_pptr); + + attr = tldap_context_findattr(ld, name); + if (attr != NULL) { + /* + * We don't actually delete attrs, we don't expect tons of + * attributes being shuffled around. + */ + TALLOC_FREE(attr->ptr); + if (*pptr != NULL) { + attr->ptr = talloc_move(ld->ctx_attrs, pptr); + *pptr = NULL; + } + return true; + } + + tmpname = talloc_strdup(ld, name); + if (tmpname == NULL) { + return false; + } + + num_attrs = talloc_array_length(ld->ctx_attrs); + + tmp = talloc_realloc(ld, ld->ctx_attrs, struct tldap_ctx_attribute, + num_attrs+1); + if (tmp == NULL) { + TALLOC_FREE(tmpname); + return false; + } + tmp[num_attrs].name = talloc_move(tmp, &tmpname); + if (*pptr != NULL) { + tmp[num_attrs].ptr = talloc_move(tmp, pptr); + } else { + tmp[num_attrs].ptr = NULL; + } + *pptr = NULL; + ld->ctx_attrs = tmp; + return true; +} + +void *tldap_context_getattr(struct tldap_context *ld, const char *name) +{ + struct tldap_ctx_attribute *attr = tldap_context_findattr(ld, name); + + if (attr == NULL) { + return NULL; + } + return attr->ptr; +} + +struct read_ldap_state { + uint8_t *buf; + bool done; +}; + +static ssize_t read_ldap_more(uint8_t *buf, size_t buflen, void *private_data); +static void read_ldap_done(struct tevent_req *subreq); + +static struct tevent_req *read_ldap_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *conn) +{ + struct tevent_req *req, *subreq; + struct read_ldap_state *state; + + req = tevent_req_create(mem_ctx, &state, struct read_ldap_state); + if (req == NULL) { + return NULL; + } + state->done = false; + + subreq = tstream_read_packet_send(state, ev, conn, 2, read_ldap_more, + state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, read_ldap_done, req); + return req; +} + +static ssize_t read_ldap_more(uint8_t *buf, size_t buflen, void *private_data) +{ + struct read_ldap_state *state = talloc_get_type_abort( + private_data, struct read_ldap_state); + size_t len; + int i, lensize; + + if (state->done) { + /* We've been here, we're done */ + return 0; + } + + /* + * From ldap.h: LDAP_TAG_MESSAGE is 0x30 + */ + if (buf[0] != 0x30) { + return -1; + } + + len = buf[1]; + if ((len & 0x80) == 0) { + state->done = true; + return len; + } + + lensize = (len & 0x7f); + len = 0; + + if (buflen == 2) { + /* Please get us the full length */ + return lensize; + } + if (buflen > 2 + lensize) { + state->done = true; + return 0; + } + if (buflen != 2 + lensize) { + return -1; + } + + for (i=0; i<lensize; i++) { + len = (len << 8) | buf[2+i]; + } + return len; +} + +static void read_ldap_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct read_ldap_state *state = tevent_req_data( + req, struct read_ldap_state); + ssize_t nread; + int err; + + nread = tstream_read_packet_recv(subreq, state, &state->buf, &err); + TALLOC_FREE(subreq); + if (nread == -1) { + tevent_req_error(req, err); + return; + } + tevent_req_done(req); +} + +static ssize_t read_ldap_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **pbuf, int *perrno) +{ + struct read_ldap_state *state = tevent_req_data( + req, struct read_ldap_state); + + if (tevent_req_is_unix_error(req, perrno)) { + return -1; + } + *pbuf = talloc_move(mem_ctx, &state->buf); + return talloc_get_size(*pbuf); +} + +struct tldap_msg_state { + struct tldap_context *ld; + struct tevent_context *ev; + int id; + struct iovec iov; + + struct asn1_data *data; + uint8_t *inbuf; +}; + +static bool tldap_push_controls(struct asn1_data *data, + struct tldap_control *sctrls, + int num_sctrls) +{ + int i; + + if ((sctrls == NULL) || (num_sctrls == 0)) { + return true; + } + + if (!asn1_push_tag(data, ASN1_CONTEXT(0))) return false; + + for (i=0; i<num_sctrls; i++) { + struct tldap_control *c = &sctrls[i]; + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) return false; + if (!asn1_write_OctetString(data, c->oid, strlen(c->oid))) return false; + if (c->critical) { + if (!asn1_write_BOOLEAN(data, true)) return false; + } + if (c->value.data != NULL) { + if (!asn1_write_OctetString(data, c->value.data, + c->value.length)) return false; + } + if (!asn1_pop_tag(data)) return false; /* ASN1_SEQUENCE(0) */ + } + + return asn1_pop_tag(data); /* ASN1_CONTEXT(0) */ +} + +#define tldap_context_disconnect(ld, status) \ + _tldap_context_disconnect(ld, status, __location__) + +static void _tldap_context_disconnect(struct tldap_context *ld, + TLDAPRC status, + const char *location) +{ + if (ld->conn == NULL) { + /* + * We don't need to tldap_debug() on + * a potential 2nd run. + * + * The rest of the function would just + * be a noop for the 2nd run anyway. + */ + return; + } + + tldap_debug(ld, TLDAP_DEBUG_WARNING, + "tldap_context_disconnect: %s at %s\n", + tldap_rc2string(status), + location); + tevent_queue_stop(ld->outgoing); + TALLOC_FREE(ld->read_req); + TALLOC_FREE(ld->conn); + + while (talloc_array_length(ld->pending) > 0) { + struct tevent_req *req = NULL; + struct tldap_msg_state *state = NULL; + + req = ld->pending[0]; + state = tevent_req_data(req, struct tldap_msg_state); + tevent_req_defer_callback(req, state->ev); + tevent_req_ldap_error(req, status); + } +} + +static void tldap_msg_sent(struct tevent_req *subreq); +static void tldap_msg_received(struct tevent_req *subreq); + +static struct tevent_req *tldap_msg_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + int id, struct asn1_data *data, + struct tldap_control *sctrls, + int num_sctrls) +{ + struct tevent_req *req, *subreq; + struct tldap_msg_state *state; + DATA_BLOB blob; + bool ok; + + tldap_debug(ld, TLDAP_DEBUG_TRACE, "tldap_msg_send: sending msg %d\n", + id); + + req = tevent_req_create(mem_ctx, &state, struct tldap_msg_state); + if (req == NULL) { + return NULL; + } + state->ld = ld; + state->ev = ev; + state->id = id; + + ok = tldap_connection_ok(ld); + if (!ok) { + tevent_req_ldap_error(req, TLDAP_SERVER_DOWN); + return tevent_req_post(req, ev); + } + + if (!tldap_push_controls(data, sctrls, num_sctrls)) { + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); + } + + + if (!asn1_pop_tag(data)) { + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); + } + + if (!asn1_blob(data, &blob)) { + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); + } + + if (!tldap_msg_set_pending(req)) { + tevent_req_oom(req); + return tevent_req_post(req, ev);; + } + + state->iov.iov_base = (void *)blob.data; + state->iov.iov_len = blob.length; + + subreq = tstream_writev_queue_send(state, ev, ld->conn, ld->outgoing, + &state->iov, 1); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_msg_sent, req); + return req; +} + +static void tldap_msg_unset_pending(struct tevent_req *req) +{ + struct tldap_msg_state *state = tevent_req_data( + req, struct tldap_msg_state); + struct tldap_context *ld = state->ld; + int num_pending = tldap_pending_reqs(ld); + int i; + + tevent_req_set_cleanup_fn(req, NULL); + + for (i=0; i<num_pending; i++) { + if (req == ld->pending[i]) { + break; + } + } + if (i == num_pending) { + /* + * Something's seriously broken. Just returning here is the + * right thing nevertheless, the point of this routine is to + * remove ourselves from cli->pending. + */ + return; + } + + if (num_pending == 1) { + TALLOC_FREE(ld->pending); + return; + } + + /* + * Remove ourselves from the cli->pending array + */ + if (num_pending > 1) { + ld->pending[i] = ld->pending[num_pending-1]; + } + + /* + * No NULL check here, we're shrinking by sizeof(void *), and + * talloc_realloc just adjusts the size for this. + */ + ld->pending = talloc_realloc(NULL, ld->pending, struct tevent_req *, + num_pending - 1); +} + +static void tldap_msg_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + tldap_msg_unset_pending(req); +} + +static bool tldap_msg_set_pending(struct tevent_req *req) +{ + struct tldap_msg_state *state = tevent_req_data( + req, struct tldap_msg_state); + struct tldap_context *ld; + struct tevent_req **pending; + int num_pending; + + ld = state->ld; + num_pending = tldap_pending_reqs(ld); + + pending = talloc_realloc(ld, ld->pending, struct tevent_req *, + num_pending+1); + if (pending == NULL) { + return false; + } + pending[num_pending] = req; + ld->pending = pending; + tevent_req_set_cleanup_fn(req, tldap_msg_cleanup); + + if (ld->read_req != NULL) { + return true; + } + + /* + * We're the first one, add the read_ldap request that waits for the + * answer from the server + */ + ld->read_req = read_ldap_send(ld->pending, state->ev, ld->conn); + if (ld->read_req == NULL) { + tldap_msg_unset_pending(req); + return false; + } + tevent_req_set_callback(ld->read_req, tldap_msg_received, ld); + return true; +} + +static void tldap_msg_sent(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_msg_state *state = tevent_req_data( + req, struct tldap_msg_state); + ssize_t nwritten; + int err; + + nwritten = tstream_writev_queue_recv(subreq, &err); + TALLOC_FREE(subreq); + if (nwritten == -1) { + tldap_context_disconnect(state->ld, TLDAP_SERVER_DOWN); + return; + } +} + +static int tldap_msg_msgid(struct tevent_req *req) +{ + struct tldap_msg_state *state = tevent_req_data( + req, struct tldap_msg_state); + + return state->id; +} + +static void tldap_msg_received(struct tevent_req *subreq) +{ + struct tldap_context *ld = tevent_req_callback_data( + subreq, struct tldap_context); + struct tevent_req *req; + struct tldap_msg_state *state; + struct asn1_data *data; + uint8_t *inbuf; + ssize_t received; + size_t num_pending; + size_t i; + int err; + TLDAPRC status = TLDAP_PROTOCOL_ERROR; + int id; + uint8_t type; + bool ok; + + received = read_ldap_recv(subreq, talloc_tos(), &inbuf, &err); + TALLOC_FREE(subreq); + ld->read_req = NULL; + if (received == -1) { + status = TLDAP_SERVER_DOWN; + goto fail; + } + + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); + if (data == NULL) { + /* + * We have to disconnect all, we can't tell which of + * the requests this reply is for. + */ + status = TLDAP_NO_MEMORY; + goto fail; + } + asn1_load_nocopy(data, inbuf, received); + + ok = true; + ok &= asn1_start_tag(data, ASN1_SEQUENCE(0)); + ok &= asn1_read_Integer(data, &id); + ok &= asn1_peek_uint8(data, &type); + + if (!ok) { + status = TLDAP_PROTOCOL_ERROR; + goto fail; + } + + tldap_debug(ld, TLDAP_DEBUG_TRACE, "tldap_msg_received: got msg %d " + "type %d\n", id, (int)type); + + if (id == 0) { + tldap_debug( + ld, + TLDAP_DEBUG_WARNING, + "tldap_msg_received: got msgid 0 of " + "type %"PRIu8", disconnecting\n", + type); + tldap_context_disconnect(ld, TLDAP_SERVER_DOWN); + return; + } + + num_pending = talloc_array_length(ld->pending); + + for (i=0; i<num_pending; i++) { + if (id == tldap_msg_msgid(ld->pending[i])) { + break; + } + } + if (i == num_pending) { + /* Dump unexpected reply */ + tldap_debug(ld, TLDAP_DEBUG_WARNING, "tldap_msg_received: " + "No request pending for msg %d\n", id); + TALLOC_FREE(data); + TALLOC_FREE(inbuf); + goto done; + } + + req = ld->pending[i]; + state = tevent_req_data(req, struct tldap_msg_state); + + state->inbuf = talloc_move(state, &inbuf); + state->data = talloc_move(state, &data); + + tldap_msg_unset_pending(req); + num_pending = talloc_array_length(ld->pending); + + tevent_req_defer_callback(req, state->ev); + tevent_req_done(req); + + done: + if (num_pending == 0) { + return; + } + + state = tevent_req_data(ld->pending[0], struct tldap_msg_state); + ld->read_req = read_ldap_send(ld->pending, state->ev, ld->conn); + if (ld->read_req == NULL) { + status = TLDAP_NO_MEMORY; + goto fail; + } + tevent_req_set_callback(ld->read_req, tldap_msg_received, ld); + return; + + fail: + tldap_context_disconnect(ld, status); +} + +static TLDAPRC tldap_msg_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct tldap_message **pmsg) +{ + struct tldap_msg_state *state = tevent_req_data( + req, struct tldap_msg_state); + struct tldap_message *msg; + TLDAPRC err; + uint8_t msgtype; + + if (tevent_req_is_ldap_error(req, &err)) { + return err; + } + + if (!asn1_peek_uint8(state->data, &msgtype)) { + return TLDAP_PROTOCOL_ERROR; + } + + if (pmsg == NULL) { + return TLDAP_SUCCESS; + } + + msg = talloc_zero(mem_ctx, struct tldap_message); + if (msg == NULL) { + return TLDAP_NO_MEMORY; + } + msg->id = state->id; + + msg->inbuf = talloc_move(msg, &state->inbuf); + msg->data = talloc_move(msg, &state->data); + msg->type = msgtype; + + *pmsg = msg; + return TLDAP_SUCCESS; +} + +struct tldap_req_state { + int id; + struct asn1_data *out; + struct tldap_message *result; +}; + +static struct tevent_req *tldap_req_create(TALLOC_CTX *mem_ctx, + struct tldap_context *ld, + struct tldap_req_state **pstate) +{ + struct tevent_req *req; + struct tldap_req_state *state; + + req = tevent_req_create(mem_ctx, &state, struct tldap_req_state); + if (req == NULL) { + return NULL; + } + state->out = asn1_init(state, ASN1_MAX_TREE_DEPTH); + if (state->out == NULL) { + goto err; + } + state->id = tldap_next_msgid(ld); + + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto err; + if (!asn1_write_Integer(state->out, state->id)) goto err; + + *pstate = state; + return req; + + err: + + TALLOC_FREE(req); + return NULL; +} + +static void tldap_save_msg(struct tldap_context *ld, struct tevent_req *req) +{ + struct tldap_req_state *state = tevent_req_data( + req, struct tldap_req_state); + + TALLOC_FREE(ld->last_msg); + ld->last_msg = talloc_move(ld, &state->result); +} + +static char *blob2string_talloc(TALLOC_CTX *mem_ctx, DATA_BLOB blob) +{ + char *result = talloc_array(mem_ctx, char, blob.length+1); + + if (result == NULL) { + return NULL; + } + + memcpy(result, blob.data, blob.length); + result[blob.length] = '\0'; + return result; +} + +static bool asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx, + struct asn1_data *data, + char **presult) +{ + DATA_BLOB string; + char *result; + if (!asn1_read_OctetString(data, mem_ctx, &string)) + return false; + + result = blob2string_talloc(mem_ctx, string); + + data_blob_free(&string); + + if (result == NULL) { + return false; + } + *presult = result; + return true; +} + +static bool tldap_decode_controls(struct tldap_req_state *state); + +static bool tldap_decode_response(struct tldap_req_state *state) +{ + struct asn1_data *data = state->result->data; + struct tldap_message *msg = state->result; + int rc; + bool ok = true; + + ok &= asn1_read_enumerated(data, &rc); + if (ok) { + msg->lderr = TLDAP_RC(rc); + } + + ok &= asn1_read_OctetString_talloc(msg, data, &msg->res_matcheddn); + ok &= asn1_read_OctetString_talloc(msg, data, + &msg->res_diagnosticmessage); + if (!ok) return ok; + if (asn1_peek_tag(data, ASN1_CONTEXT(3))) { + ok &= asn1_start_tag(data, ASN1_CONTEXT(3)); + ok &= asn1_read_OctetString_talloc(msg, data, + &msg->res_referral); + ok &= asn1_end_tag(data); + } else { + msg->res_referral = NULL; + } + + return ok; +} + +static void tldap_sasl_bind_done(struct tevent_req *subreq); + +struct tevent_req *tldap_sasl_bind_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *dn, + const char *mechanism, + DATA_BLOB *creds, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls) +{ + struct tevent_req *req, *subreq; + struct tldap_req_state *state; + + req = tldap_req_create(mem_ctx, ld, &state); + if (req == NULL) { + return NULL; + } + + if (dn == NULL) { + dn = ""; + } + + if (!asn1_push_tag(state->out, TLDAP_REQ_BIND)) goto err; + if (!asn1_write_Integer(state->out, ld->ld_version)) goto err; + if (!asn1_write_OctetString(state->out, dn, strlen(dn))) goto err; + + if (mechanism == NULL) { + if (!asn1_push_tag(state->out, ASN1_CONTEXT_SIMPLE(0))) goto err; + if (!asn1_write(state->out, creds->data, creds->length)) goto err; + if (!asn1_pop_tag(state->out)) goto err; + } else { + if (!asn1_push_tag(state->out, ASN1_CONTEXT(3))) goto err; + if (!asn1_write_OctetString(state->out, mechanism, + strlen(mechanism))) goto err; + if ((creds != NULL) && (creds->data != NULL)) { + if (!asn1_write_OctetString(state->out, creds->data, + creds->length)) goto err; + } + if (!asn1_pop_tag(state->out)) goto err; + } + + if (!asn1_pop_tag(state->out)) goto err; + + subreq = tldap_msg_send(state, ev, ld, state->id, state->out, + sctrls, num_sctrls); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_sasl_bind_done, req); + return req; + + err: + + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); +} + +static void tldap_sasl_bind_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_req_state *state = tevent_req_data( + req, struct tldap_req_state); + TLDAPRC rc; + bool ok; + + rc = tldap_msg_recv(subreq, state, &state->result); + TALLOC_FREE(subreq); + if (tevent_req_ldap_error(req, rc)) { + return; + } + if (state->result->type != TLDAP_RES_BIND) { + tevent_req_ldap_error(req, TLDAP_PROTOCOL_ERROR); + return; + } + + ok = asn1_start_tag(state->result->data, TLDAP_RES_BIND); + ok &= tldap_decode_response(state); + + if (asn1_peek_tag(state->result->data, ASN1_CONTEXT_SIMPLE(7))) { + int len; + + ok &= asn1_start_tag(state->result->data, + ASN1_CONTEXT_SIMPLE(7)); + if (!ok) { + goto decode_error; + } + + len = asn1_tag_remaining(state->result->data); + if (len == -1) { + goto decode_error; + } + + state->result->res_serverSaslCreds = + data_blob_talloc(state->result, NULL, len); + if (state->result->res_serverSaslCreds.data == NULL) { + goto decode_error; + } + + ok = asn1_read(state->result->data, + state->result->res_serverSaslCreds.data, + state->result->res_serverSaslCreds.length); + + ok &= asn1_end_tag(state->result->data); + } + + ok &= asn1_end_tag(state->result->data); + + if (!ok) { + goto decode_error; + } + + if (!TLDAP_RC_IS_SUCCESS(state->result->lderr) && + !TLDAP_RC_EQUAL(state->result->lderr, + TLDAP_SASL_BIND_IN_PROGRESS)) { + tevent_req_ldap_error(req, state->result->lderr); + return; + } + tevent_req_done(req); + return; + +decode_error: + tevent_req_ldap_error(req, TLDAP_DECODING_ERROR); + return; +} + +TLDAPRC tldap_sasl_bind_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + DATA_BLOB *serverSaslCreds) +{ + struct tldap_req_state *state = tevent_req_data( + req, struct tldap_req_state); + TLDAPRC rc; + + if (tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + + if (serverSaslCreds != NULL) { + serverSaslCreds->data = talloc_move( + mem_ctx, &state->result->res_serverSaslCreds.data); + serverSaslCreds->length = + state->result->res_serverSaslCreds.length; + } + + return state->result->lderr; +} + +TLDAPRC tldap_sasl_bind(struct tldap_context *ld, + const char *dn, + const char *mechanism, + DATA_BLOB *creds, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls, + TALLOC_CTX *mem_ctx, + DATA_BLOB *serverSaslCreds) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_sasl_bind_send(frame, ev, ld, dn, mechanism, creds, + sctrls, num_sctrls, cctrls, num_cctrls); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_sasl_bind_recv(req, mem_ctx, serverSaslCreds); + tldap_save_msg(ld, req); + fail: + TALLOC_FREE(frame); + return rc; +} + +struct tevent_req *tldap_simple_bind_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *dn, + const char *passwd) +{ + DATA_BLOB cred; + + if (passwd != NULL) { + cred.data = discard_const_p(uint8_t, passwd); + cred.length = strlen(passwd); + } else { + cred.data = discard_const_p(uint8_t, ""); + cred.length = 0; + } + return tldap_sasl_bind_send(mem_ctx, ev, ld, dn, NULL, &cred, NULL, 0, + NULL, 0); +} + +TLDAPRC tldap_simple_bind_recv(struct tevent_req *req) +{ + return tldap_sasl_bind_recv(req, NULL, NULL); +} + +TLDAPRC tldap_simple_bind(struct tldap_context *ld, const char *dn, + const char *passwd) +{ + DATA_BLOB cred; + + if (passwd != NULL) { + cred.data = discard_const_p(uint8_t, passwd); + cred.length = strlen(passwd); + } else { + cred.data = discard_const_p(uint8_t, ""); + cred.length = 0; + } + return tldap_sasl_bind(ld, dn, NULL, &cred, NULL, 0, NULL, 0, + NULL, NULL); +} + +/*****************************************************************************/ + +/* can't use isalpha() as only a strict set is valid for LDAP */ + +static bool tldap_is_alpha(char c) +{ + return (((c >= 'a') && (c <= 'z')) || \ + ((c >= 'A') && (c <= 'Z'))); +} + +static bool tldap_is_adh(char c) +{ + return tldap_is_alpha(c) || isdigit(c) || (c == '-'); +} + +#define TLDAP_FILTER_AND ASN1_CONTEXT(0) +#define TLDAP_FILTER_OR ASN1_CONTEXT(1) +#define TLDAP_FILTER_NOT ASN1_CONTEXT(2) +#define TLDAP_FILTER_EQ ASN1_CONTEXT(3) +#define TLDAP_FILTER_SUB ASN1_CONTEXT(4) +#define TLDAP_FILTER_LE ASN1_CONTEXT(5) +#define TLDAP_FILTER_GE ASN1_CONTEXT(6) +#define TLDAP_FILTER_PRES ASN1_CONTEXT_SIMPLE(7) +#define TLDAP_FILTER_APX ASN1_CONTEXT(8) +#define TLDAP_FILTER_EXT ASN1_CONTEXT(9) + +#define TLDAP_SUB_INI ASN1_CONTEXT_SIMPLE(0) +#define TLDAP_SUB_ANY ASN1_CONTEXT_SIMPLE(1) +#define TLDAP_SUB_FIN ASN1_CONTEXT_SIMPLE(2) + + +/* oid's should be numerical only in theory, + * but apparently some broken servers may have alphanum aliases instead. + * Do like openldap libraries and allow alphanum aliases for oids, but + * do not allow Tagging options in that case. + */ +static bool tldap_is_attrdesc(const char *s, int len, bool no_tagopts) +{ + bool is_oid = false; + bool dot = false; + int i; + + /* first char has stricter rules */ + if (isdigit(*s)) { + is_oid = true; + } else if (!tldap_is_alpha(*s)) { + /* bad first char */ + return false; + } + + for (i = 1; i < len; i++) { + + if (is_oid) { + if (isdigit(s[i])) { + dot = false; + continue; + } + if (s[i] == '.') { + if (dot) { + /* malformed */ + return false; + } + dot = true; + continue; + } + } else { + if (tldap_is_adh(s[i])) { + continue; + } + } + + if (s[i] == ';') { + if (no_tagopts) { + /* no tagging options */ + return false; + } + if (dot) { + /* malformed */ + return false; + } + if ((i + 1) == len) { + /* malformed */ + return false; + } + + is_oid = false; + continue; + } + } + + if (dot) { + /* malformed */ + return false; + } + + return true; +} + +/* this function copies the value until the closing parenthesis is found. */ +static char *tldap_get_val(TALLOC_CTX *memctx, + const char *value, const char **_s) +{ + const char *s = value; + + /* find terminator */ + while (*s) { + s = strchr(s, ')'); + if (s && (*(s - 1) == '\\')) { + s++; + continue; + } + break; + } + if (!s || !(*s == ')')) { + /* malformed filter */ + return NULL; + } + + *_s = s; + + return talloc_strndup(memctx, value, s - value); +} + +static int tldap_hex2char(const char *x) +{ + if (isxdigit(x[0]) && isxdigit(x[1])) { + const char h1 = x[0], h2 = x[1]; + int c = 0; + + if (h1 >= 'a') c = h1 - (int)'a' + 10; + else if (h1 >= 'A') c = h1 - (int)'A' + 10; + else if (h1 >= '0') c = h1 - (int)'0'; + c = c << 4; + if (h2 >= 'a') c += h2 - (int)'a' + 10; + else if (h2 >= 'A') c += h2 - (int)'A' + 10; + else if (h2 >= '0') c += h2 - (int)'0'; + + return c; + } + + return -1; +} + +static bool tldap_find_first_star(const char *val, const char **star) +{ + const char *s; + + for (s = val; *s; s++) { + switch (*s) { + case '\\': + if (isxdigit(s[1]) && isxdigit(s[2])) { + s += 2; + break; + } + /* not hex based escape, check older syntax */ + switch (s[1]) { + case '(': + case ')': + case '*': + case '\\': + s++; + break; + default: + /* invalid escape sequence */ + return false; + } + break; + case ')': + /* end of val, nothing found */ + *star = s; + return true; + + case '*': + *star = s; + return true; + } + } + + /* string ended without closing parenthesis, filter is malformed */ + return false; +} + +static bool tldap_unescape_inplace(char *value, size_t *val_len) +{ + int c; + size_t i, p; + + for (i = 0,p = 0; i < *val_len; i++) { + + switch (value[i]) { + case '(': + case ')': + case '*': + /* these must be escaped */ + return false; + + case '\\': + if (!value[i + 1]) { + /* invalid EOL */ + return false; + } + i++; + + /* LDAPv3 escaped */ + c = tldap_hex2char(&value[i]); + if (c >= 0 && c < 256) { + value[p] = c; + i++; + p++; + break; + } + + /* LDAPv2 escaped */ + switch (value[i]) { + case '(': + case ')': + case '*': + case '\\': + value[p] = value[i]; + p++; + + break; + default: + /* invalid */ + return false; + } + break; + + default: + value[p] = value[i]; + p++; + } + } + value[p] = '\0'; + *val_len = p; + return true; +} + +static bool tldap_push_filter_basic(struct tldap_context *ld, + struct asn1_data *data, + const char **_s); +static bool tldap_push_filter_substring(struct tldap_context *ld, + struct asn1_data *data, + const char *val, + const char **_s); +static bool tldap_push_filter_int(struct tldap_context *ld, + struct asn1_data *data, + const char **_s) +{ + const char *s = *_s; + bool ret; + + if (*s != '(') { + tldap_debug(ld, TLDAP_DEBUG_ERROR, + "Incomplete or malformed filter\n"); + return false; + } + s++; + + /* we are right after a parenthesis, + * find out what op we have at hand */ + switch (*s) { + case '&': + tldap_debug(ld, TLDAP_DEBUG_TRACE, "Filter op: AND\n"); + if (!asn1_push_tag(data, TLDAP_FILTER_AND)) return false; + s++; + break; + + case '|': + tldap_debug(ld, TLDAP_DEBUG_TRACE, "Filter op: OR\n"); + if (!asn1_push_tag(data, TLDAP_FILTER_OR)) return false; + s++; + break; + + case '!': + tldap_debug(ld, TLDAP_DEBUG_TRACE, "Filter op: NOT\n"); + if (!asn1_push_tag(data, TLDAP_FILTER_NOT)) return false; + s++; + ret = tldap_push_filter_int(ld, data, &s); + if (!ret) { + return false; + } + if (!asn1_pop_tag(data)) return false; + goto done; + + case '(': + case ')': + tldap_debug(ld, TLDAP_DEBUG_ERROR, + "Invalid parenthesis '%c'\n", *s); + return false; + + case '\0': + tldap_debug(ld, TLDAP_DEBUG_ERROR, + "Invalid filter termination\n"); + return false; + + default: + ret = tldap_push_filter_basic(ld, data, &s); + if (!ret) { + return false; + } + goto done; + } + + /* only and/or filters get here. + * go through the list of filters */ + + if (*s == ')') { + /* RFC 4526: empty and/or */ + if (!asn1_pop_tag(data)) return false; + goto done; + } + + while (*s) { + ret = tldap_push_filter_int(ld, data, &s); + if (!ret) { + return false; + } + + if (*s == ')') { + /* end of list, return */ + if (!asn1_pop_tag(data)) return false; + break; + } + } + +done: + if (*s != ')') { + tldap_debug(ld, TLDAP_DEBUG_ERROR, + "Incomplete or malformed filter\n"); + return false; + } + s++; + + if (asn1_has_error(data)) { + return false; + } + + *_s = s; + return true; +} + + +static bool tldap_push_filter_basic(struct tldap_context *ld, + struct asn1_data *data, + const char **_s) +{ + TALLOC_CTX *tmpctx = talloc_tos(); + const char *s = *_s; + const char *e; + const char *eq; + const char *val; + const char *type; + const char *dn; + const char *rule; + const char *star; + size_t type_len = 0; + char *uval; + size_t uval_len; + bool write_octect = true; + bool ret; + + eq = strchr(s, '='); + if (!eq) { + tldap_debug(ld, TLDAP_DEBUG_ERROR, + "Invalid filter, missing equal sign\n"); + return false; + } + + val = eq + 1; + e = eq - 1; + + switch (*e) { + case '<': + if (!asn1_push_tag(data, TLDAP_FILTER_LE)) return false; + break; + + case '>': + if (!asn1_push_tag(data, TLDAP_FILTER_GE)) return false; + break; + + case '~': + if (!asn1_push_tag(data, TLDAP_FILTER_APX)) return false; + break; + + case ':': + if (!asn1_push_tag(data, TLDAP_FILTER_EXT)) return false; + write_octect = false; + + type = NULL; + dn = NULL; + rule = NULL; + + if (*s == ':') { /* [:dn]:rule:= value */ + if (s == e) { + /* malformed filter */ + return false; + } + dn = s; + } else { /* type[:dn][:rule]:= value */ + type = s; + dn = strchr(s, ':'); + type_len = dn - type; + if (dn == e) { /* type:= value */ + dn = NULL; + } + } + if (dn) { + dn++; + + rule = strchr(dn, ':'); + if (rule == NULL) { + return false; + } + if ((rule == dn + 1) || rule + 1 == e) { + /* malformed filter, contains "::" */ + return false; + } + + if (strncasecmp_m(dn, "dn:", 3) != 0) { + if (rule == e) { + rule = dn; + dn = NULL; + } else { + /* malformed filter. With two + * optionals, the first must be "dn" + */ + return false; + } + } else { + if (rule == e) { + rule = NULL; + } else { + rule++; + } + } + } + + if (!type && !dn && !rule) { + /* malformed filter, there must be at least one */ + return false; + } + + /* + MatchingRuleAssertion ::= SEQUENCE { + matchingRule [1] MatchingRuleID OPTIONAL, + type [2] AttributeDescription OPTIONAL, + matchValue [3] AssertionValue, + dnAttributes [4] BOOLEAN DEFAULT FALSE + } + */ + + /* check and add rule */ + if (rule) { + ret = tldap_is_attrdesc(rule, e - rule, true); + if (!ret) { + return false; + } + if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1))) return false; + if (!asn1_write(data, rule, e - rule)) return false; + if (!asn1_pop_tag(data)) return false; + } + + /* check and add type */ + if (type) { + ret = tldap_is_attrdesc(type, type_len, false); + if (!ret) { + return false; + } + if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(2))) return false; + if (!asn1_write(data, type, type_len)) return false; + if (!asn1_pop_tag(data)) return false; + } + + uval = tldap_get_val(tmpctx, val, _s); + if (!uval) { + return false; + } + uval_len = *_s - val; + ret = tldap_unescape_inplace(uval, &uval_len); + if (!ret) { + return false; + } + + if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(3))) return false; + if (!asn1_write(data, uval, uval_len)) return false; + if (!asn1_pop_tag(data)) return false; + + if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(4))) return false; + if (!asn1_write_uint8(data, dn?1:0)) return false; + if (!asn1_pop_tag(data)) return false; + break; + + default: + e = eq; + + ret = tldap_is_attrdesc(s, e - s, false); + if (!ret) { + return false; + } + + if (strncmp(val, "*)", 2) == 0) { + /* presence */ + if (!asn1_push_tag(data, TLDAP_FILTER_PRES)) return false; + if (!asn1_write(data, s, e - s)) return false; + *_s = val + 1; + write_octect = false; + break; + } + + ret = tldap_find_first_star(val, &star); + if (!ret) { + return false; + } + if (*star == '*') { + /* substring */ + if (!asn1_push_tag(data, TLDAP_FILTER_SUB)) return false; + if (!asn1_write_OctetString(data, s, e - s)) return false; + ret = tldap_push_filter_substring(ld, data, val, &s); + if (!ret) { + return false; + } + *_s = s; + write_octect = false; + break; + } + + /* if nothing else, then it is just equality */ + if (!asn1_push_tag(data, TLDAP_FILTER_EQ)) return false; + write_octect = true; + break; + } + + if (write_octect) { + uval = tldap_get_val(tmpctx, val, _s); + if (!uval) { + return false; + } + uval_len = *_s - val; + ret = tldap_unescape_inplace(uval, &uval_len); + if (!ret) { + return false; + } + + if (!asn1_write_OctetString(data, s, e - s)) return false; + if (!asn1_write_OctetString(data, uval, uval_len)) return false; + } + + if (asn1_has_error(data)) { + return false; + } + return asn1_pop_tag(data); +} + +static bool tldap_push_filter_substring(struct tldap_context *ld, + struct asn1_data *data, + const char *val, + const char **_s) +{ + TALLOC_CTX *tmpctx = talloc_tos(); + bool initial = true; + const char *star; + char *chunk; + size_t chunk_len; + bool ret; + + /* + SubstringFilter ::= SEQUENCE { + type AttributeDescription, + -- at least one must be present + substrings SEQUENCE OF CHOICE { + initial [0] LDAPString, + any [1] LDAPString, + final [2] LDAPString } } + */ + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) return false; + + do { + ret = tldap_find_first_star(val, &star); + if (!ret) { + return false; + } + chunk_len = star - val; + + switch (*star) { + case '*': + if (!initial && chunk_len == 0) { + /* found '**', which is illegal */ + return false; + } + break; + case ')': + if (initial) { + /* no stars ?? */ + return false; + } + /* we are done */ + break; + default: + /* ?? */ + return false; + } + + if (initial && chunk_len == 0) { + val = star + 1; + initial = false; + continue; + } + + chunk = talloc_strndup(tmpctx, val, chunk_len); + if (!chunk) { + return false; + } + ret = tldap_unescape_inplace(chunk, &chunk_len); + if (!ret) { + return false; + } + switch (*star) { + case '*': + if (initial) { + if (!asn1_push_tag(data, TLDAP_SUB_INI)) return false; + initial = false; + } else { + if (!asn1_push_tag(data, TLDAP_SUB_ANY)) return false; + } + break; + case ')': + if (!asn1_push_tag(data, TLDAP_SUB_FIN)) return false; + break; + default: + /* ?? */ + return false; + } + if (!asn1_write(data, chunk, chunk_len)) return false; + if (!asn1_pop_tag(data)) return false; + + val = star + 1; + + } while (*star == '*'); + + *_s = star; + + /* end of sequence */ + return asn1_pop_tag(data); +} + +/* NOTE: although openldap libraries allow for spaces in some places, mostly + * around parentheses, we do not allow any spaces (except in values of + * course) as I couldn't find any place in RFC 4512 or RFC 4515 where + * leading or trailing spaces were allowed. + */ +static bool tldap_push_filter(struct tldap_context *ld, + struct asn1_data *data, + const char *filter) +{ + const char *s = filter; + bool ret; + + ret = tldap_push_filter_int(ld, data, &s); + if (ret && *s) { + tldap_debug(ld, TLDAP_DEBUG_ERROR, + "Incomplete or malformed filter\n"); + return false; + } + return ret; +} + +/*****************************************************************************/ + +static void tldap_search_done(struct tevent_req *subreq); + +struct tevent_req *tldap_search_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *base, int scope, + const char *filter, + const char **attrs, + int num_attrs, + int attrsonly, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls, + int timelimit, + int sizelimit, + int deref) +{ + struct tevent_req *req, *subreq; + struct tldap_req_state *state; + int i; + + req = tldap_req_create(mem_ctx, ld, &state); + if (req == NULL) { + return NULL; + } + + if (!asn1_push_tag(state->out, TLDAP_REQ_SEARCH)) goto encoding_error; + if (!asn1_write_OctetString(state->out, base, strlen(base))) goto encoding_error; + if (!asn1_write_enumerated(state->out, scope)) goto encoding_error; + if (!asn1_write_enumerated(state->out, deref)) goto encoding_error; + if (!asn1_write_Integer(state->out, sizelimit)) goto encoding_error; + if (!asn1_write_Integer(state->out, timelimit)) goto encoding_error; + if (!asn1_write_BOOLEAN(state->out, attrsonly)) goto encoding_error; + + if (!tldap_push_filter(ld, state->out, filter)) { + goto encoding_error; + } + + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto encoding_error; + for (i=0; i<num_attrs; i++) { + if (!asn1_write_OctetString(state->out, attrs[i], strlen(attrs[i]))) goto encoding_error; + } + if (!asn1_pop_tag(state->out)) goto encoding_error; + if (!asn1_pop_tag(state->out)) goto encoding_error; + + subreq = tldap_msg_send(state, ev, ld, state->id, state->out, + sctrls, num_sctrls); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_search_done, req); + return req; + + encoding_error: + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); +} + +static void tldap_search_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_req_state *state = tevent_req_data( + req, struct tldap_req_state); + TLDAPRC rc; + + rc = tldap_msg_recv(subreq, state, &state->result); + if (tevent_req_ldap_error(req, rc)) { + return; + } + switch (state->result->type) { + case TLDAP_RES_SEARCH_ENTRY: + case TLDAP_RES_SEARCH_REFERENCE: + if (!tldap_msg_set_pending(subreq)) { + tevent_req_oom(req); + return; + } + tevent_req_notify_callback(req); + break; + case TLDAP_RES_SEARCH_RESULT: + TALLOC_FREE(subreq); + if (!asn1_start_tag(state->result->data, + state->result->type) || + !tldap_decode_response(state) || + !asn1_end_tag(state->result->data) || + !tldap_decode_controls(state)) { + tevent_req_ldap_error(req, TLDAP_DECODING_ERROR); + return; + } + tevent_req_done(req); + break; + default: + tevent_req_ldap_error(req, TLDAP_PROTOCOL_ERROR); + return; + } +} + +TLDAPRC tldap_search_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct tldap_message **pmsg) +{ + struct tldap_req_state *state = tevent_req_data( + req, struct tldap_req_state); + TLDAPRC rc; + + if (!tevent_req_is_in_progress(req) + && tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + + if (tevent_req_is_in_progress(req)) { + switch (state->result->type) { + case TLDAP_RES_SEARCH_ENTRY: + case TLDAP_RES_SEARCH_REFERENCE: + break; + default: + return TLDAP_OPERATIONS_ERROR; + } + } + + *pmsg = talloc_move(mem_ctx, &state->result); + return TLDAP_SUCCESS; +} + +struct tldap_search_all_state { + struct tldap_message **msgs; + struct tldap_message *result; +}; + +static void tldap_search_all_done(struct tevent_req *subreq); + +struct tevent_req *tldap_search_all_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct tldap_context *ld, const char *base, int scope, + const char *filter, const char **attrs, int num_attrs, int attrsonly, + struct tldap_control *sctrls, int num_sctrls, + struct tldap_control *cctrls, int num_cctrls, + int timelimit, int sizelimit, int deref) +{ + struct tevent_req *req, *subreq; + struct tldap_search_all_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct tldap_search_all_state); + if (req == NULL) { + return NULL; + } + + subreq = tldap_search_send(state, ev, ld, base, scope, filter, + attrs, num_attrs, attrsonly, + sctrls, num_sctrls, cctrls, num_cctrls, + timelimit, sizelimit, deref); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_search_all_done, req); + return req; +} + +static void tldap_search_all_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_search_all_state *state = tevent_req_data( + req, struct tldap_search_all_state); + struct tldap_message *msg, **tmp; + size_t num_msgs; + TLDAPRC rc; + int msgtype; + + rc = tldap_search_recv(subreq, state, &msg); + /* No TALLOC_FREE(subreq), this is multi-step */ + if (tevent_req_ldap_error(req, rc)) { + return; + } + + msgtype = tldap_msg_type(msg); + if (msgtype == TLDAP_RES_SEARCH_RESULT) { + state->result = msg; + tevent_req_done(req); + return; + } + + num_msgs = talloc_array_length(state->msgs); + + tmp = talloc_realloc(state, state->msgs, struct tldap_message *, + num_msgs + 1); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->msgs = tmp; + state->msgs[num_msgs] = talloc_move(state->msgs, &msg); +} + +TLDAPRC tldap_search_all_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct tldap_message ***msgs, + struct tldap_message **result) +{ + struct tldap_search_all_state *state = tevent_req_data( + req, struct tldap_search_all_state); + TLDAPRC rc; + + if (tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + + if (msgs != NULL) { + *msgs = talloc_move(mem_ctx, &state->msgs); + } + if (result != NULL) { + *result = talloc_move(mem_ctx, &state->result); + } + + return TLDAP_SUCCESS; +} + +TLDAPRC tldap_search(struct tldap_context *ld, + const char *base, int scope, const char *filter, + const char **attrs, int num_attrs, int attrsonly, + struct tldap_control *sctrls, int num_sctrls, + struct tldap_control *cctrls, int num_cctrls, + int timelimit, int sizelimit, int deref, + TALLOC_CTX *mem_ctx, struct tldap_message ***pmsgs) +{ + TALLOC_CTX *frame; + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + struct tldap_message **msgs; + struct tldap_message *result; + + if (tldap_pending_reqs(ld)) { + return TLDAP_BUSY; + } + + frame = talloc_stackframe(); + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_search_all_send(frame, ev, ld, base, scope, filter, + attrs, num_attrs, attrsonly, + sctrls, num_sctrls, cctrls, num_cctrls, + timelimit, sizelimit, deref); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_search_all_recv(req, frame, &msgs, &result); + TALLOC_FREE(req); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + goto fail; + } + + TALLOC_FREE(ld->last_msg); + ld->last_msg = talloc_move(ld, &result); + + if (pmsgs != NULL) { + *pmsgs = talloc_move(mem_ctx, &msgs); + } +fail: + TALLOC_FREE(frame); + return rc; +} + +static bool tldap_parse_search_entry(struct tldap_message *msg) +{ + int num_attribs = 0; + + if (msg->type != TLDAP_RES_SEARCH_ENTRY) { + return false; + } + if (!asn1_start_tag(msg->data, TLDAP_RES_SEARCH_ENTRY)) { + return false; + } + + /* dn */ + + if (!asn1_read_OctetString_talloc(msg, msg->data, &msg->dn)) return false; + + if (msg->dn == NULL) { + return false; + } + + /* + * Attributes: We overallocate msg->attribs by one, so that while + * looping over the attributes we can directly parse into the last + * array element. Same for the values in the inner loop. + */ + + msg->attribs = talloc_array(msg, struct tldap_attribute, 1); + if (msg->attribs == NULL) { + return false; + } + + if (!asn1_start_tag(msg->data, ASN1_SEQUENCE(0))) return false; + while (asn1_peek_tag(msg->data, ASN1_SEQUENCE(0))) { + struct tldap_attribute *attrib; + int num_values = 0; + + attrib = &msg->attribs[num_attribs]; + attrib->values = talloc_array(msg->attribs, DATA_BLOB, 1); + if (attrib->values == NULL) { + return false; + } + if (!asn1_start_tag(msg->data, ASN1_SEQUENCE(0))) return false; + if (!asn1_read_OctetString_talloc(msg->attribs, msg->data, + &attrib->name)) return false; + if (!asn1_start_tag(msg->data, ASN1_SET)) return false; + + while (asn1_peek_tag(msg->data, ASN1_OCTET_STRING)) { + if (!asn1_read_OctetString(msg->data, msg, + &attrib->values[num_values])) return false; + + attrib->values = talloc_realloc( + msg->attribs, attrib->values, DATA_BLOB, + num_values + 2); + if (attrib->values == NULL) { + return false; + } + num_values += 1; + } + attrib->values = talloc_realloc(msg->attribs, attrib->values, + DATA_BLOB, num_values); + attrib->num_values = num_values; + + if (!asn1_end_tag(msg->data)) return false; /* ASN1_SET */ + if (!asn1_end_tag(msg->data)) return false; /* ASN1_SEQUENCE(0) */ + msg->attribs = talloc_realloc( + msg, msg->attribs, struct tldap_attribute, + num_attribs + 2); + if (msg->attribs == NULL) { + return false; + } + num_attribs += 1; + } + msg->attribs = talloc_realloc( + msg, msg->attribs, struct tldap_attribute, num_attribs); + return asn1_end_tag(msg->data); +} + +bool tldap_entry_dn(struct tldap_message *msg, char **dn) +{ + if ((msg->dn == NULL) && (!tldap_parse_search_entry(msg))) { + return false; + } + *dn = msg->dn; + return true; +} + +bool tldap_entry_attributes(struct tldap_message *msg, + struct tldap_attribute **attributes, + int *num_attributes) +{ + if ((msg->dn == NULL) && (!tldap_parse_search_entry(msg))) { + return false; + } + *attributes = msg->attribs; + *num_attributes = talloc_array_length(msg->attribs); + return true; +} + +static bool tldap_decode_controls(struct tldap_req_state *state) +{ + struct tldap_message *msg = state->result; + struct asn1_data *data = msg->data; + struct tldap_control *sctrls = NULL; + int num_controls = 0; + bool ret = false; + + msg->res_sctrls = NULL; + + if (!asn1_peek_tag(data, ASN1_CONTEXT(0))) { + return true; + } + + if (!asn1_start_tag(data, ASN1_CONTEXT(0))) goto out; + + while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) { + struct tldap_control *c; + char *oid = NULL; + + sctrls = talloc_realloc(msg, sctrls, struct tldap_control, + num_controls + 1); + if (sctrls == NULL) { + goto out; + } + c = &sctrls[num_controls]; + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto out; + if (!asn1_read_OctetString_talloc(msg, data, &oid)) goto out; + if (asn1_has_error(data) || (oid == NULL)) { + goto out; + } + c->oid = oid; + if (asn1_peek_tag(data, ASN1_BOOLEAN)) { + if (!asn1_read_BOOLEAN(data, &c->critical)) goto out; + } else { + c->critical = false; + } + c->value = data_blob_null; + if (asn1_peek_tag(data, ASN1_OCTET_STRING) && + !asn1_read_OctetString(data, msg, &c->value)) { + goto out; + } + if (!asn1_end_tag(data)) goto out; /* ASN1_SEQUENCE(0) */ + + num_controls += 1; + } + + if (!asn1_end_tag(data)) goto out; /* ASN1_CONTEXT(0) */ + + ret = true; + + out: + + if (ret) { + msg->res_sctrls = sctrls; + } else { + TALLOC_FREE(sctrls); + } + return ret; +} + +static void tldap_simple_done(struct tevent_req *subreq, int type) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_req_state *state = tevent_req_data( + req, struct tldap_req_state); + TLDAPRC rc; + + rc = tldap_msg_recv(subreq, state, &state->result); + TALLOC_FREE(subreq); + if (tevent_req_ldap_error(req, rc)) { + return; + } + if (state->result->type != type) { + tevent_req_ldap_error(req, TLDAP_PROTOCOL_ERROR); + return; + } + if (!asn1_start_tag(state->result->data, state->result->type) || + !tldap_decode_response(state) || + !asn1_end_tag(state->result->data) || + !tldap_decode_controls(state)) { + tevent_req_ldap_error(req, TLDAP_DECODING_ERROR); + return; + } + if (!TLDAP_RC_IS_SUCCESS(state->result->lderr)) { + tevent_req_ldap_error(req, state->result->lderr); + return; + } + tevent_req_done(req); +} + +static TLDAPRC tldap_simple_recv(struct tevent_req *req) +{ + TLDAPRC rc; + if (tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + return TLDAP_SUCCESS; +} + +static void tldap_add_done(struct tevent_req *subreq); + +struct tevent_req *tldap_add_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *dn, + struct tldap_mod *attributes, + int num_attributes, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls) +{ + struct tevent_req *req, *subreq; + struct tldap_req_state *state; + int i, j; + + req = tldap_req_create(mem_ctx, ld, &state); + if (req == NULL) { + return NULL; + } + + if (!asn1_push_tag(state->out, TLDAP_REQ_ADD)) goto err; + if (!asn1_write_OctetString(state->out, dn, strlen(dn))) goto err; + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto err; + + for (i=0; i<num_attributes; i++) { + struct tldap_mod *attrib = &attributes[i]; + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto err; + if (!asn1_write_OctetString(state->out, attrib->attribute, + strlen(attrib->attribute))) goto err; + if (!asn1_push_tag(state->out, ASN1_SET)) goto err; + for (j=0; j<attrib->num_values; j++) { + if (!asn1_write_OctetString(state->out, + attrib->values[j].data, + attrib->values[j].length)) goto err; + } + if (!asn1_pop_tag(state->out)) goto err; + if (!asn1_pop_tag(state->out)) goto err; + } + + if (!asn1_pop_tag(state->out)) goto err; + if (!asn1_pop_tag(state->out)) goto err; + + subreq = tldap_msg_send(state, ev, ld, state->id, state->out, + sctrls, num_sctrls); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_add_done, req); + return req; + + err: + + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); +} + +static void tldap_add_done(struct tevent_req *subreq) +{ + tldap_simple_done(subreq, TLDAP_RES_ADD); +} + +TLDAPRC tldap_add_recv(struct tevent_req *req) +{ + return tldap_simple_recv(req); +} + +TLDAPRC tldap_add(struct tldap_context *ld, const char *dn, + struct tldap_mod *attributes, int num_attributes, + struct tldap_control *sctrls, int num_sctrls, + struct tldap_control *cctrls, int num_cctrls) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_add_send(frame, ev, ld, dn, attributes, num_attributes, + sctrls, num_sctrls, cctrls, num_cctrls); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_add_recv(req); + tldap_save_msg(ld, req); + fail: + TALLOC_FREE(frame); + return rc; +} + +static void tldap_modify_done(struct tevent_req *subreq); + +struct tevent_req *tldap_modify_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *dn, + struct tldap_mod *mods, int num_mods, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls) +{ + struct tevent_req *req, *subreq; + struct tldap_req_state *state; + int i, j; + + req = tldap_req_create(mem_ctx, ld, &state); + if (req == NULL) { + return NULL; + } + + if (!asn1_push_tag(state->out, TLDAP_REQ_MODIFY)) goto err; + if (!asn1_write_OctetString(state->out, dn, strlen(dn))) goto err; + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto err; + + for (i=0; i<num_mods; i++) { + struct tldap_mod *mod = &mods[i]; + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto err; + if (!asn1_write_enumerated(state->out, mod->mod_op)) goto err; + if (!asn1_push_tag(state->out, ASN1_SEQUENCE(0))) goto err; + if (!asn1_write_OctetString(state->out, mod->attribute, + strlen(mod->attribute))) goto err; + if (!asn1_push_tag(state->out, ASN1_SET)) goto err; + for (j=0; j<mod->num_values; j++) { + if (!asn1_write_OctetString(state->out, + mod->values[j].data, + mod->values[j].length)) goto err; + } + if (!asn1_pop_tag(state->out)) goto err; + if (!asn1_pop_tag(state->out)) goto err; + if (!asn1_pop_tag(state->out)) goto err; + } + + if (!asn1_pop_tag(state->out)) goto err; + if (!asn1_pop_tag(state->out)) goto err; + + subreq = tldap_msg_send(state, ev, ld, state->id, state->out, + sctrls, num_sctrls); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_modify_done, req); + return req; + + err: + + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); +} + +static void tldap_modify_done(struct tevent_req *subreq) +{ + tldap_simple_done(subreq, TLDAP_RES_MODIFY); +} + +TLDAPRC tldap_modify_recv(struct tevent_req *req) +{ + return tldap_simple_recv(req); +} + +TLDAPRC tldap_modify(struct tldap_context *ld, const char *dn, + struct tldap_mod *mods, int num_mods, + struct tldap_control *sctrls, int num_sctrls, + struct tldap_control *cctrls, int num_cctrls) + { + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_modify_send(frame, ev, ld, dn, mods, num_mods, + sctrls, num_sctrls, cctrls, num_cctrls); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_modify_recv(req); + tldap_save_msg(ld, req); + fail: + TALLOC_FREE(frame); + return rc; +} + +static void tldap_delete_done(struct tevent_req *subreq); + +struct tevent_req *tldap_delete_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *dn, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls) +{ + struct tevent_req *req, *subreq; + struct tldap_req_state *state; + + req = tldap_req_create(mem_ctx, ld, &state); + if (req == NULL) { + return NULL; + } + + if (!asn1_push_tag(state->out, TLDAP_REQ_DELETE)) goto err; + if (!asn1_write(state->out, dn, strlen(dn))) goto err; + if (!asn1_pop_tag(state->out)) goto err; + + subreq = tldap_msg_send(state, ev, ld, state->id, state->out, + sctrls, num_sctrls); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_delete_done, req); + return req; + + err: + + tevent_req_ldap_error(req, TLDAP_ENCODING_ERROR); + return tevent_req_post(req, ev); +} + +static void tldap_delete_done(struct tevent_req *subreq) +{ + tldap_simple_done(subreq, TLDAP_RES_DELETE); +} + +TLDAPRC tldap_delete_recv(struct tevent_req *req) +{ + return tldap_simple_recv(req); +} + +TLDAPRC tldap_delete(struct tldap_context *ld, const char *dn, + struct tldap_control *sctrls, int num_sctrls, + struct tldap_control *cctrls, int num_cctrls) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_delete_send(frame, ev, ld, dn, sctrls, num_sctrls, + cctrls, num_cctrls); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_delete_recv(req); + tldap_save_msg(ld, req); + fail: + TALLOC_FREE(frame); + return rc; +} + +int tldap_msg_id(const struct tldap_message *msg) +{ + return msg->id; +} + +int tldap_msg_type(const struct tldap_message *msg) +{ + return msg->type; +} + +const char *tldap_msg_matcheddn(struct tldap_message *msg) +{ + if (msg == NULL) { + return NULL; + } + return msg->res_matcheddn; +} + +const char *tldap_msg_diagnosticmessage(struct tldap_message *msg) +{ + if (msg == NULL) { + return NULL; + } + return msg->res_diagnosticmessage; +} + +const char *tldap_msg_referral(struct tldap_message *msg) +{ + if (msg == NULL) { + return NULL; + } + return msg->res_referral; +} + +void tldap_msg_sctrls(struct tldap_message *msg, int *num_sctrls, + struct tldap_control **sctrls) +{ + if (msg == NULL) { + *sctrls = NULL; + *num_sctrls = 0; + return; + } + *sctrls = msg->res_sctrls; + *num_sctrls = talloc_array_length(msg->res_sctrls); +} + +struct tldap_message *tldap_ctx_lastmsg(struct tldap_context *ld) +{ + return ld->last_msg; +} + +static const struct { TLDAPRC rc; const char *string; } tldaprc_errmap[] = +{ + { TLDAP_SUCCESS, + "TLDAP_SUCCESS" }, + { TLDAP_OPERATIONS_ERROR, + "TLDAP_OPERATIONS_ERROR" }, + { TLDAP_PROTOCOL_ERROR, + "TLDAP_PROTOCOL_ERROR" }, + { TLDAP_TIMELIMIT_EXCEEDED, + "TLDAP_TIMELIMIT_EXCEEDED" }, + { TLDAP_SIZELIMIT_EXCEEDED, + "TLDAP_SIZELIMIT_EXCEEDED" }, + { TLDAP_COMPARE_FALSE, + "TLDAP_COMPARE_FALSE" }, + { TLDAP_COMPARE_TRUE, + "TLDAP_COMPARE_TRUE" }, + { TLDAP_STRONG_AUTH_NOT_SUPPORTED, + "TLDAP_STRONG_AUTH_NOT_SUPPORTED" }, + { TLDAP_STRONG_AUTH_REQUIRED, + "TLDAP_STRONG_AUTH_REQUIRED" }, + { TLDAP_REFERRAL, + "TLDAP_REFERRAL" }, + { TLDAP_ADMINLIMIT_EXCEEDED, + "TLDAP_ADMINLIMIT_EXCEEDED" }, + { TLDAP_UNAVAILABLE_CRITICAL_EXTENSION, + "TLDAP_UNAVAILABLE_CRITICAL_EXTENSION" }, + { TLDAP_CONFIDENTIALITY_REQUIRED, + "TLDAP_CONFIDENTIALITY_REQUIRED" }, + { TLDAP_SASL_BIND_IN_PROGRESS, + "TLDAP_SASL_BIND_IN_PROGRESS" }, + { TLDAP_NO_SUCH_ATTRIBUTE, + "TLDAP_NO_SUCH_ATTRIBUTE" }, + { TLDAP_UNDEFINED_TYPE, + "TLDAP_UNDEFINED_TYPE" }, + { TLDAP_INAPPROPRIATE_MATCHING, + "TLDAP_INAPPROPRIATE_MATCHING" }, + { TLDAP_CONSTRAINT_VIOLATION, + "TLDAP_CONSTRAINT_VIOLATION" }, + { TLDAP_TYPE_OR_VALUE_EXISTS, + "TLDAP_TYPE_OR_VALUE_EXISTS" }, + { TLDAP_INVALID_SYNTAX, + "TLDAP_INVALID_SYNTAX" }, + { TLDAP_NO_SUCH_OBJECT, + "TLDAP_NO_SUCH_OBJECT" }, + { TLDAP_ALIAS_PROBLEM, + "TLDAP_ALIAS_PROBLEM" }, + { TLDAP_INVALID_DN_SYNTAX, + "TLDAP_INVALID_DN_SYNTAX" }, + { TLDAP_IS_LEAF, + "TLDAP_IS_LEAF" }, + { TLDAP_ALIAS_DEREF_PROBLEM, + "TLDAP_ALIAS_DEREF_PROBLEM" }, + { TLDAP_INAPPROPRIATE_AUTH, + "TLDAP_INAPPROPRIATE_AUTH" }, + { TLDAP_INVALID_CREDENTIALS, + "TLDAP_INVALID_CREDENTIALS" }, + { TLDAP_INSUFFICIENT_ACCESS, + "TLDAP_INSUFFICIENT_ACCESS" }, + { TLDAP_BUSY, + "TLDAP_BUSY" }, + { TLDAP_UNAVAILABLE, + "TLDAP_UNAVAILABLE" }, + { TLDAP_UNWILLING_TO_PERFORM, + "TLDAP_UNWILLING_TO_PERFORM" }, + { TLDAP_LOOP_DETECT, + "TLDAP_LOOP_DETECT" }, + { TLDAP_NAMING_VIOLATION, + "TLDAP_NAMING_VIOLATION" }, + { TLDAP_OBJECT_CLASS_VIOLATION, + "TLDAP_OBJECT_CLASS_VIOLATION" }, + { TLDAP_NOT_ALLOWED_ON_NONLEAF, + "TLDAP_NOT_ALLOWED_ON_NONLEAF" }, + { TLDAP_NOT_ALLOWED_ON_RDN, + "TLDAP_NOT_ALLOWED_ON_RDN" }, + { TLDAP_ALREADY_EXISTS, + "TLDAP_ALREADY_EXISTS" }, + { TLDAP_NO_OBJECT_CLASS_MODS, + "TLDAP_NO_OBJECT_CLASS_MODS" }, + { TLDAP_RESULTS_TOO_LARGE, + "TLDAP_RESULTS_TOO_LARGE" }, + { TLDAP_AFFECTS_MULTIPLE_DSAS, + "TLDAP_AFFECTS_MULTIPLE_DSAS" }, + { TLDAP_OTHER, + "TLDAP_OTHER" }, + { TLDAP_SERVER_DOWN, + "TLDAP_SERVER_DOWN" }, + { TLDAP_LOCAL_ERROR, + "TLDAP_LOCAL_ERROR" }, + { TLDAP_ENCODING_ERROR, + "TLDAP_ENCODING_ERROR" }, + { TLDAP_DECODING_ERROR, + "TLDAP_DECODING_ERROR" }, + { TLDAP_TIMEOUT, + "TLDAP_TIMEOUT" }, + { TLDAP_AUTH_UNKNOWN, + "TLDAP_AUTH_UNKNOWN" }, + { TLDAP_FILTER_ERROR, + "TLDAP_FILTER_ERROR" }, + { TLDAP_USER_CANCELLED, + "TLDAP_USER_CANCELLED" }, + { TLDAP_PARAM_ERROR, + "TLDAP_PARAM_ERROR" }, + { TLDAP_NO_MEMORY, + "TLDAP_NO_MEMORY" }, + { TLDAP_CONNECT_ERROR, + "TLDAP_CONNECT_ERROR" }, + { TLDAP_NOT_SUPPORTED, + "TLDAP_NOT_SUPPORTED" }, + { TLDAP_CONTROL_NOT_FOUND, + "TLDAP_CONTROL_NOT_FOUND" }, + { TLDAP_NO_RESULTS_RETURNED, + "TLDAP_NO_RESULTS_RETURNED" }, + { TLDAP_MORE_RESULTS_TO_RETURN, + "TLDAP_MORE_RESULTS_TO_RETURN" }, + { TLDAP_CLIENT_LOOP, + "TLDAP_CLIENT_LOOP" }, + { TLDAP_REFERRAL_LIMIT_EXCEEDED, + "TLDAP_REFERRAL_LIMIT_EXCEEDED" }, +}; + +const char *tldap_rc2string(TLDAPRC rc) +{ + size_t i; + + for (i=0; i<ARRAY_SIZE(tldaprc_errmap); i++) { + if (TLDAP_RC_EQUAL(rc, tldaprc_errmap[i].rc)) { + return tldaprc_errmap[i].string; + } + } + + return "Unknown LDAP Error"; +} diff --git a/source3/lib/tldap_gensec_bind.c b/source3/lib/tldap_gensec_bind.c new file mode 100644 index 0000000..c409213 --- /dev/null +++ b/source3/lib/tldap_gensec_bind.c @@ -0,0 +1,366 @@ +/* + * Unix SMB/CIFS implementation. + * Gensec based tldap auth + * Copyright (C) Volker Lendecke 2015 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "tldap_gensec_bind.h" +#include "tldap_util.h" +#include "lib/util/tevent_unix.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/samba_util.h" +#include "lib/util/debug.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_internal.h" /* TODO: remove this */ +#include "lib/param/param.h" +#include "source4/auth/gensec/gensec_tstream.h" + +struct tldap_gensec_bind_state { + struct tevent_context *ev; + struct tldap_context *ctx; + struct cli_credentials *creds; + const char *target_service; + const char *target_hostname; + const char *target_principal; + struct loadparm_context *lp_ctx; + uint32_t gensec_features; + + bool first; + struct gensec_security *gensec; + NTSTATUS gensec_status; + DATA_BLOB gensec_output; +}; + +static void tldap_gensec_bind_got_mechs(struct tevent_req *subreq); +static void tldap_gensec_update_done(struct tldap_gensec_bind_state *state, + struct tevent_req *subreq); +static void tldap_gensec_bind_done(struct tevent_req *subreq); + +static struct tevent_req *tldap_gensec_bind_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct tldap_context *ctx, struct cli_credentials *creds, + const char *target_service, const char *target_hostname, + const char *target_principal, struct loadparm_context *lp_ctx, + uint32_t gensec_features) +{ + struct tevent_req *req, *subreq; + struct tldap_gensec_bind_state *state; + + const char *attrs[] = { "supportedSASLMechanisms" }; + + req = tevent_req_create(mem_ctx, &state, + struct tldap_gensec_bind_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = ctx; + state->creds = creds; + state->target_service = target_service; + state->target_hostname = target_hostname; + state->target_principal = target_principal; + state->lp_ctx = lp_ctx; + state->gensec_features = gensec_features; + state->first = true; + + subreq = tldap_search_all_send( + state, state->ev, state->ctx, "", TLDAP_SCOPE_BASE, + "(objectclass=*)", attrs, ARRAY_SIZE(attrs), + false, NULL, 0, NULL, 0, 0, 1 /* sizelimit */, 0); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_gensec_bind_got_mechs, req); + return req; +} + +static void tldap_gensec_bind_got_mechs(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_gensec_bind_state *state = tevent_req_data( + req, struct tldap_gensec_bind_state); + struct tldap_message **msgs, *msg, *result; + struct tldap_attribute *attribs, *attrib; + int num_attribs; + size_t num_msgs; + TLDAPRC rc; + int i; + bool ok; + const char **sasl_mechs; + NTSTATUS status; + + rc = tldap_search_all_recv(subreq, state, &msgs, &result); + TALLOC_FREE(subreq); + if (tevent_req_ldap_error(req, rc)) { + return; + } + + /* + * TODO: Inspect "Result" + */ + + num_msgs = talloc_array_length(msgs); + if (num_msgs != 1) { + DBG_DEBUG("num_msgs = %zu\n", num_msgs); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + msg = msgs[0]; + + ok = tldap_entry_attributes(msg, &attribs, &num_attribs); + if (!ok) { + DBG_DEBUG("tldap_entry_attributes failed\n"); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + + if (num_attribs != 1) { + DBG_DEBUG("num_attribs = %d\n", num_attribs); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + attrib = &attribs[0]; + + sasl_mechs = talloc_array(state, const char *, attrib->num_values+1); + if (tevent_req_nomem(sasl_mechs, req)) { + return; + } + + for (i=0; i<attrib->num_values; i++) { + DATA_BLOB *v = &attrib->values[i]; + size_t len; + + ok = convert_string_talloc(sasl_mechs, CH_UTF8, CH_UNIX, + v->data, v->length, + &sasl_mechs[i], &len); + if (!ok) { + DBG_DEBUG("convert_string_talloc failed\n"); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + } + sasl_mechs[attrib->num_values] = NULL; + + gensec_init(); + + status = gensec_client_start( + state, &state->gensec, + lpcfg_gensec_settings(state, state->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_client_start failed: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + + status = gensec_set_credentials(state->gensec, state->creds); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_set_credentials failed: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + + status = gensec_set_target_service(state->gensec, + state->target_service); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_set_target_service failed: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + + if (state->target_hostname != NULL) { + status = gensec_set_target_hostname(state->gensec, + state->target_hostname); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_set_target_hostname failed: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + } + + if (state->target_principal != NULL) { + status = gensec_set_target_principal(state->gensec, + state->target_principal); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_set_target_principal failed: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + } + + gensec_want_feature(state->gensec, state->gensec_features); + + status = gensec_start_mech_by_sasl_list(state->gensec, sasl_mechs); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_start_mech_by_sasl_list failed: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return; + } + + state->gensec_status = gensec_update(state->gensec, state, + data_blob_null, + &state->gensec_output); + tldap_gensec_update_done(state, req); +} + +static void tldap_gensec_update_done(struct tldap_gensec_bind_state *state, + struct tevent_req *req) +{ + struct tevent_req *subreq; + + if (!NT_STATUS_IS_OK(state->gensec_status) && + !NT_STATUS_EQUAL(state->gensec_status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DBG_DEBUG("gensec_update failed: %s\n", + nt_errstr(state->gensec_status)); + tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS); + return; + } + + if (NT_STATUS_IS_OK(state->gensec_status) && + (state->gensec_output.length == 0)) { + + if (state->first) { + tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS); + } else { + tevent_req_done(req); + } + return; + } + + state->first = false; + + subreq = tldap_sasl_bind_send( + state, state->ev, state->ctx, "", + state->gensec->ops->sasl_name, &state->gensec_output, + NULL, 0, NULL, 0); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tldap_gensec_bind_done, req); +} + +static void tldap_gensec_bind_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_gensec_bind_state *state = tevent_req_data( + req, struct tldap_gensec_bind_state); + DATA_BLOB input; + TLDAPRC rc; + + rc = tldap_sasl_bind_recv(subreq, state, &input); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc) && + !TLDAP_RC_EQUAL(rc, TLDAP_SASL_BIND_IN_PROGRESS)) { + tevent_req_ldap_error(req, rc); + return; + } + + if (TLDAP_RC_IS_SUCCESS(rc) && NT_STATUS_IS_OK(state->gensec_status)) { + tevent_req_done(req); + return; + } + + state->gensec_status = gensec_update(state->gensec, state, + input, + &state->gensec_output); + tldap_gensec_update_done(state, req); +} + +static TLDAPRC tldap_gensec_bind_recv(struct tevent_req *req) +{ + struct tldap_gensec_bind_state *state = tevent_req_data( + req, struct tldap_gensec_bind_state); + struct tstream_context *plain, *sec; + NTSTATUS status; + TLDAPRC rc; + + if (tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + + if ((state->gensec_features & GENSEC_FEATURE_SIGN) && + !gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN)) { + return TLDAP_OPERATIONS_ERROR; + } + if ((state->gensec_features & GENSEC_FEATURE_SEAL) && + !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) { + return TLDAP_OPERATIONS_ERROR; + } + + if (!gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN) && + !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) { + return TLDAP_SUCCESS; + } + + /* + * The gensec ctx needs to survive as long as the ldap context + * lives + */ + talloc_steal(state->ctx, state->gensec); + + plain = tldap_get_tstream(state->ctx); + + status = gensec_create_tstream(state->ctx, state->gensec, + plain, &sec); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_create_tstream failed: %s\n", + nt_errstr(status)); + return TLDAP_OPERATIONS_ERROR; + } + + tldap_set_tstream(state->ctx, sec); + + return TLDAP_SUCCESS; +} + +TLDAPRC tldap_gensec_bind( + struct tldap_context *ctx, struct cli_credentials *creds, + const char *target_service, const char *target_hostname, + const char *target_principal, struct loadparm_context *lp_ctx, + uint32_t gensec_features) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_gensec_bind_send(frame, ev, ctx, creds, target_service, + target_hostname, target_principal, lp_ctx, + gensec_features); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_gensec_bind_recv(req); + fail: + TALLOC_FREE(frame); + return rc; +} diff --git a/source3/lib/tldap_gensec_bind.h b/source3/lib/tldap_gensec_bind.h new file mode 100644 index 0000000..cb6b8e6 --- /dev/null +++ b/source3/lib/tldap_gensec_bind.h @@ -0,0 +1,33 @@ +/* + * Unix SMB/CIFS implementation. + * Gensec based tldap bind + * Copyright (C) Volker Lendecke 2015 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __TLDAP_GENSEC_BIND_H__ +#define __TLDAP_GENSEC_BIND_H__ + +#include "replace.h" +#include "tldap.h" +#include "auth/credentials/credentials.h" + +TLDAPRC tldap_gensec_bind( + struct tldap_context *ctx, struct cli_credentials *creds, + const char *target_service, const char *target_hostname, + const char *target_principal, struct loadparm_context *lp_ctx, + uint32_t gensec_features); + +#endif diff --git a/source3/lib/tldap_util.c b/source3/lib/tldap_util.c new file mode 100644 index 0000000..89aea4c --- /dev/null +++ b/source3/lib/tldap_util.c @@ -0,0 +1,843 @@ +/* + Unix SMB/CIFS implementation. + Infrastructure for async ldap client requests + Copyright (C) Volker Lendecke 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "tldap.h" +#include "tldap_util.h" +#include "../libcli/security/security.h" +#include "../lib/util/asn1.h" +#include "lib/util/smb_strtox.h" + +bool tldap_entry_values(struct tldap_message *msg, const char *attribute, + DATA_BLOB **values, int *num_values) +{ + struct tldap_attribute *attributes; + int i, num_attributes; + + if (!tldap_entry_attributes(msg, &attributes, &num_attributes)) { + return false; + } + + for (i=0; i<num_attributes; i++) { + if (strequal(attribute, attributes[i].name)) { + break; + } + } + if (i == num_attributes) { + return false; + } + *num_values = attributes[i].num_values; + *values = attributes[i].values; + return true; +} + +bool tldap_get_single_valueblob(struct tldap_message *msg, + const char *attribute, DATA_BLOB *blob) +{ + int num_values; + DATA_BLOB *values; + + if (attribute == NULL) { + return NULL; + } + if (!tldap_entry_values(msg, attribute, &values, &num_values)) { + return NULL; + } + if (num_values != 1) { + return NULL; + } + *blob = values[0]; + return true; +} + +char *tldap_talloc_single_attribute(struct tldap_message *msg, + const char *attribute, + TALLOC_CTX *mem_ctx) +{ + DATA_BLOB val; + char *result; + size_t len; + + if (!tldap_get_single_valueblob(msg, attribute, &val)) { + return NULL; + } + if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX, + val.data, val.length, + &result, &len)) { + return NULL; + } + return result; +} + +bool tldap_pull_binsid(struct tldap_message *msg, const char *attribute, + struct dom_sid *sid) +{ + DATA_BLOB val; + ssize_t ret; + + if (!tldap_get_single_valueblob(msg, attribute, &val)) { + return false; + } + ret = sid_parse(val.data, val.length, sid); + return (ret != -1); +} + +bool tldap_pull_guid(struct tldap_message *msg, const char *attribute, + struct GUID *guid) +{ + DATA_BLOB val; + + if (!tldap_get_single_valueblob(msg, attribute, &val)) { + return false; + } + return NT_STATUS_IS_OK(GUID_from_data_blob(&val, guid)); +} + +static bool tldap_add_blob_vals(TALLOC_CTX *mem_ctx, struct tldap_mod *mod, + DATA_BLOB *newvals, int num_newvals) +{ + int num_values = talloc_array_length(mod->values); + int i; + DATA_BLOB *tmp; + + tmp = talloc_realloc(mem_ctx, mod->values, DATA_BLOB, + num_values + num_newvals); + if (tmp == NULL) { + return false; + } + mod->values = tmp; + + for (i=0; i<num_newvals; i++) { + mod->values[i+num_values].data = (uint8_t *)talloc_memdup( + mod->values, newvals[i].data, newvals[i].length); + if (mod->values[i+num_values].data == NULL) { + return false; + } + mod->values[i+num_values].length = newvals[i].length; + } + mod->num_values = num_values + num_newvals; + return true; +} + +bool tldap_add_mod_blobs(TALLOC_CTX *mem_ctx, + struct tldap_mod **pmods, int *pnum_mods, + int mod_op, const char *attrib, + DATA_BLOB *newvals, int num_newvals) +{ + struct tldap_mod new_mod; + struct tldap_mod *mods = *pmods; + struct tldap_mod *mod = NULL; + int i, num_mods; + + if (mods == NULL) { + mods = talloc_array(mem_ctx, struct tldap_mod, 0); + } + if (mods == NULL) { + return false; + } + + num_mods = *pnum_mods; + + for (i=0; i<num_mods; i++) { + if ((mods[i].mod_op == mod_op) + && strequal(mods[i].attribute, attrib)) { + mod = &mods[i]; + break; + } + } + + if (mod == NULL) { + new_mod.mod_op = mod_op; + new_mod.attribute = talloc_strdup(mods, attrib); + if (new_mod.attribute == NULL) { + return false; + } + new_mod.num_values = 0; + new_mod.values = NULL; + mod = &new_mod; + } + + if ((num_newvals != 0) + && !tldap_add_blob_vals(mods, mod, newvals, num_newvals)) { + return false; + } + + if ((i == num_mods) && (talloc_array_length(mods) < num_mods + 1)) { + mods = talloc_realloc(talloc_tos(), mods, struct tldap_mod, + num_mods+1); + if (mods == NULL) { + return false; + } + mods[num_mods] = *mod; + } + + *pmods = mods; + *pnum_mods += 1; + return true; +} + +bool tldap_add_mod_str(TALLOC_CTX *mem_ctx, + struct tldap_mod **pmods, int *pnum_mods, + int mod_op, const char *attrib, const char *str) +{ + DATA_BLOB utf8; + bool ret; + + if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_UTF8, str, + strlen(str), &utf8.data, &utf8.length)) { + return false; + } + + ret = tldap_add_mod_blobs(mem_ctx, pmods, pnum_mods, mod_op, attrib, + &utf8, 1); + TALLOC_FREE(utf8.data); + return ret; +} + +static bool tldap_make_mod_blob_int(struct tldap_message *existing, + TALLOC_CTX *mem_ctx, + struct tldap_mod **pmods, int *pnum_mods, + const char *attrib, DATA_BLOB newval, + int (*comparison)(const DATA_BLOB *d1, + const DATA_BLOB *d2)) +{ + int num_values = 0; + DATA_BLOB *values = NULL; + DATA_BLOB oldval = data_blob_null; + + if ((existing != NULL) + && tldap_entry_values(existing, attrib, &values, &num_values)) { + + if (num_values > 1) { + /* can't change multivalue attributes atm */ + return false; + } + if (num_values == 1) { + oldval = values[0]; + } + } + + if ((oldval.data != NULL) && (newval.data != NULL) + && (comparison(&oldval, &newval) == 0)) { + /* Believe it or not, but LDAP will deny a delete and + an add at the same time if the values are the + same... */ + DEBUG(10,("tldap_make_mod_blob_int: attribute |%s| not " + "changed.\n", attrib)); + return true; + } + + if (oldval.data != NULL) { + /* By deleting exactly the value we found in the entry this + * should be race-free in the sense that the LDAP-Server will + * deny the complete operation if somebody changed the + * attribute behind our back. */ + /* This will also allow modifying single valued attributes in + * Novell NDS. In NDS you have to first remove attribute and + * then you could add new value */ + + DEBUG(10, ("tldap_make_mod_blob_int: deleting attribute |%s|\n", + attrib)); + if (!tldap_add_mod_blobs(mem_ctx, pmods, pnum_mods, + TLDAP_MOD_DELETE, + attrib, &oldval, 1)) { + return false; + } + } + + /* Regardless of the real operation (add or modify) + we add the new value here. We rely on deleting + the old value, should it exist. */ + + if (newval.data != NULL) { + DEBUG(10, ("tldap_make_mod_blob_int: adding attribute |%s| value len " + "%d\n", attrib, (int)newval.length)); + if (!tldap_add_mod_blobs(mem_ctx, pmods, pnum_mods, + TLDAP_MOD_ADD, + attrib, &newval, 1)) { + return false; + } + } + return true; +} + +bool tldap_make_mod_blob(struct tldap_message *existing, TALLOC_CTX *mem_ctx, + struct tldap_mod **pmods, int *pnum_mods, + const char *attrib, DATA_BLOB newval) +{ + return tldap_make_mod_blob_int(existing, mem_ctx, pmods, pnum_mods, + attrib, newval, data_blob_cmp); +} + +static int compare_utf8_blobs(const DATA_BLOB *d1, const DATA_BLOB *d2) +{ + char *s1, *s2; + size_t s1len, s2len; + int ret; + + if (!convert_string_talloc(talloc_tos(), CH_UTF8, CH_UNIX, d1->data, + d1->length, &s1, &s1len)) { + /* can't do much here */ + return 0; + } + if (!convert_string_talloc(talloc_tos(), CH_UTF8, CH_UNIX, d2->data, + d2->length, &s2, &s2len)) { + /* can't do much here */ + TALLOC_FREE(s1); + return 0; + } + ret = strcasecmp_m(s1, s2); + TALLOC_FREE(s2); + TALLOC_FREE(s1); + return ret; +} + +bool tldap_make_mod_fmt(struct tldap_message *existing, TALLOC_CTX *mem_ctx, + struct tldap_mod **pmods, int *pnum_mods, + const char *attrib, const char *fmt, ...) +{ + va_list ap; + char *newval; + bool ret; + DATA_BLOB blob = data_blob_null; + + va_start(ap, fmt); + newval = talloc_vasprintf(talloc_tos(), fmt, ap); + va_end(ap); + + if (newval == NULL) { + return false; + } + + blob.length = strlen(newval); + if (blob.length != 0) { + blob.data = discard_const_p(uint8_t, newval); + } + ret = tldap_make_mod_blob_int(existing, mem_ctx, pmods, pnum_mods, + attrib, blob, compare_utf8_blobs); + TALLOC_FREE(newval); + return ret; +} + +const char *tldap_errstr(TALLOC_CTX *mem_ctx, struct tldap_context *ld, + TLDAPRC rc) +{ + const char *ld_error = NULL; + char *res; + + if (ld != NULL) { + ld_error = tldap_msg_diagnosticmessage(tldap_ctx_lastmsg(ld)); + } + res = talloc_asprintf(mem_ctx, "LDAP error %d (%s), %s", + (int)TLDAP_RC_V(rc), tldap_rc2string(rc), + ld_error ? ld_error : "unknown"); + return res; +} + +TLDAPRC tldap_search_va(struct tldap_context *ld, const char *base, int scope, + const char *attrs[], int num_attrs, int attrsonly, + TALLOC_CTX *mem_ctx, struct tldap_message ***res, + const char *fmt, va_list ap) +{ + char *filter; + TLDAPRC rc; + + filter = talloc_vasprintf(talloc_tos(), fmt, ap); + if (filter == NULL) { + return TLDAP_NO_MEMORY; + } + + rc = tldap_search(ld, base, scope, filter, + attrs, num_attrs, attrsonly, + NULL /*sctrls*/, 0, NULL /*cctrls*/, 0, + 0 /*timelimit*/, 0 /*sizelimit*/, 0 /*deref*/, + mem_ctx, res); + TALLOC_FREE(filter); + return rc; +} + +TLDAPRC tldap_search_fmt(struct tldap_context *ld, const char *base, int scope, + const char *attrs[], int num_attrs, int attrsonly, + TALLOC_CTX *mem_ctx, struct tldap_message ***res, + const char *fmt, ...) +{ + va_list ap; + TLDAPRC rc; + + va_start(ap, fmt); + rc = tldap_search_va(ld, base, scope, attrs, num_attrs, attrsonly, + mem_ctx, res, fmt, ap); + va_end(ap); + return rc; +} + +bool tldap_pull_uint64(struct tldap_message *msg, const char *attr, + uint64_t *presult) +{ + char *str; + uint64_t result; + int error = 0; + + str = tldap_talloc_single_attribute(msg, attr, talloc_tos()); + if (str == NULL) { + DEBUG(10, ("Could not find attribute %s\n", attr)); + return false; + } + + result = smb_strtoull(str, NULL, 10, &error, SMB_STR_STANDARD); + if (error != 0) { + DBG_DEBUG("Attribute conversion failed (%s)\n", + strerror(error)); + TALLOC_FREE(str); + return false; + } + + TALLOC_FREE(str); + *presult = result; + return true; +} + +bool tldap_pull_uint32(struct tldap_message *msg, const char *attr, + uint32_t *presult) +{ + uint64_t result; + + if (!tldap_pull_uint64(msg, attr, &result)) { + return false; + } + *presult = (uint32_t)result; + return true; +} + +struct tldap_fetch_rootdse_state { + struct tldap_context *ld; + struct tldap_message *rootdse; +}; + +static void tldap_fetch_rootdse_done(struct tevent_req *subreq); + +struct tevent_req *tldap_fetch_rootdse_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld) +{ + struct tevent_req *req, *subreq; + struct tldap_fetch_rootdse_state *state; + static const char *attrs[2] = { "*", "+" }; + + req = tevent_req_create(mem_ctx, &state, + struct tldap_fetch_rootdse_state); + if (req == NULL) { + return NULL; + } + state->ld = ld; + state->rootdse = NULL; + + subreq = tldap_search_send( + mem_ctx, ev, ld, "", TLDAP_SCOPE_BASE, "(objectclass=*)", + attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0, 0, 0, 0); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_fetch_rootdse_done, req); + return req; +} + +static void tldap_fetch_rootdse_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_fetch_rootdse_state *state = tevent_req_data( + req, struct tldap_fetch_rootdse_state); + struct tldap_message *msg; + TLDAPRC rc; + + rc = tldap_search_recv(subreq, state, &msg); + if (tevent_req_ldap_error(req, rc)) { + return; + } + + switch (tldap_msg_type(msg)) { + case TLDAP_RES_SEARCH_ENTRY: + if (state->rootdse != NULL) { + goto protocol_error; + } + state->rootdse = msg; + break; + case TLDAP_RES_SEARCH_RESULT: + TALLOC_FREE(subreq); + if (state->rootdse == NULL) { + goto protocol_error; + } + tevent_req_done(req); + break; + default: + goto protocol_error; + } + return; + +protocol_error: + tevent_req_ldap_error(req, TLDAP_PROTOCOL_ERROR); + return; +} + +TLDAPRC tldap_fetch_rootdse_recv(struct tevent_req *req) +{ + struct tldap_fetch_rootdse_state *state = tevent_req_data( + req, struct tldap_fetch_rootdse_state); + TLDAPRC rc; + char *dn; + + if (tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + /* Trigger parsing the dn, just to make sure it's ok */ + if (!tldap_entry_dn(state->rootdse, &dn)) { + return TLDAP_DECODING_ERROR; + } + if (!tldap_context_setattr(state->ld, "tldap:rootdse", + &state->rootdse)) { + return TLDAP_NO_MEMORY; + } + return TLDAP_SUCCESS; +} + +TLDAPRC tldap_fetch_rootdse(struct tldap_context *ld) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_fetch_rootdse_send(frame, ev, ld); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + + rc = tldap_fetch_rootdse_recv(req); + fail: + TALLOC_FREE(frame); + return rc; +} + +struct tldap_message *tldap_rootdse(struct tldap_context *ld) +{ + return talloc_get_type(tldap_context_getattr(ld, "tldap:rootdse"), + struct tldap_message); +} + +bool tldap_entry_has_attrvalue(struct tldap_message *msg, + const char *attribute, + const DATA_BLOB blob) +{ + int i, num_values; + DATA_BLOB *values; + + if (!tldap_entry_values(msg, attribute, &values, &num_values)) { + return false; + } + for (i=0; i<num_values; i++) { + if (data_blob_cmp(&values[i], &blob) == 0) { + return true; + } + } + return false; +} + +bool tldap_supports_control(struct tldap_context *ld, const char *oid) +{ + struct tldap_message *rootdse = tldap_rootdse(ld); + + if (rootdse == NULL) { + return false; + } + return tldap_entry_has_attrvalue(rootdse, "supportedControl", + data_blob_const(oid, strlen(oid))); +} + +struct tldap_control *tldap_add_control(TALLOC_CTX *mem_ctx, + struct tldap_control *ctrls, + int num_ctrls, + struct tldap_control *ctrl) +{ + struct tldap_control *result; + + result = talloc_array(mem_ctx, struct tldap_control, num_ctrls+1); + if (result == NULL) { + return NULL; + } + if (num_ctrls > 0) { + memcpy(result, ctrls, sizeof(struct tldap_control) * num_ctrls); + } + result[num_ctrls] = *ctrl; + return result; +} + +/* + * Find a control returned by the server + */ +struct tldap_control *tldap_msg_findcontrol(struct tldap_message *msg, + const char *oid) +{ + struct tldap_control *controls; + int i, num_controls; + + tldap_msg_sctrls(msg, &num_controls, &controls); + + for (i=0; i<num_controls; i++) { + if (strcmp(controls[i].oid, oid) == 0) { + return &controls[i]; + } + } + return NULL; +} + +struct tldap_search_paged_state { + struct tevent_context *ev; + struct tldap_context *ld; + const char *base; + const char *filter; + int scope; + const char **attrs; + int num_attrs; + int attrsonly; + struct tldap_control *sctrls; + int num_sctrls; + struct tldap_control *cctrls; + int num_cctrls; + int timelimit; + int sizelimit; + int deref; + + int page_size; + struct asn1_data *asn1; + DATA_BLOB cookie; + struct tldap_message *result; +}; + +static struct tevent_req *tldap_ship_paged_search( + TALLOC_CTX *mem_ctx, + struct tldap_search_paged_state *state) +{ + struct tldap_control *pgctrl; + struct asn1_data *asn1 = NULL; + + asn1 = asn1_init(state, ASN1_MAX_TREE_DEPTH); + if (asn1 == NULL) { + return NULL; + } + if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err; + if (!asn1_write_Integer(asn1, state->page_size)) goto err; + if (!asn1_write_OctetString(asn1, state->cookie.data, state->cookie.length)) goto err; + if (!asn1_pop_tag(asn1)) goto err; + state->asn1 = asn1; + + pgctrl = &state->sctrls[state->num_sctrls-1]; + pgctrl->oid = TLDAP_CONTROL_PAGEDRESULTS; + pgctrl->critical = true; + if (!asn1_blob(state->asn1, &pgctrl->value)) { + goto err; + } + return tldap_search_send(mem_ctx, state->ev, state->ld, state->base, + state->scope, state->filter, state->attrs, + state->num_attrs, state->attrsonly, + state->sctrls, state->num_sctrls, + state->cctrls, state->num_cctrls, + state->timelimit, state->sizelimit, + state->deref); + + err: + + TALLOC_FREE(asn1); + return NULL; +} + +static void tldap_search_paged_done(struct tevent_req *subreq); + +struct tevent_req *tldap_search_paged_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *base, int scope, + const char *filter, + const char **attrs, + int num_attrs, + int attrsonly, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls, + int timelimit, + int sizelimit, + int deref, + int page_size) +{ + struct tevent_req *req, *subreq; + struct tldap_search_paged_state *state; + struct tldap_control empty_control; + + req = tevent_req_create(mem_ctx, &state, + struct tldap_search_paged_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ld = ld; + state->base = base; + state->filter = filter; + state->scope = scope; + state->attrs = attrs; + state->num_attrs = num_attrs; + state->attrsonly = attrsonly; + state->cctrls = cctrls; + state->num_cctrls = num_cctrls; + state->timelimit = timelimit; + state->sizelimit = sizelimit; + state->deref = deref; + + state->page_size = page_size; + state->asn1 = NULL; + state->cookie = data_blob_null; + + ZERO_STRUCT(empty_control); + + state->sctrls = tldap_add_control(state, sctrls, num_sctrls, + &empty_control); + if (tevent_req_nomem(state->sctrls, req)) { + return tevent_req_post(req, ev); + } + state->num_sctrls = num_sctrls+1; + + subreq = tldap_ship_paged_search(state, state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_search_paged_done, req); + + return req; +} + +static void tldap_search_paged_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_search_paged_state *state = tevent_req_data( + req, struct tldap_search_paged_state); + struct asn1_data *asn1 = NULL; + struct tldap_control *pgctrl; + TLDAPRC rc; + int size; + + rc = tldap_search_recv(subreq, state, &state->result); + if (tevent_req_ldap_error(req, rc)) { + return; + } + + TALLOC_FREE(state->asn1); + + switch (tldap_msg_type(state->result)) { + case TLDAP_RES_SEARCH_ENTRY: + case TLDAP_RES_SEARCH_REFERENCE: + tevent_req_notify_callback(req); + return; + case TLDAP_RES_SEARCH_RESULT: + break; + default: + TALLOC_FREE(subreq); + tevent_req_ldap_error(req, TLDAP_PROTOCOL_ERROR); + return; + } + + TALLOC_FREE(subreq); + + /* We've finished one paged search, fire the next */ + + pgctrl = tldap_msg_findcontrol(state->result, + TLDAP_CONTROL_PAGEDRESULTS); + if (pgctrl == NULL) { + /* RFC2696 requires the server to return the control */ + tevent_req_ldap_error(req, TLDAP_PROTOCOL_ERROR); + return; + } + + TALLOC_FREE(state->cookie.data); + + asn1 = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); + if (tevent_req_nomem(asn1, req)) { + return; + } + + asn1_load_nocopy(asn1, pgctrl->value.data, pgctrl->value.length); + if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) goto err; + if (!asn1_read_Integer(asn1, &size)) goto err; + if (!asn1_read_OctetString(asn1, state, &state->cookie)) goto err; + if (!asn1_end_tag(asn1)) goto err; + + TALLOC_FREE(asn1); + + if (state->cookie.length == 0) { + /* We're done, no cookie anymore */ + tevent_req_done(req); + return; + } + + TALLOC_FREE(state->result); + + subreq = tldap_ship_paged_search(state, state); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tldap_search_paged_done, req); + + return; +err: + + TALLOC_FREE(asn1); + tevent_req_ldap_error(req, TLDAP_DECODING_ERROR); +} + +TLDAPRC tldap_search_paged_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct tldap_message **pmsg) +{ + struct tldap_search_paged_state *state = tevent_req_data( + req, struct tldap_search_paged_state); + TLDAPRC rc; + + if (!tevent_req_is_in_progress(req) + && tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + if (tevent_req_is_in_progress(req)) { + switch (tldap_msg_type(state->result)) { + case TLDAP_RES_SEARCH_ENTRY: + case TLDAP_RES_SEARCH_REFERENCE: + break; + default: + return TLDAP_PROTOCOL_ERROR; + } + } + *pmsg = talloc_move(mem_ctx, &state->result); + return TLDAP_SUCCESS; +} diff --git a/source3/lib/username.c b/source3/lib/username.c new file mode 100644 index 0000000..280285e --- /dev/null +++ b/source3/lib/username.c @@ -0,0 +1,243 @@ +/* + Unix SMB/CIFS implementation. + Username handling + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1997-2001. + Copyright (C) Andrew Bartlett 2002 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/passwd.h" +#include "../lib/util/memcache.h" +#include "../lib/util/util_pw.h" +#include "lib/util/string_wrappers.h" + +/* internal functions */ +static struct passwd *uname_string_combinations(char *s, TALLOC_CTX *mem_ctx, + struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *), + int N); +static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx, int offset, + struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *), + int N); + +static struct passwd *getpwnam_alloc_cached(TALLOC_CTX *mem_ctx, const char *name) +{ + struct passwd *pw, *for_cache; + + pw = (struct passwd *)memcache_lookup_talloc( + NULL, GETPWNAM_CACHE, data_blob_string_const_null(name)); + if (pw != NULL) { + return tcopy_passwd(mem_ctx, pw); + } + + pw = getpwnam(name); + if (pw == NULL) { + return NULL; + } + + for_cache = tcopy_passwd(talloc_tos(), pw); + if (for_cache == NULL) { + return NULL; + } + + memcache_add_talloc(NULL, GETPWNAM_CACHE, + data_blob_string_const_null(name), &for_cache); + + return tcopy_passwd(mem_ctx, pw); +} + +/**************************************************************************** + Flush all cached passwd structs. +****************************************************************************/ + +void flush_pwnam_cache(void) +{ + memcache_flush(NULL, GETPWNAM_CACHE); +} + +/**************************************************************************** + Get a users home directory. +****************************************************************************/ + +char *get_user_home_dir(TALLOC_CTX *mem_ctx, const char *user) +{ + struct passwd *pass; + char *result; + + /* Ensure the user exists. */ + + pass = Get_Pwnam_alloc(mem_ctx, user); + + if (!pass) + return(NULL); + + /* Return home directory from struct passwd. */ + + result = talloc_move(mem_ctx, &pass->pw_dir); + + TALLOC_FREE(pass); + return result; +} + +/**************************************************************************** + * A wrapper for getpwnam(). The following variations are tried: + * - as transmitted + * - in all lower case if this differs from transmitted + * - in all upper case if this differs from transmitted + * - using lp_username_level() for permutations. +****************************************************************************/ + +static struct passwd *Get_Pwnam_internals(TALLOC_CTX *mem_ctx, + const char *user, char *user2) +{ + struct passwd *ret = NULL; + + if (!user2 || !(*user2)) + return(NULL); + + if (!user || !(*user)) + return(NULL); + + /* Try in all lower case first as this is the most + common case on UNIX systems */ + if (!strlower_m(user2)) { + DEBUG(5,("strlower_m %s failed\n", user2)); + goto done; + } + + DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2)); + ret = getpwnam_alloc_cached(mem_ctx, user2); + if(ret) + goto done; + + /* Try as given, if username wasn't originally lowercase */ + if(strcmp(user, user2) != 0) { + DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", + user)); + ret = getpwnam_alloc_cached(mem_ctx, user); + if(ret) + goto done; + } + + /* Try as uppercase, if username wasn't originally uppercase */ + if (!strupper_m(user2)) { + goto done; + } + + if(strcmp(user, user2) != 0) { + DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", + user2)); + ret = getpwnam_alloc_cached(mem_ctx, user2); + if(ret) + goto done; + } + + /* Try all combinations up to usernamelevel */ + if (!strlower_m(user2)) { + DEBUG(5,("strlower_m %s failed\n", user2)); + goto done; + } + DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", + lp_username_level(), user2)); + ret = uname_string_combinations(user2, mem_ctx, getpwnam_alloc_cached, + lp_username_level()); + +done: + DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? + "did":"didn't", user)); + + return ret; +} + +/**************************************************************************** + Get_Pwnam wrapper without modification. + NOTE: This with NOT modify 'user'! + This will return an allocated structure +****************************************************************************/ + +struct passwd *Get_Pwnam_alloc(TALLOC_CTX *mem_ctx, const char *user) +{ + fstring user2; + + if ( *user == '\0' ) { + DEBUG(10,("Get_Pwnam: empty username!\n")); + return NULL; + } + + fstrcpy(user2, user); + + DEBUG(5,("Finding user %s\n", user)); + + return Get_Pwnam_internals(mem_ctx, user, user2); +} + +/* The functions below have been taken from password.c and slightly modified */ +/**************************************************************************** + Apply a function to upper/lower case combinations + of a string and return true if one of them returns true. + Try all combinations with N uppercase letters. + offset is the first char to try and change (start with 0) + it assumes the string starts lowercased +****************************************************************************/ + +static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx, + int offset, + struct passwd *(*fn)(TALLOC_CTX *mem_ctx, const char *), + int N) +{ + ssize_t len = (ssize_t)strlen(s); + int i; + struct passwd *ret; + + if (N <= 0 || offset >= len) + return(fn(mem_ctx, s)); + + for (i=offset;i<(len-(N-1));i++) { + char c = s[i]; + if (!islower_m((int)c)) + continue; + s[i] = toupper_m(c); + ret = uname_string_combinations2(s, mem_ctx, i+1, fn, N-1); + if(ret) + return(ret); + s[i] = c; + } + return(NULL); +} + +/**************************************************************************** + Apply a function to upper/lower case combinations + of a string and return true if one of them returns true. + Try all combinations with up to N uppercase letters. + offset is the first char to try and change (start with 0) + it assumes the string starts lowercased +****************************************************************************/ + +static struct passwd * uname_string_combinations(char *s, TALLOC_CTX *mem_ctx, + struct passwd * (*fn)(TALLOC_CTX *mem_ctx, const char *), + int N) +{ + int n; + struct passwd *ret; + + for (n=1;n<=N;n++) { + ret = uname_string_combinations2(s,mem_ctx,0,fn,n); + if(ret) + return(ret); + } + return(NULL); +} + diff --git a/source3/lib/util.c b/source3/lib/util.c new file mode 100644 index 0000000..51dc50d --- /dev/null +++ b/source3/lib/util.c @@ -0,0 +1,2013 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2007 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + Copyright (C) James Peach 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @brief Small functions that don't fit anywhere else + * @file util.c + */ + +#include "includes.h" +#include "system/passwd.h" +#include "system/filesys.h" +#include "lib/util/server_id.h" +#include "lib/util/memcache.h" +#include "util_tdb.h" +#include "ctdbd_conn.h" +#include "../lib/util/util_pw.h" +#include "messages.h" +#include "lib/messaging/messages_dgm.h" +#include "libcli/security/security.h" +#include "serverid.h" +#include "lib/util/sys_rw.h" +#include "lib/util/sys_rw_data.h" +#include "lib/util/util_process.h" +#include "lib/dbwrap/dbwrap_ctdb.h" +#include "lib/gencache.h" +#include "lib/util/string_wrappers.h" + +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + +/* Max allowable allococation - 256mb - 0x10000000 */ +#define MAX_ALLOC_SIZE (1024*1024*256) + +static enum protocol_types Protocol = PROTOCOL_COREPLUS; + +enum protocol_types get_Protocol(void) +{ + return Protocol; +} + +void set_Protocol(enum protocol_types p) +{ + Protocol = p; +} + +static enum remote_arch_types ra_type = RA_UNKNOWN; + +void gfree_all( void ) +{ + gfree_loadparm(); + gfree_charcnv(); + gfree_interfaces(); + gfree_debugsyms(); + gfree_memcache(); + +} + +/******************************************************************* + Check if a file exists - call vfs_file_exist for samba files. +********************************************************************/ + +bool file_exist_stat(const char *fname,SMB_STRUCT_STAT *sbuf, + bool fake_dir_create_times) +{ + SMB_STRUCT_STAT st; + if (!sbuf) + sbuf = &st; + + if (sys_stat(fname, sbuf, fake_dir_create_times) != 0) + return(False); + + return((S_ISREG(sbuf->st_ex_mode)) || (S_ISFIFO(sbuf->st_ex_mode))); +} + +/******************************************************************* + Check if a unix domain socket exists - call vfs_file_exist for samba files. +********************************************************************/ + +bool socket_exist(const char *fname) +{ + SMB_STRUCT_STAT st; + if (sys_stat(fname, &st, false) != 0) + return(False); + + return S_ISSOCK(st.st_ex_mode); +} + +/******************************************************************* + Returns the size in bytes of the named given the stat struct. +********************************************************************/ + +uint64_t get_file_size_stat(const SMB_STRUCT_STAT *sbuf) +{ + return sbuf->st_ex_size; +} + +/**************************************************************************** + Check two stats have identical dev and ino fields. +****************************************************************************/ + +bool check_same_dev_ino(const SMB_STRUCT_STAT *sbuf1, + const SMB_STRUCT_STAT *sbuf2) +{ + return ((sbuf1->st_ex_dev == sbuf2->st_ex_dev) && + (sbuf1->st_ex_ino == sbuf2->st_ex_ino)); +} + +/**************************************************************************** + Check if a stat struct is identical for use. +****************************************************************************/ + +bool check_same_stat(const SMB_STRUCT_STAT *sbuf1, + const SMB_STRUCT_STAT *sbuf2) +{ + return ((sbuf1->st_ex_uid == sbuf2->st_ex_uid) && + (sbuf1->st_ex_gid == sbuf2->st_ex_gid) && + check_same_dev_ino(sbuf1, sbuf2)); +} + +/******************************************************************* + Show a smb message structure. +********************************************************************/ + +void show_msg(const char *buf) +{ + int i; + int bcc=0; + + if (!DEBUGLVL(5)) + return; + + DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n", + smb_len(buf), + (int)CVAL(buf,smb_com), + (int)CVAL(buf,smb_rcls), + (int)CVAL(buf,smb_reh), + (int)SVAL(buf,smb_err), + (int)CVAL(buf,smb_flg), + (int)SVAL(buf,smb_flg2))); + DEBUGADD(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\n", + (int)SVAL(buf,smb_tid), + (int)SVAL(buf,smb_pid), + (int)SVAL(buf,smb_uid), + (int)SVAL(buf,smb_mid))); + DEBUGADD(5,("smt_wct=%d\n",(int)CVAL(buf,smb_wct))); + + for (i=0;i<(int)CVAL(buf,smb_wct);i++) + DEBUGADD(5,("smb_vwv[%2d]=%5d (0x%X)\n",i, + SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i))); + + bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct))); + + DEBUGADD(5,("smb_bcc=%d\n",bcc)); + + if (DEBUGLEVEL < 10) + return; + + if (DEBUGLEVEL < 50) + bcc = MIN(bcc, 512); + + dump_data(10, (const uint8_t *)smb_buf_const(buf), bcc); +} + +/******************************************************************* + Setup only the byte count for a smb message. +********************************************************************/ + +int set_message_bcc(char *buf,int num_bytes) +{ + int num_words = CVAL(buf,smb_wct); + SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); + _smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); + return (smb_size + num_words*2 + num_bytes); +} + +/******************************************************************* + Add a data blob to the end of a smb_buf, adjusting bcc and smb_len. + Return the bytes added +********************************************************************/ + +ssize_t message_push_blob(uint8_t **outbuf, DATA_BLOB blob) +{ + size_t newlen = smb_len(*outbuf) + 4 + blob.length; + uint8_t *tmp; + + if (!(tmp = talloc_realloc(NULL, *outbuf, uint8_t, newlen))) { + DEBUG(0, ("talloc failed\n")); + return -1; + } + *outbuf = tmp; + + memcpy(tmp + smb_len(tmp) + 4, blob.data, blob.length); + set_message_bcc((char *)tmp, smb_buflen(tmp) + blob.length); + return blob.length; +} + +/******************************************************************* + Reduce a file name, removing .. elements. +********************************************************************/ + +static char *dos_clean_name(TALLOC_CTX *ctx, const char *s) +{ + char *p = NULL; + char *str = NULL; + + DEBUG(3,("dos_clean_name [%s]\n",s)); + + /* remove any double slashes */ + str = talloc_all_string_sub(ctx, s, "\\\\", "\\"); + if (!str) { + return NULL; + } + + /* Remove leading .\\ characters */ + if(strncmp(str, ".\\", 2) == 0) { + trim_string(str, ".\\", NULL); + if(*str == 0) { + str = talloc_strdup(ctx, ".\\"); + if (!str) { + return NULL; + } + } + } + + while ((p = strstr_m(str,"\\..\\")) != NULL) { + char *s1; + + *p = 0; + s1 = p+3; + + if ((p=strrchr_m(str,'\\')) != NULL) { + *p = 0; + } else { + *str = 0; + } + str = talloc_asprintf(ctx, + "%s%s", + str, + s1); + if (!str) { + return NULL; + } + } + + trim_string(str,NULL,"\\.."); + return talloc_all_string_sub(ctx, str, "\\.\\", "\\"); +} + +/******************************************************************* + Reduce a file name, removing .. elements. +********************************************************************/ + +char *unix_clean_name(TALLOC_CTX *ctx, const char *s) +{ + char *p = NULL; + char *str = NULL; + + DEBUG(3,("unix_clean_name [%s]\n",s)); + + /* remove any double slashes */ + str = talloc_all_string_sub(ctx, s, "//","/"); + if (!str) { + return NULL; + } + + /* Remove leading ./ characters */ + if(strncmp(str, "./", 2) == 0) { + trim_string(str, "./", NULL); + if(*str == 0) { + str = talloc_strdup(ctx, "./"); + if (!str) { + return NULL; + } + } + } + + while ((p = strstr_m(str,"/../")) != NULL) { + char *s1; + + *p = 0; + s1 = p+3; + + if ((p=strrchr_m(str,'/')) != NULL) { + *p = 0; + } else { + *str = 0; + } + str = talloc_asprintf(ctx, + "%s%s", + str, + s1); + if (!str) { + return NULL; + } + } + + trim_string(str,NULL,"/.."); + return talloc_all_string_sub(ctx, str, "/./", "/"); +} + +char *clean_name(TALLOC_CTX *ctx, const char *s) +{ + char *str = dos_clean_name(ctx, s); + if (!str) { + return NULL; + } + return unix_clean_name(ctx, str); +} + +/******************************************************************* + Write data into an fd at a given offset. Ignore seek errors. +********************************************************************/ + +ssize_t write_data_at_offset(int fd, const char *buffer, size_t N, off_t pos) +{ + size_t total=0; + ssize_t ret; + + if (pos == (off_t)-1) { + return write_data(fd, buffer, N); + } +#if defined(HAVE_PWRITE) || defined(HAVE_PRWITE64) + while (total < N) { + ret = sys_pwrite(fd,buffer + total,N - total, pos); + if (ret == -1 && errno == ESPIPE) { + return write_data(fd, buffer + total,N - total); + } + if (ret == -1) { + DEBUG(0,("write_data_at_offset: write failure. Error = %s\n", strerror(errno) )); + return -1; + } + if (ret == 0) { + return total; + } + total += ret; + pos += ret; + } + return (ssize_t)total; +#else + /* Use lseek and write_data. */ + if (lseek(fd, pos, SEEK_SET) == -1) { + if (errno != ESPIPE) { + return -1; + } + } + return write_data(fd, buffer, N); +#endif +} + +static int reinit_after_fork_pipe[2] = { -1, -1 }; + +NTSTATUS init_before_fork(void) +{ + int ret; + + ret = pipe(reinit_after_fork_pipe); + if (ret == -1) { + NTSTATUS status; + + status = map_nt_error_from_unix_common(errno); + + DEBUG(0, ("Error creating child_pipe: %s\n", + nt_errstr(status))); + + return status; + } + + return NT_STATUS_OK; +} + +/** + * @brief Get a fd to watch for our parent process to exit + * + * Samba parent processes open a pipe that naturally closes when the + * parent exits. Child processes can watch the read end of the pipe + * for readability: Readability with 0 bytes to read means the parent + * has exited and the child process might also want to exit. + */ + +int parent_watch_fd(void) +{ + return reinit_after_fork_pipe[0]; +} + +/** + * Detect died parent by detecting EOF on the pipe + */ +static void reinit_after_fork_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + char c; + + if (sys_read(reinit_after_fork_pipe[0], &c, 1) != 1) { + /* + * we have reached EOF on stdin, which means the + * parent has exited. Shutdown the server + */ + TALLOC_FREE(fde); + (void)kill(getpid(), SIGTERM); + } +} + + +NTSTATUS reinit_after_fork(struct messaging_context *msg_ctx, + struct tevent_context *ev_ctx, + bool parent_longlived) +{ + NTSTATUS status = NT_STATUS_OK; + int ret; + + /* + * The main process thread should never + * allow per_thread_cwd_enable() to be + * called. + */ + per_thread_cwd_disable(); + + if (reinit_after_fork_pipe[1] != -1) { + close(reinit_after_fork_pipe[1]); + reinit_after_fork_pipe[1] = -1; + } + + /* tdb needs special fork handling */ + if (tdb_reopen_all(parent_longlived ? 1 : 0) != 0) { + DEBUG(0,("tdb_reopen_all failed.\n")); + status = NT_STATUS_OPEN_FAILED; + goto done; + } + + if (ev_ctx != NULL) { + /* + * The parent can have different private data for the callbacks, + * which are gone in the child. Reset the callbacks to be safe. + */ + tevent_set_trace_callback(ev_ctx, NULL, NULL); + tevent_set_trace_fd_callback(ev_ctx, NULL, NULL); + tevent_set_trace_signal_callback(ev_ctx, NULL, NULL); + tevent_set_trace_timer_callback(ev_ctx, NULL, NULL); + tevent_set_trace_immediate_callback(ev_ctx, NULL, NULL); + tevent_set_trace_queue_callback(ev_ctx, NULL, NULL); + if (tevent_re_initialise(ev_ctx) != 0) { + smb_panic(__location__ ": Failed to re-initialise event context"); + } + } + + if (reinit_after_fork_pipe[0] != -1) { + struct tevent_fd *fde; + + fde = tevent_add_fd(ev_ctx, ev_ctx /* TALLOC_CTX */, + reinit_after_fork_pipe[0], TEVENT_FD_READ, + reinit_after_fork_pipe_handler, NULL); + if (fde == NULL) { + smb_panic(__location__ ": Failed to add reinit_after_fork pipe event"); + } + } + + if (msg_ctx) { + /* + * For clustering, we need to re-init our ctdbd connection after the + * fork + */ + status = messaging_reinit(msg_ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("messaging_reinit() failed: %s\n", + nt_errstr(status))); + } + + if (lp_clustering()) { + ret = ctdb_async_ctx_reinit( + NULL, messaging_tevent_context(msg_ctx)); + if (ret != 0) { + DBG_ERR("db_ctdb_async_ctx_reinit failed: %s\n", + strerror(errno)); + return map_nt_error_from_unix(ret); + } + } + } + + done: + return status; +} + +/**************************************************************************** + (Hopefully) efficient array append. +****************************************************************************/ + +void add_to_large_array(TALLOC_CTX *mem_ctx, size_t element_size, + void *element, void *_array, uint32_t *num_elements, + ssize_t *array_size) +{ + void **array = (void **)_array; + + if (*array_size < 0) { + return; + } + + if (*array == NULL) { + if (*array_size == 0) { + *array_size = 128; + } + + if (*array_size >= MAX_ALLOC_SIZE/element_size) { + goto error; + } + + *array = TALLOC(mem_ctx, element_size * (*array_size)); + if (*array == NULL) { + goto error; + } + } + + if (*num_elements == *array_size) { + *array_size *= 2; + + if (*array_size >= MAX_ALLOC_SIZE/element_size) { + goto error; + } + + *array = TALLOC_REALLOC(mem_ctx, *array, + element_size * (*array_size)); + + if (*array == NULL) { + goto error; + } + } + + memcpy((char *)(*array) + element_size*(*num_elements), + element, element_size); + *num_elements += 1; + + return; + + error: + *num_elements = 0; + *array_size = -1; +} + +/**************************************************************************** + Get my own domain name, or "" if we have none. +****************************************************************************/ + +char *get_mydnsdomname(TALLOC_CTX *ctx) +{ + const char *domname; + char *p; + + domname = get_mydnsfullname(); + if (!domname) { + return NULL; + } + + p = strchr_m(domname, '.'); + if (p) { + p++; + return talloc_strdup(ctx, p); + } else { + return talloc_strdup(ctx, ""); + } +} + +bool process_exists(const struct server_id pid) +{ + return serverid_exists(&pid); +} + +/******************************************************************* + Convert a uid into a user name. +********************************************************************/ + +const char *uidtoname(uid_t uid) +{ + TALLOC_CTX *ctx = talloc_tos(); + char *name = NULL; + struct passwd *pass = NULL; + + pass = getpwuid_alloc(ctx,uid); + if (pass) { + name = talloc_strdup(ctx,pass->pw_name); + TALLOC_FREE(pass); + } else { + name = talloc_asprintf(ctx, + "%ld", + (long int)uid); + } + return name; +} + +/******************************************************************* + Convert a gid into a group name. +********************************************************************/ + +char *gidtoname(gid_t gid) +{ + struct group *grp; + + grp = getgrgid(gid); + if (grp) { + return talloc_strdup(talloc_tos(), grp->gr_name); + } + else { + return talloc_asprintf(talloc_tos(), + "%d", + (int)gid); + } +} + +/******************************************************************* + Convert a user name into a uid. +********************************************************************/ + +uid_t nametouid(const char *name) +{ + struct passwd *pass; + char *p; + uid_t u; + + pass = Get_Pwnam_alloc(talloc_tos(), name); + if (pass) { + u = pass->pw_uid; + TALLOC_FREE(pass); + return u; + } + + u = (uid_t)strtol(name, &p, 0); + if ((p != name) && (*p == '\0')) + return u; + + return (uid_t)-1; +} + +/******************************************************************* + Convert a name to a gid_t if possible. Return -1 if not a group. +********************************************************************/ + +gid_t nametogid(const char *name) +{ + struct group *grp; + char *p; + gid_t g; + + g = (gid_t)strtol(name, &p, 0); + if ((p != name) && (*p == '\0')) + return g; + + grp = getgrnam(name); + if (grp) + return(grp->gr_gid); + return (gid_t)-1; +} + +/******************************************************************* + Something really nasty happened - panic ! +********************************************************************/ + +static void call_panic_action(const char *why, bool as_root) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *cmd; + int result; + + cmd = lp_panic_action(talloc_tos(), lp_sub); + if (cmd == NULL || cmd[0] == '\0') { + return; + } + + DBG_ERR("Calling panic action [%s]\n", cmd); + +#if defined(HAVE_PRCTL) && defined(PR_SET_PTRACER) + /* + * Make sure all children can attach a debugger. + */ + prctl(PR_SET_PTRACER, getpid(), 0, 0, 0); +#endif + + if (as_root) { + become_root(); + } + + result = system(cmd); + + if (as_root) { + unbecome_root(); + } + + if (result == -1) + DBG_ERR("fork failed in panic action: %s\n", + strerror(errno)); + else + DBG_ERR("action returned status %d\n", + WEXITSTATUS(result)); +} + +void smb_panic_s3(const char *why) +{ + call_panic_action(why, false); + dump_core(); +} + +void log_panic_action(const char *msg) +{ + DBG_ERR("%s", msg); + call_panic_action(msg, true); +} + +/******************************************************************* + A readdir wrapper which just returns the file name. + ********************************************************************/ + +const char *readdirname(DIR *p) +{ + struct dirent *ptr; + char *dname; + + if (!p) + return(NULL); + + ptr = (struct dirent *)readdir(p); + if (!ptr) + return(NULL); + + dname = ptr->d_name; + + return talloc_strdup(talloc_tos(), dname); +} + +/******************************************************************* + Utility function used to decide if the last component + of a path matches a (possibly wildcarded) entry in a namelist. +********************************************************************/ + +bool is_in_path(const char *name, name_compare_entry *namelist, bool case_sensitive) +{ + const char *last_component; + + /* if we have no list it's obviously not in the path */ + if ((namelist == NULL) || (namelist[0].name == NULL)) { + return False; + } + + /* Do not reject path components if namelist is set to '.*' */ + if (ISDOT(name) || ISDOTDOT(name)) { + return false; + } + + DEBUG(8, ("is_in_path: %s\n", name)); + + /* Get the last component of the unix name. */ + last_component = strrchr_m(name, '/'); + if (!last_component) { + last_component = name; + } else { + last_component++; /* Go past '/' */ + } + + for(; namelist->name != NULL; namelist++) { + if(namelist->is_wild) { + if (mask_match(last_component, namelist->name, case_sensitive)) { + DEBUG(8,("is_in_path: mask match succeeded\n")); + return True; + } + } else { + if((case_sensitive && (strcmp(last_component, namelist->name) == 0))|| + (!case_sensitive && (strcasecmp_m(last_component, namelist->name) == 0))) { + DEBUG(8,("is_in_path: match succeeded\n")); + return True; + } + } + } + DEBUG(8,("is_in_path: match not found\n")); + return False; +} + +/******************************************************************* + Strip a '/' separated list into an array of + name_compare_enties structures suitable for + passing to is_in_path(). We do this for + speed so we can pre-parse all the names in the list + and don't do it for each call to is_in_path(). + We also check if the entry contains a wildcard to + remove a potentially expensive call to mask_match + if possible. +********************************************************************/ + +void set_namearray(name_compare_entry **ppname_array, const char *namelist_in) +{ + char *name_end; + char *namelist; + char *namelist_end; + char *nameptr; + int num_entries = 0; + int i; + + (*ppname_array) = NULL; + + if((namelist_in == NULL ) || ((namelist_in != NULL) && (*namelist_in == '\0'))) + return; + + namelist = talloc_strdup(talloc_tos(), namelist_in); + if (namelist == NULL) { + DEBUG(0,("set_namearray: talloc fail\n")); + return; + } + nameptr = namelist; + + namelist_end = &namelist[strlen(namelist)]; + + /* We need to make two passes over the string. The + first to count the number of elements, the second + to split it. + */ + + while(nameptr <= namelist_end) { + if ( *nameptr == '/' ) { + /* cope with multiple (useless) /s) */ + nameptr++; + continue; + } + /* anything left? */ + if ( *nameptr == '\0' ) + break; + + /* find the next '/' or consume remaining */ + name_end = strchr_m(nameptr, '/'); + if (name_end == NULL) { + /* Point nameptr at the terminating '\0' */ + nameptr += strlen(nameptr); + } else { + /* next segment please */ + nameptr = name_end + 1; + } + num_entries++; + } + + if(num_entries == 0) { + talloc_free(namelist); + return; + } + + if(( (*ppname_array) = SMB_MALLOC_ARRAY(name_compare_entry, num_entries + 1)) == NULL) { + DEBUG(0,("set_namearray: malloc fail\n")); + talloc_free(namelist); + return; + } + + /* Now copy out the names */ + nameptr = namelist; + i = 0; + while(nameptr <= namelist_end) { + if ( *nameptr == '/' ) { + /* cope with multiple (useless) /s) */ + nameptr++; + continue; + } + /* anything left? */ + if ( *nameptr == '\0' ) + break; + + /* find the next '/' or consume remaining */ + name_end = strchr_m(nameptr, '/'); + if (name_end != NULL) { + *name_end = '\0'; + } + + (*ppname_array)[i].is_wild = ms_has_wild(nameptr); + if(((*ppname_array)[i].name = SMB_STRDUP(nameptr)) == NULL) { + DEBUG(0,("set_namearray: malloc fail (1)\n")); + talloc_free(namelist); + return; + } + + if (name_end == NULL) { + /* Point nameptr at the terminating '\0' */ + nameptr += strlen(nameptr); + } else { + /* next segment please */ + nameptr = name_end + 1; + } + i++; + } + + (*ppname_array)[i].name = NULL; + + talloc_free(namelist); + return; +} + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_LOCKING + +/**************************************************************************** + Simple routine to query existing file locks. Cruft in NFS and 64->32 bit mapping + is dealt with in posix.c + Returns True if we have information regarding this lock region (and returns + F_UNLCK in *ptype if the region is unlocked). False if the call failed. +****************************************************************************/ + +bool fcntl_getlock(int fd, int op, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid) +{ + struct flock lock; + int ret; + + DEBUG(8,("fcntl_getlock fd=%d op=%d offset=%.0f count=%.0f type=%d\n", + fd,op,(double)*poffset,(double)*pcount,*ptype)); + + lock.l_type = *ptype; + lock.l_whence = SEEK_SET; + lock.l_start = *poffset; + lock.l_len = *pcount; + lock.l_pid = 0; + + ret = sys_fcntl_ptr(fd,op,&lock); + + if (ret == -1) { + int saved_errno = errno; + DEBUG(3,("fcntl_getlock: lock request failed at offset %.0f count %.0f type %d (%s)\n", + (double)*poffset,(double)*pcount,*ptype,strerror(errno))); + errno = saved_errno; + return False; + } + + *ptype = lock.l_type; + *poffset = lock.l_start; + *pcount = lock.l_len; + *ppid = lock.l_pid; + + DEBUG(3,("fcntl_getlock: fd %d is returned info %d pid %u\n", + fd, (int)lock.l_type, (unsigned int)lock.l_pid)); + return True; +} + +#if defined(HAVE_OFD_LOCKS) +int map_process_lock_to_ofd_lock(int op) +{ + switch (op) { + case F_GETLK: + case F_OFD_GETLK: + op = F_OFD_GETLK; + break; + case F_SETLK: + case F_OFD_SETLK: + op = F_OFD_SETLK; + break; + case F_SETLKW: + case F_OFD_SETLKW: + op = F_OFD_SETLKW; + break; + default: + return -1; + } + return op; +} +#else /* HAVE_OFD_LOCKS */ +int map_process_lock_to_ofd_lock(int op) +{ + return op; +} +#endif /* HAVE_OFD_LOCKS */ + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_ALL + +/******************************************************************* + Is the name specified one of my netbios names. + Returns true if it is equal, false otherwise. +********************************************************************/ + +static bool nb_name_equal(const char *s1, const char *s2) +{ + int cmp = strncasecmp_m(s1, s2, MAX_NETBIOSNAME_LEN-1); + return (cmp == 0); +} + +bool is_myname(const char *s) +{ + const char **aliases = NULL; + bool ok = false; + + ok = nb_name_equal(lp_netbios_name(), s); + if (ok) { + goto done; + } + + aliases = lp_netbios_aliases(); + if (aliases == NULL) { + goto done; + } + + while (*aliases != NULL) { + ok = nb_name_equal(*aliases, s); + if (ok) { + goto done; + } + aliases += 1; + } + +done: + DBG_DEBUG("is_myname(\"%s\") returns %d\n", s, (int)ok); + return ok; +} + +/******************************************************************* + we distinguish between 2K and XP by the "Native Lan Manager" string + WinXP => "Windows 2002 5.1" + WinXP 64bit => "Windows XP 5.2" + Win2k => "Windows 2000 5.0" + NT4 => "Windows NT 4.0" + Win9x => "Windows 4.0" + Windows 2003 doesn't set the native lan manager string but + they do set the domain to "Windows 2003 5.2" (probably a bug). +********************************************************************/ + +void ra_lanman_string( const char *native_lanman ) +{ + if ( strcmp( native_lanman, "Windows 2002 5.1" ) == 0 ) + set_remote_arch( RA_WINXP ); + else if ( strcmp( native_lanman, "Windows XP 5.2" ) == 0 ) + set_remote_arch( RA_WINXP64 ); + else if ( strcmp( native_lanman, "Windows Server 2003 5.2" ) == 0 ) + set_remote_arch( RA_WIN2K3 ); +} + +static const char *remote_arch_strings[] = { + [RA_UNKNOWN] = "UNKNOWN", + [RA_WFWG] = "WfWg", + [RA_OS2] = "OS2", + [RA_WIN95] = "Win95", + [RA_WINNT] = "WinNT", + [RA_WIN2K] = "Win2K", + [RA_WINXP] = "WinXP", + [RA_WIN2K3] = "Win2K3", + [RA_VISTA] = "Vista", + [RA_SAMBA] = "Samba", + [RA_CIFSFS] = "CIFSFS", + [RA_WINXP64] = "WinXP64", + [RA_OSX] = "OSX", +}; + +const char *get_remote_arch_str(void) +{ + if (ra_type >= ARRAY_SIZE(remote_arch_strings)) { + /* + * set_remote_arch() already checks this so ra_type + * should be in the allowed range, but anyway, let's + * do another bound check here. + */ + DBG_ERR("Remote arch info out of sync [%d] missing\n", ra_type); + ra_type = RA_UNKNOWN; + } + return remote_arch_strings[ra_type]; +} + +enum remote_arch_types get_remote_arch_from_str(const char *remote_arch_string) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(remote_arch_strings); i++) { + if (strcmp(remote_arch_string, remote_arch_strings[i]) == 0) { + return i; + } + } + return RA_UNKNOWN; +} + +/******************************************************************* + Set the horrid remote_arch string based on an enum. +********************************************************************/ + +void set_remote_arch(enum remote_arch_types type) +{ + if (ra_type >= ARRAY_SIZE(remote_arch_strings)) { + /* + * This protects against someone adding values to enum + * remote_arch_types without updating + * remote_arch_strings array. + */ + DBG_ERR("Remote arch info out of sync [%d] missing\n", ra_type); + ra_type = RA_UNKNOWN; + return; + } + + ra_type = type; + DEBUG(10,("set_remote_arch: Client arch is \'%s\'\n", + get_remote_arch_str())); +} + +/******************************************************************* + Get the remote_arch type. +********************************************************************/ + +enum remote_arch_types get_remote_arch(void) +{ + return ra_type; +} + +#define RA_CACHE_TTL 7*24*3600 + +static bool remote_arch_cache_key(const struct GUID *client_guid, + fstring key) +{ + struct GUID_txt_buf guid_buf; + const char *guid_string = NULL; + + guid_string = GUID_buf_string(client_guid, &guid_buf); + if (guid_string == NULL) { + return false; + } + + fstr_sprintf(key, "RA/%s", guid_string); + return true; +} + +struct ra_parser_state { + bool found; + enum remote_arch_types ra; +}; + +static void ra_parser(const struct gencache_timeout *t, + DATA_BLOB blob, + void *priv_data) +{ + struct ra_parser_state *state = (struct ra_parser_state *)priv_data; + const char *ra_str = NULL; + + if (gencache_timeout_expired(t)) { + return; + } + + if ((blob.length == 0) || (blob.data[blob.length-1] != '\0')) { + DBG_ERR("Remote arch cache key not a string\n"); + return; + } + + ra_str = (const char *)blob.data; + DBG_INFO("Got remote arch [%s] from cache\n", ra_str); + + state->ra = get_remote_arch_from_str(ra_str); + state->found = true; + return; +} + +static bool remote_arch_cache_get(const struct GUID *client_guid) +{ + bool ok; + fstring ra_key; + struct ra_parser_state state = (struct ra_parser_state) { + .found = false, + .ra = RA_UNKNOWN, + }; + + ok = remote_arch_cache_key(client_guid, ra_key); + if (!ok) { + return false; + } + + ok = gencache_parse(ra_key, ra_parser, &state); + if (!ok || !state.found) { + return true; + } + + if (state.ra == RA_UNKNOWN) { + return true; + } + + set_remote_arch(state.ra); + return true; +} + +static bool remote_arch_cache_set(const struct GUID *client_guid) +{ + bool ok; + fstring ra_key; + const char *ra_str = NULL; + + if (get_remote_arch() == RA_UNKNOWN) { + return true; + } + + ok = remote_arch_cache_key(client_guid, ra_key); + if (!ok) { + return false; + } + + ra_str = get_remote_arch_str(); + if (ra_str == NULL) { + return false; + } + + ok = gencache_set(ra_key, ra_str, time(NULL) + RA_CACHE_TTL); + if (!ok) { + return false; + } + + return true; +} + +bool remote_arch_cache_update(const struct GUID *client_guid) +{ + bool ok; + + if (get_remote_arch() == RA_UNKNOWN) { + + become_root(); + ok = remote_arch_cache_get(client_guid); + unbecome_root(); + + return ok; + } + + become_root(); + ok = remote_arch_cache_set(client_guid); + unbecome_root(); + + return ok; +} + +bool remote_arch_cache_delete(const struct GUID *client_guid) +{ + bool ok; + fstring ra_key; + + ok = remote_arch_cache_key(client_guid, ra_key); + if (!ok) { + return false; + } + + become_root(); + ok = gencache_del(ra_key); + unbecome_root(); + + if (!ok) { + return false; + } + + return true; +} + + +/***************************************************************************** + Provide a checksum on a string + + Input: s - the null-terminated character string for which the checksum + will be calculated. + + Output: The checksum value calculated for s. +*****************************************************************************/ + +int str_checksum(const char *s) +{ + TDB_DATA key; + if (s == NULL) + return 0; + + key = (TDB_DATA) { .dptr = discard_const_p(uint8_t, s), + .dsize = strlen(s) }; + + return tdb_jenkins_hash(&key); +} + +/***************************************************************** + Zero a memory area then free it. Used to catch bugs faster. +*****************************************************************/ + +void zero_free(void *p, size_t size) +{ + memset(p, 0, size); + SAFE_FREE(p); +} + +/***************************************************************** + Set our open file limit to a requested max and return the limit. +*****************************************************************/ + +int set_maxfiles(int requested_max) +{ +#if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)) + struct rlimit rlp; + int saved_current_limit; + + if(getrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(0,("set_maxfiles: getrlimit (1) for RLIMIT_NOFILE failed with error %s\n", + strerror(errno) )); + /* just guess... */ + return requested_max; + } + + /* + * Set the fd limit to be real_max_open_files + MAX_OPEN_FUDGEFACTOR to + * account for the extra fd we need + * as well as the log files and standard + * handles etc. Save the limit we want to set in case + * we are running on an OS that doesn't support this limit (AIX) + * which always returns RLIM_INFINITY for rlp.rlim_max. + */ + + /* Try raising the hard (max) limit to the requested amount. */ + +#if defined(RLIM_INFINITY) + if (rlp.rlim_max != RLIM_INFINITY) { + int orig_max = rlp.rlim_max; + + if ( rlp.rlim_max < requested_max ) + rlp.rlim_max = requested_max; + + /* This failing is not an error - many systems (Linux) don't + support our default request of 10,000 open files. JRA. */ + + if(setrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(3,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d max files failed with error %s\n", + (int)rlp.rlim_max, strerror(errno) )); + + /* Set failed - restore original value from get. */ + rlp.rlim_max = orig_max; + } + } +#endif + + /* Now try setting the soft (current) limit. */ + + saved_current_limit = rlp.rlim_cur = MIN(requested_max,rlp.rlim_max); + + if(setrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(0,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d files failed with error %s\n", + (int)rlp.rlim_cur, strerror(errno) )); + /* just guess... */ + return saved_current_limit; + } + + if(getrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(0,("set_maxfiles: getrlimit (2) for RLIMIT_NOFILE failed with error %s\n", + strerror(errno) )); + /* just guess... */ + return saved_current_limit; + } + +#if defined(RLIM_INFINITY) + if(rlp.rlim_cur == RLIM_INFINITY) + return saved_current_limit; +#endif + + if((int)rlp.rlim_cur > saved_current_limit) + return saved_current_limit; + + return rlp.rlim_cur; +#else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */ + /* + * No way to know - just guess... + */ + return requested_max; +#endif +} + +/***************************************************************** + malloc that aborts with smb_panic on fail or zero size. + *****************************************************************/ + +void *smb_xmalloc_array(size_t size, unsigned int count) +{ + void *p; + if (size == 0) { + smb_panic("smb_xmalloc_array: called with zero size"); + } + if (count >= MAX_ALLOC_SIZE/size) { + smb_panic("smb_xmalloc_array: alloc size too large"); + } + if ((p = SMB_MALLOC(size*count)) == NULL) { + DEBUG(0, ("smb_xmalloc_array failed to allocate %lu * %lu bytes\n", + (unsigned long)size, (unsigned long)count)); + smb_panic("smb_xmalloc_array: malloc failed"); + } + return p; +} + +/***************************************************************** + Get local hostname and cache result. +*****************************************************************/ + +char *myhostname(void) +{ + static char *ret; + if (ret == NULL) { + ret = get_myname(NULL); + } + return ret; +} + +/***************************************************************** + Get local hostname and cache result. +*****************************************************************/ + +char *myhostname_upper(void) +{ + static char *ret; + if (ret == NULL) { + char *name = get_myname(NULL); + if (name == NULL) { + return NULL; + } + ret = strupper_talloc(NULL, name); + talloc_free(name); + } + return ret; +} + +/******************************************************************* + Given a filename - get its directory name +********************************************************************/ + +bool parent_dirname(TALLOC_CTX *mem_ctx, const char *dir, char **parent, + const char **name) +{ + char *p; + ptrdiff_t len; + + p = strrchr_m(dir, '/'); /* Find final '/', if any */ + + if (p == NULL) { + if (!(*parent = talloc_strdup(mem_ctx, "."))) { + return False; + } + if (name) { + *name = dir; + } + return True; + } + + len = p-dir; + + *parent = talloc_strndup(mem_ctx, dir, len); + if (*parent == NULL) { + return False; + } + + if (name) { + *name = p+1; + } + return True; +} + +/******************************************************************* + Determine if a pattern contains any Microsoft wildcard characters. +*******************************************************************/ + +bool ms_has_wild(const char *s) +{ + const char *found = strpbrk(s, "*?<>\""); + return (found != NULL); +} + +bool ms_has_wild_w(const smb_ucs2_t *s) +{ + smb_ucs2_t c; + if (!s) return False; + while ((c = *s++)) { + switch (c) { + case UCS2_CHAR('*'): + case UCS2_CHAR('?'): + case UCS2_CHAR('<'): + case UCS2_CHAR('>'): + case UCS2_CHAR('"'): + return True; + } + } + return False; +} + +/******************************************************************* + A wrapper that handles case sensitivity and the special handling + of the ".." name. +*******************************************************************/ + +bool mask_match(const char *string, const char *pattern, bool is_case_sensitive) +{ + if (ISDOTDOT(string)) + string = "."; + if (ISDOT(pattern)) + return False; + + return ms_fnmatch_protocol(pattern, string, Protocol, is_case_sensitive) == 0; +} + +/******************************************************************* + A wrapper that handles a list of patterns and calls mask_match() + on each. Returns True if any of the patterns match. +*******************************************************************/ + +bool mask_match_list(const char *string, char **list, int listLen, bool is_case_sensitive) +{ + while (listLen-- > 0) { + if (mask_match(string, *list++, is_case_sensitive)) + return True; + } + return False; +} + +/********************************************************************** + Converts a name to a fully qualified domain name. + Returns true if lookup succeeded, false if not (then fqdn is set to name) + Uses getaddrinfo() with AI_CANONNAME flag to obtain the official + canonical name of the host. getaddrinfo() may use a variety of sources + including /etc/hosts to obtain the domainname. It expects aliases in + /etc/hosts to NOT be the FQDN. The FQDN should come first. +************************************************************************/ + +bool name_to_fqdn(fstring fqdn, const char *name) +{ + char *full = NULL; + struct addrinfo hints; + struct addrinfo *result; + int s; + + /* Configure hints to obtain canonical name */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_CANONNAME; /* Get host's FQDN */ + hints.ai_protocol = 0; /* Any protocol */ + + s = getaddrinfo(name, NULL, &hints, &result); + if (s != 0) { + DBG_WARNING("getaddrinfo lookup for %s failed: %s\n", + name, + gai_strerror(s)); + fstrcpy(fqdn, name); + return false; + } + full = result->ai_canonname; + + /* Find out if the FQDN is returned as an alias + * to cope with /etc/hosts files where the first + * name is not the FQDN but the short name. + * getaddrinfo provides no easy way of handling aliases + * in /etc/hosts. Users should make sure the FQDN + * comes first in /etc/hosts. */ + if (full && (! strchr_m(full, '.'))) { + DEBUG(1, ("WARNING: your /etc/hosts file may be broken!\n")); + DEBUGADD(1, (" Full qualified domain names (FQDNs) should not be specified\n")); + DEBUGADD(1, (" as an alias in /etc/hosts. FQDN should be the first name\n")); + DEBUGADD(1, (" prior to any aliases.\n")); + } + if (full && (strcasecmp_m(full, "localhost.localdomain") == 0)) { + DEBUG(1, ("WARNING: your /etc/hosts file may be broken!\n")); + DEBUGADD(1, (" Specifying the machine hostname for address 127.0.0.1 may lead\n")); + DEBUGADD(1, (" to Kerberos authentication problems as localhost.localdomain\n")); + DEBUGADD(1, (" may end up being used instead of the real machine FQDN.\n")); + } + + DEBUG(10,("name_to_fqdn: lookup for %s -> %s.\n", name, full)); + fstrcpy(fqdn, full); + freeaddrinfo(result); /* No longer needed */ + return true; +} + +struct server_id interpret_pid(const char *pid_string) +{ + return server_id_from_string(get_my_vnn(), pid_string); +} + +/**************************************************************** + Check if an offset into a buffer is safe. + If this returns True it's safe to indirect into the byte at + pointer ptr+off. +****************************************************************/ + +bool is_offset_safe(const char *buf_base, size_t buf_len, char *ptr, size_t off) +{ + const char *end_base = buf_base + buf_len; + char *end_ptr = ptr + off; + + if (!buf_base || !ptr) { + return False; + } + + if (end_base < buf_base || end_ptr < ptr) { + return False; /* wrap. */ + } + + if (end_ptr < end_base) { + return True; + } + return False; +} + +/**************************************************************** + Return a safe pointer into a string within a buffer, or NULL. +****************************************************************/ + +char *get_safe_str_ptr(const char *buf_base, size_t buf_len, char *ptr, size_t off) +{ + if (!is_offset_safe(buf_base, buf_len, ptr, off)) { + return NULL; + } + /* Check if a valid string exists at this offset. */ + if (skip_string(buf_base,buf_len, ptr + off) == NULL) { + return NULL; + } + return ptr + off; +} + + +/**************************************************************** + Split DOM\user into DOM and user. Do not mix with winbind variants of that + call (they take care of winbind separator and other winbind specific settings). +****************************************************************/ + +bool split_domain_user(TALLOC_CTX *mem_ctx, + const char *full_name, + char **domain, + char **user) +{ + const char *p = NULL; + + p = strchr_m(full_name, '\\'); + + if (p != NULL) { + *domain = talloc_strndup(mem_ctx, full_name, + PTR_DIFF(p, full_name)); + if (*domain == NULL) { + return false; + } + *user = talloc_strdup(mem_ctx, p+1); + if (*user == NULL) { + TALLOC_FREE(*domain); + return false; + } + } else { + *domain = NULL; + *user = talloc_strdup(mem_ctx, full_name); + if (*user == NULL) { + return false; + } + } + + return true; +} + +/**************************************************************** + strip off leading '\\' from a hostname +****************************************************************/ + +const char *strip_hostname(const char *s) +{ + if (!s) { + return NULL; + } + + if (strlen_m(s) < 3) { + return s; + } + + if (s[0] == '\\') s++; + if (s[0] == '\\') s++; + + return s; +} + +bool any_nt_status_not_ok(NTSTATUS err1, NTSTATUS err2, NTSTATUS *result) +{ + if (!NT_STATUS_IS_OK(err1)) { + *result = err1; + return true; + } + if (!NT_STATUS_IS_OK(err2)) { + *result = err2; + return true; + } + return false; +} + +int timeval_to_msec(struct timeval t) +{ + return t.tv_sec * 1000 + (t.tv_usec+999) / 1000; +} + +/******************************************************************* + Check a given DOS pathname is valid for a share. +********************************************************************/ + +char *valid_share_pathname(TALLOC_CTX *ctx, const char *dos_pathname) +{ + char *ptr = NULL; + + if (!dos_pathname) { + return NULL; + } + + ptr = talloc_strdup(ctx, dos_pathname); + if (!ptr) { + return NULL; + } + /* Convert any '\' paths to '/' */ + unix_format(ptr); + ptr = unix_clean_name(ctx, ptr); + if (!ptr) { + return NULL; + } + + /* NT is braindead - it wants a C: prefix to a pathname ! So strip it. */ + if (strlen(ptr) > 2 && ptr[1] == ':' && ptr[0] != '/') + ptr += 2; + + /* Only absolute paths allowed. */ + if (*ptr != '/') + return NULL; + + return ptr; +} + +/******************************************************************* + Return True if the filename is one of the special executable types. +********************************************************************/ + +bool is_executable(const char *fname) +{ + if ((fname = strrchr_m(fname,'.'))) { + if (strequal(fname,".com") || + strequal(fname,".dll") || + strequal(fname,".exe") || + strequal(fname,".sym")) { + return True; + } + } + return False; +} + +/**************************************************************************** + Open a file with a share mode - old openX method - map into NTCreate. +****************************************************************************/ + +bool map_open_params_to_ntcreate(const char *smb_base_fname, + int deny_mode, int open_func, + uint32_t *paccess_mask, + uint32_t *pshare_mode, + uint32_t *pcreate_disposition, + uint32_t *pcreate_options, + uint32_t *pprivate_flags) +{ + uint32_t access_mask; + uint32_t share_mode; + uint32_t create_disposition; + uint32_t create_options = FILE_NON_DIRECTORY_FILE; + uint32_t private_flags = 0; + + DEBUG(10,("map_open_params_to_ntcreate: fname = %s, deny_mode = 0x%x, " + "open_func = 0x%x\n", + smb_base_fname, (unsigned int)deny_mode, + (unsigned int)open_func )); + + /* Create the NT compatible access_mask. */ + switch (GET_OPENX_MODE(deny_mode)) { + case DOS_OPEN_EXEC: /* Implies read-only - used to be FILE_READ_DATA */ + case DOS_OPEN_RDONLY: + access_mask = FILE_GENERIC_READ; + break; + case DOS_OPEN_WRONLY: + access_mask = FILE_GENERIC_WRITE; + break; + case DOS_OPEN_RDWR: + case DOS_OPEN_FCB: + access_mask = FILE_GENERIC_READ|FILE_GENERIC_WRITE; + break; + default: + DEBUG(10,("map_open_params_to_ntcreate: bad open mode = 0x%x\n", + (unsigned int)GET_OPENX_MODE(deny_mode))); + return False; + } + + /* Create the NT compatible create_disposition. */ + switch (open_func) { + case OPENX_FILE_EXISTS_FAIL|OPENX_FILE_CREATE_IF_NOT_EXIST: + create_disposition = FILE_CREATE; + break; + + case OPENX_FILE_EXISTS_OPEN: + create_disposition = FILE_OPEN; + break; + + case OPENX_FILE_EXISTS_OPEN|OPENX_FILE_CREATE_IF_NOT_EXIST: + create_disposition = FILE_OPEN_IF; + break; + + case OPENX_FILE_EXISTS_TRUNCATE: + create_disposition = FILE_OVERWRITE; + break; + + case OPENX_FILE_EXISTS_TRUNCATE|OPENX_FILE_CREATE_IF_NOT_EXIST: + create_disposition = FILE_OVERWRITE_IF; + break; + + default: + /* From samba4 - to be confirmed. */ + if (GET_OPENX_MODE(deny_mode) == DOS_OPEN_EXEC) { + create_disposition = FILE_CREATE; + break; + } + DEBUG(10,("map_open_params_to_ntcreate: bad " + "open_func 0x%x\n", (unsigned int)open_func)); + return False; + } + + /* Create the NT compatible share modes. */ + switch (GET_DENY_MODE(deny_mode)) { + case DENY_ALL: + share_mode = FILE_SHARE_NONE; + break; + + case DENY_WRITE: + share_mode = FILE_SHARE_READ; + break; + + case DENY_READ: + share_mode = FILE_SHARE_WRITE; + break; + + case DENY_NONE: + share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + break; + + case DENY_DOS: + private_flags |= NTCREATEX_FLAG_DENY_DOS; + if (is_executable(smb_base_fname)) { + share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + } else { + if (GET_OPENX_MODE(deny_mode) == DOS_OPEN_RDONLY) { + share_mode = FILE_SHARE_READ; + } else { + share_mode = FILE_SHARE_NONE; + } + } + break; + + case DENY_FCB: + private_flags |= NTCREATEX_FLAG_DENY_FCB; + share_mode = FILE_SHARE_NONE; + break; + + default: + DEBUG(10,("map_open_params_to_ntcreate: bad deny_mode 0x%x\n", + (unsigned int)GET_DENY_MODE(deny_mode) )); + return False; + } + + DEBUG(10,("map_open_params_to_ntcreate: file %s, access_mask = 0x%x, " + "share_mode = 0x%x, create_disposition = 0x%x, " + "create_options = 0x%x private_flags = 0x%x\n", + smb_base_fname, + (unsigned int)access_mask, + (unsigned int)share_mode, + (unsigned int)create_disposition, + (unsigned int)create_options, + (unsigned int)private_flags)); + + if (paccess_mask) { + *paccess_mask = access_mask; + } + if (pshare_mode) { + *pshare_mode = share_mode; + } + if (pcreate_disposition) { + *pcreate_disposition = create_disposition; + } + if (pcreate_options) { + *pcreate_options = create_options; + } + if (pprivate_flags) { + *pprivate_flags = private_flags; + } + + return True; + +} + +/************************************************************************* + Return a talloced copy of a struct security_unix_token. NULL on fail. +*************************************************************************/ + +struct security_unix_token *copy_unix_token(TALLOC_CTX *ctx, const struct security_unix_token *tok) +{ + struct security_unix_token *cpy; + + cpy = talloc(ctx, struct security_unix_token); + if (!cpy) { + return NULL; + } + + cpy->uid = tok->uid; + cpy->gid = tok->gid; + cpy->ngroups = tok->ngroups; + if (tok->ngroups) { + /* Make this a talloc child of cpy. */ + cpy->groups = (gid_t *)talloc_memdup( + cpy, tok->groups, tok->ngroups * sizeof(gid_t)); + if (!cpy->groups) { + TALLOC_FREE(cpy); + return NULL; + } + } else { + cpy->groups = NULL; + } + return cpy; +} + +/**************************************************************************** + Return a root token +****************************************************************************/ + +struct security_unix_token *root_unix_token(TALLOC_CTX *mem_ctx) +{ + struct security_unix_token *t = NULL; + + t = talloc_zero(mem_ctx, struct security_unix_token); + if (t == NULL) { + return NULL; + } + + /* + * This is not needed, but lets make it explicit, not implicit. + */ + *t = (struct security_unix_token) { + .uid = 0, + .gid = 0, + .ngroups = 0, + .groups = NULL + }; + + return t; +} + +char *utok_string(TALLOC_CTX *mem_ctx, const struct security_unix_token *tok) +{ + char *str; + uint32_t i; + + str = talloc_asprintf( + mem_ctx, + "uid=%ju, gid=%ju, %"PRIu32" groups:", + (uintmax_t)(tok->uid), + (uintmax_t)(tok->gid), + tok->ngroups); + + for (i=0; i<tok->ngroups; i++) { + talloc_asprintf_addbuf( + &str, " %ju", (uintmax_t)tok->groups[i]); + } + + return str; +} + +/**************************************************************************** + Check that a file matches a particular file type. +****************************************************************************/ + +bool dir_check_ftype(uint32_t mode, uint32_t dirtype) +{ + uint32_t mask; + + /* Check the "may have" search bits. */ + if (((mode & ~dirtype) & + (FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_DIRECTORY)) != 0) { + return false; + } + + /* Check the "must have" bits, + which are the may have bits shifted eight */ + /* If must have bit is set, the file/dir can + not be returned in search unless the matching + file attribute is set */ + mask = ((dirtype >> 8) & (FILE_ATTRIBUTE_DIRECTORY| + FILE_ATTRIBUTE_ARCHIVE| + FILE_ATTRIBUTE_READONLY| + FILE_ATTRIBUTE_HIDDEN| + FILE_ATTRIBUTE_SYSTEM)); /* & 0x37 */ + if(mask) { + if((mask & (mode & (FILE_ATTRIBUTE_DIRECTORY| + FILE_ATTRIBUTE_ARCHIVE| + FILE_ATTRIBUTE_READONLY| + FILE_ATTRIBUTE_HIDDEN| + FILE_ATTRIBUTE_SYSTEM))) == mask) { + /* check if matching attribute present */ + return true; + } else { + return false; + } + } + + return true; +} diff --git a/source3/lib/util_builtin.c b/source3/lib/util_builtin.c new file mode 100644 index 0000000..a456218 --- /dev/null +++ b/source3/lib/util_builtin.c @@ -0,0 +1,158 @@ +/* + Unix SMB/CIFS implementation. + Translate BUILTIN names to SIDs and vice versa + Copyright (C) Volker Lendecke 2005 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/security/security.h" + +struct rid_name_map { + uint32_t rid; + const char *name; +}; + +static const struct rid_name_map builtin_aliases[] = { + { BUILTIN_RID_ADMINISTRATORS, "Administrators" }, + { BUILTIN_RID_USERS, "Users" }, + { BUILTIN_RID_GUESTS, "Guests" }, + { BUILTIN_RID_POWER_USERS, "Power Users" }, + { BUILTIN_RID_ACCOUNT_OPERATORS, "Account Operators" }, + { BUILTIN_RID_SERVER_OPERATORS, "Server Operators" }, + { BUILTIN_RID_PRINT_OPERATORS, "Print Operators" }, + { BUILTIN_RID_BACKUP_OPERATORS, "Backup Operators" }, + { BUILTIN_RID_REPLICATOR, "Replicator" }, + { BUILTIN_RID_RAS_SERVERS, "RAS Servers" }, + { BUILTIN_RID_PRE_2K_ACCESS, + "Pre-Windows 2000 Compatible Access" }, + { BUILTIN_RID_REMOTE_DESKTOP_USERS, + "Remote Desktop Users" }, + { BUILTIN_RID_NETWORK_CONF_OPERATORS, + "Network Configuration Operators" }, + { BUILTIN_RID_INCOMING_FOREST_TRUST, + "Incoming Forest Trust Builders" }, + { BUILTIN_RID_PERFMON_USERS, + "Performance Monitor Users" }, + { BUILTIN_RID_PERFLOG_USERS, + "Performance Log Users" }, + { BUILTIN_RID_AUTH_ACCESS, + "Windows Authorization Access Group" }, + { BUILTIN_RID_TS_LICENSE_SERVERS, + "Terminal Server License Servers" }, + { BUILTIN_RID_DISTRIBUTED_COM_USERS, + "Distributed COM Users" }, + { BUILTIN_RID_CRYPTO_OPERATORS, + "Cryptographic Operators" }, + { BUILTIN_RID_EVENT_LOG_READERS, + "Event Log Readers" }, + { BUILTIN_RID_CERT_SERV_DCOM_ACCESS, + "Certificate Service DCOM Access" }, + { 0, NULL}}; + +/******************************************************************* + Look up a rid in the BUILTIN domain + ********************************************************************/ +bool lookup_builtin_rid(TALLOC_CTX *mem_ctx, uint32_t rid, const char **name) +{ + const struct rid_name_map *aliases = builtin_aliases; + + while (aliases->name != NULL) { + if (rid == aliases->rid) { + *name = talloc_strdup(mem_ctx, aliases->name); + return True; + } + aliases++; + } + + return False; +} + +/******************************************************************* + Look up a name in the BUILTIN domain + ********************************************************************/ +bool lookup_builtin_name(const char *name, uint32_t *rid) +{ + const struct rid_name_map *aliases = builtin_aliases; + + while (aliases->name != NULL) { + if (strequal(name, aliases->name)) { + *rid = aliases->rid; + return True; + } + aliases++; + } + + return False; +} + +/***************************************************************** + Return the name of the BUILTIN domain +*****************************************************************/ + +const char *builtin_domain_name(void) +{ + return "BUILTIN"; +} + +/***************************************************************** + Check if the SID is the builtin SID (S-1-5-32). +*****************************************************************/ + +bool sid_check_is_builtin(const struct dom_sid *sid) +{ + return dom_sid_equal(sid, &global_sid_Builtin); +} + +/***************************************************************** + Check if the SID is one of the builtin SIDs (S-1-5-32-a). +*****************************************************************/ + +bool sid_check_is_in_builtin(const struct dom_sid *sid) +{ + struct dom_sid dom_sid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, NULL); + + return sid_check_is_builtin(&dom_sid); +} + +/******************************************************************** + Check if the SID is one of the well-known builtin SIDs (S-1-5-32-x) +*********************************************************************/ + +bool sid_check_is_wellknown_builtin(const struct dom_sid *sid) +{ + struct dom_sid dom_sid; + const struct rid_name_map *aliases = builtin_aliases; + uint32_t rid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, &rid); + + if (!sid_check_is_builtin(&dom_sid)) { + return false; + } + + while (aliases->name != NULL) { + if (aliases->rid == rid) { + return True; + } + aliases++; + } + + return False; +} diff --git a/source3/lib/util_cluster.c b/source3/lib/util_cluster.c new file mode 100644 index 0000000..4287c2c --- /dev/null +++ b/source3/lib/util_cluster.c @@ -0,0 +1,42 @@ +/* + * Unix SMB/CIFS implementation. + * cluster utility functions + * Copyright (C) Volker Lendecke 2013 + * Copyright (C) Michael Adam 2013 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "ctdbd_conn.h" +#include "util_cluster.h" +#include "lib/cluster_support.h" +#include "lib/util/debug.h" +#include "source3/param/param_proto.h" + +bool cluster_probe_ok(void) +{ + if (lp_clustering()) { + int ret; + + ret = ctdbd_probe(lp_ctdbd_socket(), lp_ctdb_timeout()); + if (ret != 0) { + DEBUG(0, ("clustering=yes but ctdbd connect failed: " + "%s\n", strerror(ret))); + return false; + } + } + + return true; +} diff --git a/source3/lib/util_cluster.h b/source3/lib/util_cluster.h new file mode 100644 index 0000000..6d05987 --- /dev/null +++ b/source3/lib/util_cluster.h @@ -0,0 +1,27 @@ +/* + * Unix SMB/CIFS implementation. + * cluster utility functions + * Copyright (C) Volker Lendecke 2013 + * Copyright (C) Michael Adam 2013 + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef __UTIL_CLUSTER_H__ +#define __UTIL_CLUSTER_H__ + +bool cluster_probe_ok(void); + +#endif /* __UTIL_CLUSTER_H__ */ diff --git a/source3/lib/util_ea.c b/source3/lib/util_ea.c new file mode 100644 index 0000000..dd48e77 --- /dev/null +++ b/source3/lib/util_ea.c @@ -0,0 +1,126 @@ +/* + Unix SMB/CIFS implementation. + SMB Extended attribute buffer handling + Copyright (C) Jeremy Allison 2005-2013 + Copyright (C) Tim Prouty 2008 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util_ea.h" + +/**************************************************************************** + Read one EA list entry from the buffer. +****************************************************************************/ + +struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used) +{ + struct ea_list *eal = talloc_zero(ctx, struct ea_list); + uint16_t val_len; + unsigned int namelen; + size_t converted_size; + + if (!eal) { + return NULL; + } + + if (data_size < 6) { + return NULL; + } + + eal->ea.flags = CVAL(pdata,0); + namelen = CVAL(pdata,1); + val_len = SVAL(pdata,2); + + if (4 + namelen + 1 + val_len > data_size) { + return NULL; + } + + /* Ensure the name is null terminated. */ + if (pdata[namelen + 4] != '\0') { + return NULL; + } + if (!pull_ascii_talloc(ctx, &eal->ea.name, pdata + 4, &converted_size)) { + DEBUG(0,("read_ea_list_entry: pull_ascii_talloc failed: %s\n", + strerror(errno))); + } + if (!eal->ea.name) { + return NULL; + } + + eal->ea.value = data_blob_talloc(eal, NULL, (size_t)val_len + 1); + if (!eal->ea.value.data) { + return NULL; + } + + memcpy(eal->ea.value.data, pdata + 4 + namelen + 1, val_len); + + /* Ensure we're null terminated just in case we print the value. */ + eal->ea.value.data[val_len] = '\0'; + /* But don't count the null. */ + eal->ea.value.length--; + + if (pbytes_used) { + *pbytes_used = 4 + namelen + 1 + val_len; + } + + DEBUG(10,("read_ea_list_entry: read ea name %s\n", eal->ea.name)); + dump_data(10, eal->ea.value.data, eal->ea.value.length); + + return eal; +} + +/**************************************************************************** + Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. +****************************************************************************/ + +struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) +{ + struct ea_list *ea_list_head = NULL; + size_t offset = 0; + + if (data_size < 4) { + return NULL; + } + + while (offset + 4 <= data_size) { + size_t next_offset = IVAL(pdata,offset); + struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset + 4, data_size - offset - 4, NULL); + + if (!eal) { + return NULL; + } + + DLIST_ADD_END(ea_list_head, eal); + if (next_offset == 0) { + break; + } + + /* Integer wrap protection for the increment. */ + if (offset + next_offset < offset) { + break; + } + + offset += next_offset; + + /* Integer wrap protection for while loop. */ + if (offset + 4 < offset) { + break; + } + + } + + return ea_list_head; +} diff --git a/source3/lib/util_ea.h b/source3/lib/util_ea.h new file mode 100644 index 0000000..54423ac --- /dev/null +++ b/source3/lib/util_ea.h @@ -0,0 +1,36 @@ +/* + Unix SMB/CIFS implementation. + SMB Extended attribute buffer handling + Copyright (C) Jeremy Allison 2005-2013 + Copyright (C) Tim Prouty 2008 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __LIB_UTIL_EA_H__ +#define __LIB_UTIL_EA_H__ + +/**************************************************************************** + Read one EA list entry from a buffer. +****************************************************************************/ + +struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used); + +/**************************************************************************** + Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. +****************************************************************************/ + +struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size); + +#endif /* __LIB_UTIL_EA_H__ */ diff --git a/source3/lib/util_event.c b/source3/lib/util_event.c new file mode 100644 index 0000000..ea7e7b7 --- /dev/null +++ b/source3/lib/util_event.c @@ -0,0 +1,102 @@ +/* + Unix SMB/CIFS implementation. + Timed event library. + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Volker Lendecke 2005-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "util_event.h" + +struct idle_event { + struct tevent_timer *te; + struct timeval interval; + char *name; + bool (*handler)(const struct timeval *now, void *private_data); + void *private_data; +}; + +static void smbd_idle_event_handler(struct tevent_context *ctx, + struct tevent_timer *te, + struct timeval now, + void *private_data) +{ + struct idle_event *event = + talloc_get_type_abort(private_data, struct idle_event); + + TALLOC_FREE(event->te); + + DEBUG(10,("smbd_idle_event_handler: %s %p called\n", + event->name, event->te)); + + if (!event->handler(&now, event->private_data)) { + DEBUG(10,("smbd_idle_event_handler: %s %p stopped\n", + event->name, event->te)); + /* Don't repeat, delete ourselves */ + TALLOC_FREE(event); + return; + } + + DEBUG(10,("smbd_idle_event_handler: %s %p rescheduled\n", + event->name, event->te)); + + event->te = tevent_add_timer(ctx, event, + timeval_sum(&now, &event->interval), + smbd_idle_event_handler, event); + + /* We can't do much but fail here. */ + SMB_ASSERT(event->te != NULL); +} + +struct idle_event *event_add_idle(struct tevent_context *event_ctx, + TALLOC_CTX *mem_ctx, + struct timeval interval, + const char *name, + bool (*handler)(const struct timeval *now, + void *private_data), + void *private_data) +{ + struct idle_event *result; + struct timeval now = timeval_current(); + + result = talloc(mem_ctx, struct idle_event); + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + result->interval = interval; + result->handler = handler; + result->private_data = private_data; + + if (!(result->name = talloc_asprintf(result, "idle_evt(%s)", name))) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(result); + return NULL; + } + + result->te = tevent_add_timer(event_ctx, result, + timeval_sum(&now, &interval), + smbd_idle_event_handler, result); + if (result->te == NULL) { + DEBUG(0, ("event_add_timed failed\n")); + TALLOC_FREE(result); + return NULL; + } + + DEBUG(10,("event_add_idle: %s %p\n", result->name, result->te)); + return result; +} diff --git a/source3/lib/util_file.c b/source3/lib/util_file.c new file mode 100644 index 0000000..ba96eee --- /dev/null +++ b/source3/lib/util_file.c @@ -0,0 +1,187 @@ +/* + * Unix SMB/CIFS implementation. + * SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "lib/util_file.h" +#include "lib/util/debug.h" +#include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" +#include "lib/util/sys_popen.h" +#include "lib/async_req/async_sock.h" +#include "lib/util/tevent_unix.h" + +struct file_ploadv_state { + struct tevent_context *ev; + struct tevent_req *subreq; + size_t maxsize; + int fd; + uint8_t *buf; +}; + +static void file_ploadv_cleanup_fn( + struct tevent_req *req, enum tevent_req_state req_state); +static void file_ploadv_readable(struct tevent_req *subreq); + +struct tevent_req *file_ploadv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + char * const argl[], size_t maxsize) +{ + struct tevent_req *req = NULL; + struct file_ploadv_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct file_ploadv_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->maxsize = maxsize; + + state->fd = sys_popenv(argl); + if (state->fd == -1) { + tevent_req_error(req, errno); + return tevent_req_post(req, ev); + } + tevent_req_set_cleanup_fn(req, file_ploadv_cleanup_fn); + + state->subreq = wait_for_read_send(state, state->ev, state->fd, false); + if (tevent_req_nomem(state->subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->subreq, file_ploadv_readable, req); + return req; +} + +static void file_ploadv_cleanup_fn( + struct tevent_req *req, enum tevent_req_state req_state) +{ + struct file_ploadv_state *state = tevent_req_data( + req, struct file_ploadv_state); + + TALLOC_FREE(state->subreq); + if (state->fd != -1) { + sys_pclose(state->fd); + state->fd = -1; + } +} + +static void file_ploadv_readable(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct file_ploadv_state *state = tevent_req_data( + req, struct file_ploadv_state); + uint8_t buf[1024]; + uint8_t *tmp; + ssize_t nread; + size_t bufsize; + int err; + bool ok; + + ok = wait_for_read_recv(subreq, &err); + TALLOC_FREE(subreq); + state->subreq = NULL; + if (!ok) { + tevent_req_error(req, err); + return; + } + + nread = sys_read(state->fd, buf, sizeof(buf)); + if (nread == -1) { + tevent_req_error(req, errno); + return; + } + if (nread == 0) { + tevent_req_done(req); + return; + } + + bufsize = talloc_get_size(state->buf); + if (bufsize > 0) { + /* + * Last round we've added the trailing '\0'. Remove it + * for this round. + */ + bufsize -= 1; + } + + if (((bufsize + nread) < bufsize) || + ((bufsize + nread + 1) < bufsize)) { + /* overflow */ + tevent_req_error(req, EMSGSIZE); + return; + } + + if ((state->maxsize != 0) && ((bufsize + nread) > state->maxsize)) { + tevent_req_error(req, EMSGSIZE); + return; + } + + tmp = talloc_realloc(state, state->buf, uint8_t, bufsize + nread + 1); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->buf = tmp; + + memcpy(state->buf + bufsize, buf, nread); + state->buf[bufsize+nread] = '\0'; + + state->subreq = wait_for_read_send(state, state->ev, state->fd, false); + if (tevent_req_nomem(state->subreq, req)) { + return; + } + tevent_req_set_callback(state->subreq, file_ploadv_readable, req); +} + +int file_ploadv_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf) +{ + struct file_ploadv_state *state = tevent_req_data( + req, struct file_ploadv_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + return err; + } + *buf = talloc_move(mem_ctx, &state->buf); + + tevent_req_received(req); + + return 0; +} + + +/** + Load a pipe into memory and return an array of pointers to lines in the data + must be freed with TALLOC_FREE. +**/ + +char **file_lines_ploadv(TALLOC_CTX *mem_ctx, + char * const argl[], + int *numlines) +{ + char *p = NULL; + size_t size; + + p = file_ploadv(argl, &size); + if (!p) { + return NULL; + } + + return file_lines_parse(p, size, numlines, mem_ctx); +} diff --git a/source3/lib/util_file.h b/source3/lib/util_file.h new file mode 100644 index 0000000..1aef5a2 --- /dev/null +++ b/source3/lib/util_file.h @@ -0,0 +1,35 @@ +/* + * Unix SMB/CIFS implementation. + * SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_UTIL_FILE_H__ +#define __LIB_UTIL_FILE_H__ + +#include "replace.h" +#include <tevent.h> + +struct tevent_req *file_ploadv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + char * const argl[], size_t maxsize); +int file_ploadv_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf); +char **file_lines_ploadv(TALLOC_CTX *mem_ctx, + char * const argl[], + int *numlines); + +#endif diff --git a/source3/lib/util_macstreams.c b/source3/lib/util_macstreams.c new file mode 100644 index 0000000..787ad88 --- /dev/null +++ b/source3/lib/util_macstreams.c @@ -0,0 +1,73 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Ralph Boehme 2019 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "MacExtensions.h" +#include "util_macstreams.h" + +/* Yes, I have considered multibyte */ +#undef strncasecmp + +bool is_afpinfo_stream(const char *sname) +{ + int cmp; + + if (sname == NULL) { + return false; + } + + cmp = strncasecmp(sname, + AFPINFO_STREAM_NAME, + strlen(AFPINFO_STREAM_NAME)); + if (cmp == 0) { + return true; + } + return false; +} + +bool is_afpresource_stream(const char *sname) +{ + int cmp; + + if (sname == NULL) { + return false; + } + + cmp = strncasecmp(sname, + AFPRESOURCE_STREAM_NAME, + strlen(AFPRESOURCE_STREAM_NAME)); + if (cmp == 0) { + return true; + } + return false; +} + +/** + * Test whether stream is an Apple stream. + **/ +bool is_apple_stream(const char *sname) +{ + if (is_afpinfo_stream(sname)) { + return true; + } + if (is_afpresource_stream(sname)) { + return true; + } + return false; +} diff --git a/source3/lib/util_macstreams.h b/source3/lib/util_macstreams.h new file mode 100644 index 0000000..ad50abc --- /dev/null +++ b/source3/lib/util_macstreams.h @@ -0,0 +1,27 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Ralph Boehme 2019 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _UTIL_MACSTREAMS_H_ +#define _UTIL_MACSTREAMS_H_ + +bool is_afpinfo_stream(const char *sname); +bool is_afpresource_stream(const char *sname); +bool is_apple_stream(const char *sname); + +#endif diff --git a/source3/lib/util_malloc.c b/source3/lib/util_malloc.c new file mode 100644 index 0000000..09586d5 --- /dev/null +++ b/source3/lib/util_malloc.c @@ -0,0 +1,122 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2007 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + Copyright (C) James Peach 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#if defined(PARANOID_MALLOC_CHECKER) + +/**************************************************************************** + Internal malloc wrapper. Externally visible. +****************************************************************************/ + +void *malloc_(size_t size) +{ + if (size == 0) { + return NULL; + } +#undef malloc + return malloc(size); +#define malloc(s) __ERROR_DONT_USE_MALLOC_DIRECTLY +} + +/**************************************************************************** + Internal realloc wrapper. Not externally visible. +****************************************************************************/ + +static void *realloc_(void *ptr, size_t size) +{ +#undef realloc + return realloc(ptr, size); +#define realloc(p,s) __ERROR_DONT_USE_REALLOC_DIRECTLY +} + +#endif /* PARANOID_MALLOC_CHECKER */ + +/**************************************************************************** + Expand a pointer to be a particular size. + Note that this version of Realloc has an extra parameter that decides + whether to free the passed in storage on allocation failure or if the + new size is zero. + + This is designed for use in the typical idiom of : + + p = SMB_REALLOC(p, size) + if (!p) { + return error; + } + + and not to have to keep track of the old 'p' contents to free later, nor + to worry if the size parameter was zero. In the case where NULL is returned + we guarantee that p has been freed. + + If free later semantics are desired, then pass 'free_old_on_error' as False which + guarantees that the old contents are not freed on error, even if size == 0. To use + this idiom use : + + tmp = SMB_REALLOC_KEEP_OLD_ON_ERROR(p, size); + if (!tmp) { + SAFE_FREE(p); + return error; + } else { + p = tmp; + } + + Changes were instigated by Coverity error checking. JRA. +****************************************************************************/ + +void *Realloc(void *p, size_t size, bool free_old_on_error) +{ + void *ret=NULL; + + if (size == 0) { + if (free_old_on_error) { + SAFE_FREE(p); + } + DEBUG(2,("Realloc asked for 0 bytes\n")); + return NULL; + } + +#if defined(PARANOID_MALLOC_CHECKER) + if (!p) { + ret = (void *)malloc_(size); + } else { + ret = (void *)realloc_(p,size); + } +#else + if (!p) { + ret = (void *)malloc(size); + } else { + ret = (void *)realloc(p,size); + } +#endif + + if (!ret) { + if (free_old_on_error && p) { + SAFE_FREE(p); + } + DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size)); + } + + return(ret); +} + diff --git a/source3/lib/util_matching.c b/source3/lib/util_matching.c new file mode 100644 index 0000000..4a321f2 --- /dev/null +++ b/source3/lib/util_matching.c @@ -0,0 +1,391 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Stefan Metzmacher 2021 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util_matching.h" +#include "lib/util/string_wrappers.h" + +struct samba_path_matching_entry { + const char *name; + bool is_wild; + regex_t re; +}; + +struct samba_path_matching_result { + ssize_t replace_start; + ssize_t replace_end; + bool match; +}; + +struct samba_path_matching { + bool case_sensitive; + NTSTATUS (*matching_fn)(const struct samba_path_matching *pm, + const struct samba_path_matching_entry *e, + const char *namecomponent, + struct samba_path_matching_result *result); + size_t num_entries; + struct samba_path_matching_entry *entries; +}; + +static NTSTATUS samba_path_matching_split(TALLOC_CTX *mem_ctx, + const char *namelist_in, + struct samba_path_matching **ppm) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *name_end = NULL; + char *namelist = NULL; + char *namelist_end = NULL; + char *nameptr = NULL; + struct samba_path_matching *pm = NULL; + size_t num_entries = 0; + struct samba_path_matching_entry *entries = NULL; + + *ppm = NULL; + + pm = talloc_zero(mem_ctx, struct samba_path_matching); + if (pm == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + talloc_reparent(mem_ctx, frame, pm); + + namelist = talloc_strdup(frame, namelist_in); + if (namelist == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + nameptr = namelist; + + namelist_end = &namelist[strlen(namelist)]; + + /* + * We need to make two passes over the string. The + * first to count the number of elements, the second + * to split it. + * + * The 1st time entries is NULL. + * the 2nd time entries is allocated. + */ +again: + while (nameptr <= namelist_end) { + /* anything left? */ + if (*nameptr == '\0') { + break; + } + + if (*nameptr == '/') { + /* cope with multiple (useless) /s) */ + nameptr++; + continue; + } + + /* find the next '/' or consume remaining */ + name_end = strchr_m(nameptr, '/'); + if (entries != NULL) { + if (name_end != NULL) { + *name_end = '\0'; + } + entries[num_entries].name = talloc_strdup(entries, + nameptr); + if (entries[num_entries].name == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + num_entries++; + if (name_end != NULL) { + /* next segment please */ + nameptr = name_end + 1; + continue; + } + + /* no entries remaining */ + break; + } + + if (num_entries == 0) { + /* + * No entries in the first round => we're done + */ + goto done; + } + + if (entries != NULL) { + /* + * We finished the 2nd round => we're done + */ + goto done; + } + + /* + * Now allocate the array and loop again + * in order to split the names. + */ + entries = talloc_zero_array(pm, + struct samba_path_matching_entry, + num_entries); + if (entries == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + num_entries = 0; + nameptr = namelist; + goto again; + +done: + pm->num_entries = num_entries; + pm->entries = entries; + *ppm = talloc_move(mem_ctx, &pm); + TALLOC_FREE(frame); + return NT_STATUS_OK; +}; + +static NTSTATUS samba_path_create_mswild_fn(const struct samba_path_matching *pm, + const struct samba_path_matching_entry *e, + const char *namecomponent, + struct samba_path_matching_result *result) +{ + bool match = false; + + if (e->is_wild) { + match = mask_match(namecomponent, e->name, pm->case_sensitive); + } else if (pm->case_sensitive) { + match = (strcmp(namecomponent, e->name) == 0); + } else { + match = (strcasecmp_m(namecomponent, e->name) == 0); + } + + *result = (struct samba_path_matching_result) { + .match = match, + .replace_start = -1, + .replace_end = -1, + }; + + return NT_STATUS_OK; +} + +NTSTATUS samba_path_matching_mswild_create(TALLOC_CTX *mem_ctx, + bool case_sensitive, + const char *namelist_in, + struct samba_path_matching **ppm) +{ + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + struct samba_path_matching *pm = NULL; + size_t i; + + *ppm = NULL; + + status = samba_path_matching_split(mem_ctx, namelist_in, &pm); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + talloc_reparent(mem_ctx, frame, pm); + + for (i = 0; i < pm->num_entries; i++) { + struct samba_path_matching_entry *e = &pm->entries[i]; + + e->is_wild = ms_has_wild(e->name); + } + + pm->case_sensitive = case_sensitive; + pm->matching_fn = samba_path_create_mswild_fn; + *ppm = talloc_move(mem_ctx, &pm); + TALLOC_FREE(frame); + return NT_STATUS_OK; +}; + +static int samba_path_matching_regex_sub1_destructor(struct samba_path_matching *pm) +{ + ssize_t i; + + for (i = 0; i < pm->num_entries; i++) { + struct samba_path_matching_entry *e = &pm->entries[i]; + + regfree(&e->re); + } + + pm->num_entries = 0; + + return 0; +} + +static NTSTATUS samba_path_create_regex_sub1_fn(const struct samba_path_matching *pm, + const struct samba_path_matching_entry *e, + const char *namecomponent, + struct samba_path_matching_result *result) +{ + if (e->re.re_nsub == 1) { + regmatch_t matches[2] = { }; + int ret; + + ret = regexec(&e->re, namecomponent, 2, matches, 0); + if (ret == 0) { + *result = (struct samba_path_matching_result) { + .match = true, + .replace_start = matches[1].rm_so, + .replace_end = matches[1].rm_eo, + }; + + return NT_STATUS_OK; + } + } + + *result = (struct samba_path_matching_result) { + .match = false, + .replace_start = -1, + .replace_end = -1, + }; + + return NT_STATUS_OK; +} + +NTSTATUS samba_path_matching_regex_sub1_create(TALLOC_CTX *mem_ctx, + const char *namelist_in, + struct samba_path_matching **ppm) +{ + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + struct samba_path_matching *pm = NULL; + ssize_t i; + + *ppm = NULL; + + status = samba_path_matching_split(mem_ctx, namelist_in, &pm); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + talloc_reparent(mem_ctx, frame, pm); + + for (i = 0; i < pm->num_entries; i++) { + struct samba_path_matching_entry *e = &pm->entries[i]; + int ret; + + ret = regcomp(&e->re, e->name, 0); + if (ret != 0) { + fstring buf = { 0,}; + + regerror(ret, &e->re, buf, sizeof(buf)); + + DBG_ERR("idx[%zu] regcomp: /%s/ - %d - %s\n", + i, e->name, ret, buf); + + status = NT_STATUS_INVALID_PARAMETER; + i--; + goto cleanup; + } + + if (e->re.re_nsub != 1) { + DBG_ERR("idx[%zu] regcomp: /%s/ - re_nsub[%zu] != 1\n", + i, e->name, e->re.re_nsub); + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + } + + talloc_set_destructor(pm, samba_path_matching_regex_sub1_destructor); + + pm->case_sensitive = true; + pm->matching_fn = samba_path_create_regex_sub1_fn; + *ppm = talloc_move(mem_ctx, &pm); + TALLOC_FREE(frame); + return NT_STATUS_OK; + +cleanup: + for (; i >= 0; i--) { + struct samba_path_matching_entry *e = &pm->entries[i]; + + regfree(&e->re); + } + + TALLOC_FREE(frame); + return status; +}; + +NTSTATUS samba_path_matching_check_last_component(struct samba_path_matching *pm, + const char *name, + ssize_t *p_match_idx, + ssize_t *p_replace_start, + ssize_t *p_replace_end) +{ + struct samba_path_matching_result result = { + .match = false, + .replace_start = -1, + .replace_end = -1, + }; + ssize_t match_idx = -1; + NTSTATUS status = NT_STATUS_OK; + const char *last_component = NULL; + size_t i; + + if (pm->num_entries == 0) { + goto finish; + } + + /* Get the last component of the unix name. */ + last_component = strrchr_m(name, '/'); + if (last_component == NULL) { + last_component = name; + } else { + last_component++; /* Go past '/' */ + } + + for (i = 0; i < pm->num_entries; i++) { + struct samba_path_matching_entry *e = &pm->entries[i]; + + status = pm->matching_fn(pm, e, last_component, &result); + if (!NT_STATUS_IS_OK(status)) { + result = (struct samba_path_matching_result) { + .match = false, + .replace_start = -1, + .replace_end = -1, + }; + goto finish; + } + + if (result.match) { + match_idx = i; + goto finish; + } + } + +finish: + *p_match_idx = match_idx; + if (p_replace_start != NULL) { + size_t last_ofs = 0; + + if (result.replace_start >= 0) { + last_ofs = PTR_DIFF(last_component, name); + } + + *p_replace_start = last_ofs + result.replace_start; + } + if (p_replace_end != NULL) { + size_t last_ofs = 0; + + if (result.replace_end >= 0) { + last_ofs = PTR_DIFF(last_component, name); + } + + *p_replace_end = last_ofs + result.replace_end; + } + return status; +} diff --git a/source3/lib/util_matching.h b/source3/lib/util_matching.h new file mode 100644 index 0000000..5abe40e --- /dev/null +++ b/source3/lib/util_matching.h @@ -0,0 +1,40 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Stefan Metzmacher 2021 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SAMBA_LIB_UTIL_MATCHING_H_ +#define _SAMBA_LIB_UTIL_MATCHING_H_ + +struct samba_path_matching; + +NTSTATUS samba_path_matching_mswild_create(TALLOC_CTX *mem_ctx, + bool case_sensitive, + const char *namelist_in, + struct samba_path_matching **ppm); + +NTSTATUS samba_path_matching_regex_sub1_create(TALLOC_CTX *mem_ctx, + const char *namelist_in, + struct samba_path_matching **ppm); + +NTSTATUS samba_path_matching_check_last_component(struct samba_path_matching *pm, + const char *name, + ssize_t *p_match_idx, + ssize_t *p_replace_start, + ssize_t *p_replace_end); + +#endif /* _SAMBA_LIB_UTIL_MATCHING_H_ */ diff --git a/source3/lib/util_names.c b/source3/lib/util_names.c new file mode 100644 index 0000000..b62ddb3 --- /dev/null +++ b/source3/lib/util_names.c @@ -0,0 +1,86 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2007 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + Copyright (C) James Peach 2006 + Copyright (C) Andrew Bartlett 2010-2011 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/****************************************************************** + get the default domain/netbios name to be used when dealing + with our passdb list of accounts +******************************************************************/ + +const char *get_global_sam_name(void) +{ + if (IS_DC) { + return lp_workgroup(); + } + return lp_netbios_name(); +} + + +/****************************************************************** + Get the default domain/netbios name to be used when + testing authentication. +******************************************************************/ + +const char *my_sam_name(void) +{ + if (lp_server_role() == ROLE_STANDALONE) { + return lp_netbios_name(); + } + + return lp_workgroup(); +} + +bool is_allowed_domain(const char *domain_name) +{ + const char **ignored_domains = NULL; + const char **dom = NULL; + + ignored_domains = lp_parm_string_list(-1, + "winbind", + "ignore domains", + NULL); + + for (dom = ignored_domains; dom != NULL && *dom != NULL; dom++) { + if (gen_fnmatch(*dom, domain_name) == 0) { + DBG_NOTICE("Ignoring domain '%s'\n", domain_name); + return false; + } + } + + if (lp_allow_trusted_domains()) { + return true; + } + + if (strequal(lp_workgroup(), domain_name)) { + return true; + } + + if (is_myname(domain_name)) { + return true; + } + + DBG_NOTICE("Not trusted domain '%s'\n", domain_name); + return false; +} diff --git a/source3/lib/util_nscd.c b/source3/lib/util_nscd.c new file mode 100644 index 0000000..6002f14 --- /dev/null +++ b/source3/lib/util_nscd.c @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Guenther Deschner 2006 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#ifdef HAVE_LIBNSCD +#include <libnscd.h> +#endif + +static void smb_nscd_flush_cache(const char *service) +{ +#ifdef HAVE_NSCD_FLUSH_CACHE + if (nscd_flush_cache(service)) { + DEBUG(10,("failed to flush nscd cache for '%s' service: %s. " + "Is nscd running?\n", + service, strerror(errno))); + } +#endif +} + +void smb_nscd_flush_user_cache(void) +{ + smb_nscd_flush_cache("passwd"); +} + +void smb_nscd_flush_group_cache(void) +{ + smb_nscd_flush_cache("group"); +} diff --git a/source3/lib/util_nttoken.c b/source3/lib/util_nttoken.c new file mode 100644 index 0000000..925b79c --- /dev/null +++ b/source3/lib/util_nttoken.c @@ -0,0 +1,103 @@ +/* + * Unix SMB/CIFS implementation. + * Authentication utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Andrew Bartlett 2001-2023 + * Copyright (C) Jeremy Allison 2000-2001 + * Copyright (C) Rafal Szczesniak 2002 + * Copyright (C) Volker Lendecke 2006 + * Copyright (C) Michael Adam 2007 + * Copyright (C) Guenther Deschner 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* function(s) moved from auth/auth_util.c to minimize linker deps */ + +#include "includes.h" +#include "../libcli/security/security.h" + +/**************************************************************************** + merge NT tokens +****************************************************************************/ + +NTSTATUS merge_with_system_token(TALLOC_CTX *mem_ctx, + const struct security_token *token_1, + struct security_token **token_out) +{ + const struct security_token *token_2 = get_system_token(); + struct security_token *token = NULL; + NTSTATUS status; + uint32_t i; + + if (!token_1 || !token_2 || !token_out) { + return NT_STATUS_INVALID_PARAMETER; + } + + token = talloc_zero(mem_ctx, struct security_token); + NT_STATUS_HAVE_NO_MEMORY(token); + + for (i=0; i < token_1->num_sids; i++) { + status = add_sid_to_array_unique(mem_ctx, + &token_1->sids[i], + &token->sids, + &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(token); + return status; + } + } + + for (i=0; i < token_2->num_sids; i++) { + status = add_sid_to_array_unique(mem_ctx, + &token_2->sids[i], + &token->sids, + &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(token); + return status; + } + } + + token->privilege_mask |= token_1->privilege_mask; + token->privilege_mask |= token_2->privilege_mask; + + token->rights_mask |= token_1->rights_mask; + token->rights_mask |= token_2->rights_mask; + + /* + * We don't need to merge claims as the system token has no + * claims + */ + + *token_out = token; + + return NT_STATUS_OK; +} + +/******************************************************************* + Check if this struct security_ace has a SID in common with the token. +********************************************************************/ + +bool token_sid_in_ace(const struct security_token *token, const struct security_ace *ace) +{ + size_t i; + + for (i = 0; i < token->num_sids; i++) { + if (dom_sid_equal(&ace->trustee, &token->sids[i])) + return true; + } + + return false; +} diff --git a/source3/lib/util_path.c b/source3/lib/util_path.c new file mode 100644 index 0000000..a7d1a8b --- /dev/null +++ b/source3/lib/util_path.c @@ -0,0 +1,350 @@ +/* + * Unix SMB/CIFS implementation. + * Samba utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Jeremy Allison 2001-2007 + * Copyright (C) Simo Sorce 2001 + * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + * Copyright (C) James Peach 2006 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include <talloc.h> +#include "lib/util/debug.h" +#include "lib/util/samba_util.h" +#include "lib/util_path.h" + +struct loadparm_substitution; +struct share_params; +#include "source3/param/param_proto.h" + +/** + * @brief Returns an absolute path to a file concatenating the provided + * @a rootpath and @a basename + * + * @param name Filename, relative to @a rootpath + * + * @retval Pointer to a string containing the full path. + **/ + +static char *xx_path(TALLOC_CTX *mem_ctx, + const char *name, + const char *rootpath) +{ + char *fname = NULL; + + fname = talloc_strdup(mem_ctx, rootpath); + if (!fname) { + return NULL; + } + trim_string(fname,"","/"); + + if (!directory_create_or_exist(fname, 0755)) { + return NULL; + } + + return talloc_asprintf_append(fname, "/%s", name); +} + +/** + * @brief Returns an absolute path to a file in the Samba lock directory. + * + * @param name File to find, relative to LOCKDIR. + * + * @retval Pointer to a talloc'ed string containing the full path. + **/ + +char *lock_path(TALLOC_CTX *mem_ctx, const char *name) +{ + return xx_path(mem_ctx, name, lp_lock_directory()); +} + +/** + * @brief Returns an absolute path to a file in the Samba state directory. + * + * @param name File to find, relative to STATEDIR. + * + * @retval Pointer to a talloc'ed string containing the full path. + **/ + +char *state_path(TALLOC_CTX *mem_ctx, const char *name) +{ + return xx_path(mem_ctx, name, lp_state_directory()); +} + +/** + * @brief Returns an absolute path to a file in the Samba cache directory. + * + * @param name File to find, relative to CACHEDIR. + * + * @retval Pointer to a talloc'ed string containing the full path. + **/ + +char *cache_path(TALLOC_CTX *mem_ctx, const char *name) +{ + return xx_path(mem_ctx, name, lp_cache_directory()); +} + +/** + * @brief Removes any invalid path components in an absolute POSIX path. + * + * @param ctx Talloc context to return string. + * + * @param abs_path Absolute path string to process. + * + * @retval Pointer to a talloc'ed string containing the absolute full path. + **/ + +char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *pathname_in) +{ + /* + * Note we use +2 here so if pathname_in=="" then we + * have space to return "/". + */ + char *pathname = talloc_array(ctx, char, strlen(pathname_in)+2); + const char *s = pathname_in; + char *p = pathname; + + if (pathname == NULL) { + return NULL; + } + + /* Always start with a '/'. */ + *p++ = '/'; + + while (*s) { + /* Deal with '/' or multiples of '/'. */ + if (s[0] == '/') { + while (s[0] == '/') { + /* Eat trailing '/' */ + s++; + } + /* Update target with one '/' */ + if (p[-1] != '/') { + *p++ = '/'; + } + continue; + } + if (p[-1] == '/') { + /* Deal with "./" or ".\0" */ + if (s[0] == '.' && + (s[1] == '/' || s[1] == '\0')) { + /* Eat the dot. */ + s++; + while (s[0] == '/') { + /* Eat any trailing '/' */ + s++; + } + /* Don't write anything to target. */ + continue; + } + /* Deal with "../" or "..\0" */ + if (s[0] == '.' && s[1] == '.' && + (s[2] == '/' || s[2] == '\0')) { + /* Eat the dot dot. */ + s += 2; + while (s[0] == '/') { + /* Eat any trailing '/' */ + s++; + } + /* + * As we're on the slash, we go back + * one character to point p at the + * slash we just saw. + */ + if (p > pathname) { + p--; + } + /* + * Now go back to the slash + * before the one that p currently points to. + */ + while (p > pathname) { + p--; + if (p[0] == '/') { + break; + } + } + /* + * Step forward one to leave the + * last written '/' alone. + */ + p++; + + /* Don't write anything to target. */ + continue; + } + } + /* Non-separator character, just copy. */ + *p++ = *s++; + } + if (p[-1] == '/') { + /* + * We finished on a '/'. + * Remove the trailing '/', but not if it's + * the sole character in the path. + */ + if (p > pathname + 1) { + p--; + } + } + /* Terminate and we're done ! */ + *p++ = '\0'; + return pathname; +} + +static bool find_snapshot_token( + const char *filename, + char sep, + const char **_start, + const char **_next_component, + NTTIME *twrp) +{ + const char *start = NULL; + const char *end = NULL; + struct tm tm = {}; + time_t t; + + start = strstr_m(filename, "@GMT-"); + + if (start == NULL) { + return false; + } + + if ((start > filename) && (start[-1] != sep)) { + /* the GMT-token does not start a path-component */ + return false; + } + + end = strptime(start, GMT_FORMAT, &tm); + if (end == NULL) { + /* Not a valid timestring. */ + return false; + } + + if ((end[0] != '\0') && (end[0] != sep)) { + /* + * It is not a complete path component, i.e. the path + * component continues after the gmt-token. + */ + return false; + } + + tm.tm_isdst = -1; + t = timegm(&tm); + unix_to_nt_time(twrp, t); + + DBG_DEBUG("Extracted @GMT-Timestamp %s\n", + nt_time_string(talloc_tos(), *twrp)); + + *_start = start; + + if (end[0] == sep) { + end += 1; + } + *_next_component = end; + + return true; +} + +bool clistr_is_previous_version_path(const char *path) +{ + const char *start = NULL; + const char *next = NULL; + NTTIME twrp; + bool ok; + + ok = find_snapshot_token(path, '\\', &start, &next, &twrp); + return ok; +} + +static bool extract_snapshot_token_internal(char *fname, NTTIME *twrp, char sep) +{ + const char *start = NULL; + const char *next = NULL; + size_t remaining; + bool found; + + found = find_snapshot_token(fname, sep, &start, &next, twrp); + if (!found) { + return false; + } + + remaining = strlen(next); + memmove(discard_const_p(char, start), next, remaining+1); + + return true; +} + +bool extract_snapshot_token(char *fname, NTTIME *twrp) +{ + return extract_snapshot_token_internal(fname, twrp, '/'); +} + +bool clistr_smb2_extract_snapshot_token(char *fname, NTTIME *twrp) +{ + return extract_snapshot_token_internal(fname, twrp, '\\'); +} + +/* + * Take two absolute paths, figure out if "subdir" is a proper + * subdirectory of "parent". Return the component relative to the + * "parent" without the potential "/". Take care of "parent" + * possibly ending in "/". + */ +bool subdir_of(const char *parent, + size_t parent_len, + const char *subdir, + const char **_relative) +{ + const char *relative = NULL; + bool matched; + + SMB_ASSERT(parent[0] == '/'); + SMB_ASSERT(subdir[0] == '/'); + + if (parent_len == 1) { + /* + * Everything is below "/" + */ + *_relative = subdir+1; + return true; + } + + if (parent[parent_len-1] == '/') { + parent_len -= 1; + } + + matched = (strncmp(subdir, parent, parent_len) == 0); + if (!matched) { + return false; + } + + relative = &subdir[parent_len]; + + if (relative[0] == '\0') { + *_relative = relative; /* nothing left */ + return true; + } + + if (relative[0] == '/') { + /* End of parent must match a '/' in subdir. */ + *_relative = relative+1; + return true; + } + + return false; +} diff --git a/source3/lib/util_path.h b/source3/lib/util_path.h new file mode 100644 index 0000000..9dcc1dd --- /dev/null +++ b/source3/lib/util_path.h @@ -0,0 +1,55 @@ +/* + * Unix SMB/CIFS implementation. + * Samba utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Jeremy Allison 2001-2007 + * Copyright (C) Simo Sorce 2001 + * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + * Copyright (C) James Peach 2006 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_UTIL_PATH_H__ +#define __LIB_UTIL_PATH_H__ + +#include "replace.h" +#include <talloc.h> +#include "lib/util/time.h" + +/* + * Timestamp format used in "previous versions": + * This is the windows-level format of the @GMT- token. + * It is a fixed format not to be confused with the + * format for the POSIX-Level token of the shadow_copy2 + * VFS module that can be configured via the "shadow:format" + * configuration option but defaults to the same format. + * See the shadow_copy2 module. + */ +#define GMT_NAME_LEN 24 /* length of a @GMT- name */ +#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S" + +char *lock_path(TALLOC_CTX *mem_ctx, const char *name); +char *state_path(TALLOC_CTX *mem_ctx, const char *name); +char *cache_path(TALLOC_CTX *mem_ctx, const char *name); +char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path); +bool extract_snapshot_token(char *fname, NTTIME *twrp); +bool clistr_smb2_extract_snapshot_token(char *fname, NTTIME *twrp); +bool clistr_is_previous_version_path(const char *path); +bool subdir_of(const char *parent, + size_t parent_len, + const char *subdir, + const char **_relative); + +#endif diff --git a/source3/lib/util_procid.c b/source3/lib/util_procid.c new file mode 100644 index 0000000..5a4c081 --- /dev/null +++ b/source3/lib/util_procid.c @@ -0,0 +1,69 @@ +/* + * Unix SMB/CIFS implementation. + * Samba utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Jeremy Allison 2001-2007 + * Copyright (C) Simo Sorce 2001 + * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + * Copyright (C) James Peach 2006 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "util_procid.h" +#include "lib/util/debug.h" +#include "lib/messaging/messages_dgm.h" + +pid_t procid_to_pid(const struct server_id *proc) +{ + return proc->pid; +} + +static uint32_t my_vnn = NONCLUSTER_VNN; + +void set_my_vnn(uint32_t vnn) +{ + DEBUG(10, ("vnn pid %d = %u\n", (int)getpid(), (unsigned int)vnn)); + my_vnn = vnn; +} + +uint32_t get_my_vnn(void) +{ + return my_vnn; +} + +struct server_id pid_to_procid(pid_t pid) +{ + uint64_t unique = 0; + int ret; + + ret = messaging_dgm_get_unique(pid, &unique); + if (ret != 0) { + DBG_NOTICE("messaging_dgm_get_unique failed: %s\n", + strerror(ret)); + } + + return (struct server_id) { + .pid = pid, .unique_id = unique, .vnn = my_vnn }; +} + +bool procid_valid(const struct server_id *pid) +{ + return (pid->pid != (uint64_t)-1); +} + +bool procid_is_local(const struct server_id *pid) +{ + return pid->vnn == my_vnn; +} diff --git a/source3/lib/util_procid.h b/source3/lib/util_procid.h new file mode 100644 index 0000000..9637363 --- /dev/null +++ b/source3/lib/util_procid.h @@ -0,0 +1,37 @@ +/* + * Unix SMB/CIFS implementation. + * Samba utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Jeremy Allison 2001-2007 + * Copyright (C) Simo Sorce 2001 + * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003 + * Copyright (C) James Peach 2006 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_UTIL_PROCID_H__ +#define __LIB_UTIL_PROCID_H__ + +#include "replace.h" +#include "librpc/gen_ndr/server_id.h" + +pid_t procid_to_pid(const struct server_id *proc); +void set_my_vnn(uint32_t vnn); +uint32_t get_my_vnn(void); +struct server_id pid_to_procid(pid_t pid); +bool procid_valid(const struct server_id *pid); +bool procid_is_local(const struct server_id *pid); + +#endif diff --git a/source3/lib/util_sd.c b/source3/lib/util_sd.c new file mode 100644 index 0000000..23f37b7 --- /dev/null +++ b/source3/lib/util_sd.c @@ -0,0 +1,626 @@ +/* + Unix SMB/CIFS implementation. + Security Descriptor (SD) helper functions + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2000 + Copyright (C) Jelmer Vernooij 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "util_sd.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "../libcli/security/security.h" +#include "rpc_client/cli_pipe.h" +#include "rpc_client/cli_lsarpc.h" +#include "lib/util/string_wrappers.h" + +/* These values discovered by inspection */ + +struct perm_value { + const char *perm; + uint32_t mask; +}; + +static const struct perm_value special_values[] = { + { "R", SEC_RIGHTS_FILE_READ }, + { "W", SEC_RIGHTS_FILE_WRITE }, + { "X", SEC_RIGHTS_FILE_EXECUTE }, + { "D", SEC_STD_DELETE }, + { "P", SEC_STD_WRITE_DAC }, + { "O", SEC_STD_WRITE_OWNER }, + { NULL, 0 }, +}; + +static const struct perm_value standard_values[] = { + { "READ", SEC_RIGHTS_DIR_READ|SEC_DIR_TRAVERSE }, + { "CHANGE", SEC_RIGHTS_DIR_READ|SEC_STD_DELETE|\ + SEC_DIR_DELETE_CHILD|\ + SEC_RIGHTS_DIR_WRITE|SEC_DIR_TRAVERSE }, + { "FULL", SEC_RIGHTS_DIR_ALL }, + { NULL, 0 }, +}; + +static const struct { + uint16_t mask; + const char *str; + const char *desc; +} sec_desc_ctrl_bits[] = { + {SEC_DESC_OWNER_DEFAULTED, "OD", "Owner Defaulted"}, + {SEC_DESC_GROUP_DEFAULTED, "GD", "Group Defaulted"}, + {SEC_DESC_DACL_PRESENT, "DP", "DACL Present"}, + {SEC_DESC_DACL_DEFAULTED, "DD", "DACL Defaulted"}, + {SEC_DESC_SACL_PRESENT, "SP", "SACL Present"}, + {SEC_DESC_SACL_DEFAULTED, "SD", "SACL Defaulted"}, + {SEC_DESC_DACL_TRUSTED, "DT", "DACL Trusted"}, + {SEC_DESC_SERVER_SECURITY, "SS", "Server Security"}, + {SEC_DESC_DACL_AUTO_INHERIT_REQ, "DR", "DACL Inheritance Required"}, + {SEC_DESC_SACL_AUTO_INHERIT_REQ, "SR", "SACL Inheritance Required"}, + {SEC_DESC_DACL_AUTO_INHERITED, "DI", "DACL Auto Inherited"}, + {SEC_DESC_SACL_AUTO_INHERITED, "SI", "SACL Auto Inherited"}, + {SEC_DESC_DACL_PROTECTED, "PD", "DACL Protected"}, + {SEC_DESC_SACL_PROTECTED, "PS", "SACL Protected"}, + {SEC_DESC_RM_CONTROL_VALID, "RM", "RM Control Valid"}, + {SEC_DESC_SELF_RELATIVE , "SR", "Self Relative"}, +}; + +/* Open cli connection and policy handle */ +static NTSTATUS cli_lsa_lookup_sid(struct cli_state *cli, + const struct dom_sid *sid, + TALLOC_CTX *mem_ctx, + enum lsa_SidType *type, + char **domain, char **name) +{ + struct smbXcli_tcon *orig_tcon = NULL; + char *orig_share = NULL; + struct rpc_pipe_client *p = NULL; + struct policy_handle handle; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + enum lsa_SidType *types; + char **domains; + char **names; + + if (cli_state_has_tcon(cli)) { + cli_state_save_tcon_share(cli, &orig_tcon, &orig_share); + } + + status = cli_tree_connect(cli, "IPC$", "?????", NULL); + if (!NT_STATUS_IS_OK(status)) { + goto tcon_fail; + } + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &p); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = rpccli_lsa_open_policy(p, talloc_tos(), True, + GENERIC_EXECUTE_ACCESS, &handle); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = rpccli_lsa_lookup_sids(p, talloc_tos(), &handle, 1, sid, + &domains, &names, &types); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + *type = types[0]; + *domain = talloc_move(mem_ctx, &domains[0]); + *name = talloc_move(mem_ctx, &names[0]); + + status = NT_STATUS_OK; + fail: + TALLOC_FREE(p); + cli_tdis(cli); + tcon_fail: + cli_state_restore_tcon_share(cli, orig_tcon, orig_share); + TALLOC_FREE(frame); + return status; +} + +/* convert a SID to a string, either numeric or username/group */ +void SidToString(struct cli_state *cli, fstring str, const struct dom_sid *sid, + bool numeric) +{ + char *domain = NULL; + char *name = NULL; + enum lsa_SidType type; + NTSTATUS status; + + sid_to_fstring(str, sid); + + if (numeric || cli == NULL) { + return; + } + + status = cli_lsa_lookup_sid(cli, sid, talloc_tos(), &type, + &domain, &name); + + if (!NT_STATUS_IS_OK(status)) { + return; + } + + if (*domain) { + slprintf(str, sizeof(fstring) - 1, "%s%s%s", + domain, lp_winbind_separator(), name); + } else { + fstrcpy(str, name); + } +} + +static NTSTATUS cli_lsa_lookup_name(struct cli_state *cli, + const char *name, + enum lsa_SidType *type, + struct dom_sid *sid) +{ + struct smbXcli_tcon *orig_tcon = NULL; + char *orig_share = NULL; + struct rpc_pipe_client *p = NULL; + struct policy_handle handle; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + struct dom_sid *sids; + enum lsa_SidType *types; + + if (cli_state_has_tcon(cli)) { + cli_state_save_tcon_share(cli, &orig_tcon, &orig_share); + } + + status = cli_tree_connect(cli, "IPC$", "?????", NULL); + if (!NT_STATUS_IS_OK(status)) { + goto tcon_fail; + } + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &p); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = rpccli_lsa_open_policy(p, talloc_tos(), True, + GENERIC_EXECUTE_ACCESS, &handle); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = rpccli_lsa_lookup_names(p, talloc_tos(), &handle, 1, &name, + NULL, 1, &sids, &types); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + *type = types[0]; + *sid = sids[0]; + + status = NT_STATUS_OK; + fail: + TALLOC_FREE(p); + cli_tdis(cli); + tcon_fail: + cli_state_restore_tcon_share(cli, orig_tcon, orig_share); + TALLOC_FREE(frame); + return status; +} + +/* convert a string to a SID, either numeric or username/group */ +bool StringToSid(struct cli_state *cli, struct dom_sid *sid, const char *str) +{ + enum lsa_SidType type; + + if (string_to_sid(sid, str)) { + return true; + } + + if (cli == NULL) { + return false; + } + + return NT_STATUS_IS_OK(cli_lsa_lookup_name(cli, str, &type, sid)); +} + +static void print_ace_flags(FILE *f, uint8_t flags) +{ + char *str = talloc_strdup(NULL, ""); + size_t len; + + if (flags & SEC_ACE_FLAG_OBJECT_INHERIT) { + talloc_asprintf_addbuf(&str, "OI|"); + } + if (flags & SEC_ACE_FLAG_CONTAINER_INHERIT) { + talloc_asprintf_addbuf(&str, "CI|"); + } + if (flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) { + talloc_asprintf_addbuf(&str, "NP|"); + } + if (flags & SEC_ACE_FLAG_INHERIT_ONLY) { + talloc_asprintf_addbuf(&str, "IO|"); + } + if (flags & SEC_ACE_FLAG_INHERITED_ACE) { + talloc_asprintf_addbuf(&str, "I|"); + } + if (str == NULL) { + goto out; + } + + /* Ignore define SEC_ACE_FLAG_SUCCESSFUL_ACCESS ( 0x40 ) + and SEC_ACE_FLAG_FAILED_ACCESS ( 0x80 ) as they're + audit ace flags. */ + + len = strlen(str); + if (len > 0) { + fprintf(f, "/%.*s/", (int)len-1, str); + } else { + fprintf(f, "/0x%x/", flags); + } + TALLOC_FREE(str); + return; + + out: + fprintf(f, "/0x%x/", flags); +} + +/* print an ACE on a FILE, using either numeric or ascii representation */ +void print_ace(struct cli_state *cli, FILE *f, struct security_ace *ace, + bool numeric) +{ + const struct perm_value *v; + fstring sidstr; + int do_print = 0; + uint32_t got_mask; + + SidToString(cli, sidstr, &ace->trustee, numeric); + + fprintf(f, "%s:", sidstr); + + if (numeric) { + fprintf(f, "%d/0x%x/0x%08x", + ace->type, ace->flags, ace->access_mask); + return; + } + + /* Ace type */ + + if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) { + fprintf(f, "ALLOWED"); + } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) { + fprintf(f, "DENIED"); + } else { + fprintf(f, "%d", ace->type); + } + + print_ace_flags(f, ace->flags); + + /* Standard permissions */ + + for (v = standard_values; v->perm; v++) { + if (ace->access_mask == v->mask) { + fprintf(f, "%s", v->perm); + return; + } + } + + /* Special permissions. Print out a hex value if we have + leftover bits in the mask. */ + + got_mask = ace->access_mask; + + again: + for (v = special_values; v->perm; v++) { + if ((ace->access_mask & v->mask) == v->mask) { + if (do_print) { + fprintf(f, "%s", v->perm); + } + got_mask &= ~v->mask; + } + } + + if (!do_print) { + if (got_mask != 0) { + fprintf(f, "0x%08x", ace->access_mask); + } else { + do_print = 1; + goto again; + } + } +} + +static bool parse_ace_flags(const char *str, unsigned int *pflags) +{ + const char *p = str; + *pflags = 0; + + while (*p) { + if (strnequal(p, "OI", 2)) { + *pflags |= SEC_ACE_FLAG_OBJECT_INHERIT; + p += 2; + } else if (strnequal(p, "CI", 2)) { + *pflags |= SEC_ACE_FLAG_CONTAINER_INHERIT; + p += 2; + } else if (strnequal(p, "NP", 2)) { + *pflags |= SEC_ACE_FLAG_NO_PROPAGATE_INHERIT; + p += 2; + } else if (strnequal(p, "IO", 2)) { + *pflags |= SEC_ACE_FLAG_INHERIT_ONLY; + p += 2; + } else if (*p == 'I') { + *pflags |= SEC_ACE_FLAG_INHERITED_ACE; + p += 1; + } else if (*p) { + return false; + } + + switch (*p) { + case '|': + p++; + + FALL_THROUGH; + case '\0': + continue; + default: + return false; + } + } + return true; +} + +/* parse an ACE in the same format as print_ace() */ +bool parse_ace(struct cli_state *cli, struct security_ace *ace, + const char *orig_str) +{ + char *p; + const char *cp; + char *tok; + unsigned int atype = 0; + unsigned int aflags = 0; + unsigned int amask = 0; + struct dom_sid sid; + uint32_t mask; + const struct perm_value *v; + char *str = SMB_STRDUP(orig_str); + TALLOC_CTX *frame = talloc_stackframe(); + + if (!str) { + TALLOC_FREE(frame); + return False; + } + + ZERO_STRUCTP(ace); + p = strchr_m(str,':'); + if (!p) { + printf("ACE '%s': missing ':'.\n", orig_str); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + *p = '\0'; + p++; + + if (!StringToSid(cli, &sid, str)) { + printf("ACE '%s': failed to convert '%s' to SID\n", + orig_str, str); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + + cp = p; + if (!next_token_talloc(frame, &cp, &tok, "/")) { + printf("ACE '%s': failed to find '/' character.\n", + orig_str); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + + if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_ALLOWED; + } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_DENIED; + + } else if (strnequal(tok, "0x", 2)) { + int result; + + result = sscanf(tok, "%x", &atype); + if (result == 0 || + (atype != SEC_ACE_TYPE_ACCESS_ALLOWED && + atype != SEC_ACE_TYPE_ACCESS_DENIED)) { + printf("ACE '%s': bad hex value for type at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return false; + } + } else if(tok[0] >= '0' && tok[0] <= '9') { + int result; + + result = sscanf(tok, "%u", &atype); + if (result == 0 || + (atype != SEC_ACE_TYPE_ACCESS_ALLOWED && + atype != SEC_ACE_TYPE_ACCESS_DENIED)) { + printf("ACE '%s': bad integer value for type at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return false; + } + } else { + printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + + if (!next_token_talloc(frame, &cp, &tok, "/")) { + printf("ACE '%s': bad flags entry at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + + if (tok[0] < '0' || tok[0] > '9') { + if (!parse_ace_flags(tok, &aflags)) { + printf("ACE '%s': bad named flags entry at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + } else if (strnequal(tok, "0x", 2)) { + if (!sscanf(tok, "%x", &aflags)) { + printf("ACE '%s': bad hex flags entry at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + } else { + if (!sscanf(tok, "%u", &aflags)) { + printf("ACE '%s': bad integer flags entry at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + } + + if (!next_token_talloc(frame, &cp, &tok, "/")) { + printf("ACE '%s': missing / at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + + if (strncmp(tok, "0x", 2) == 0) { + if (sscanf(tok, "%x", &amask) != 1) { + printf("ACE '%s': bad hex number at '%s'\n", + orig_str, tok); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + goto done; + } + + for (v = standard_values; v->perm; v++) { + if (strcmp(tok, v->perm) == 0) { + amask = v->mask; + goto done; + } + } + + p = tok; + + while(*p) { + bool found = False; + + for (v = special_values; v->perm; v++) { + if (v->perm[0] == *p) { + amask |= v->mask; + found = True; + } + } + + if (!found) { + printf("ACE '%s': bad permission value at '%s'\n", + orig_str, p); + SAFE_FREE(str); + TALLOC_FREE(frame); + return False; + } + p++; + } + + if (*p) { + TALLOC_FREE(frame); + SAFE_FREE(str); + return False; + } + + done: + mask = amask; + init_sec_ace(ace, &sid, atype, mask, aflags); + TALLOC_FREE(frame); + SAFE_FREE(str); + return True; +} + +static void print_acl_ctrl(FILE *file, uint16_t ctrl, bool numeric) +{ + int i; + const char* separator = ""; + + fprintf(file, "CONTROL:"); + if (numeric) { + fprintf(file, "0x%x\n", ctrl); + return; + } + + for (i = ARRAY_SIZE(sec_desc_ctrl_bits) - 1; i >= 0; i--) { + if (ctrl & sec_desc_ctrl_bits[i].mask) { + fprintf(file, "%s%s", + separator, sec_desc_ctrl_bits[i].str); + separator = "|"; + } + } + fputc('\n', file); +} + +/* print a ascii version of a security descriptor on a FILE handle */ +void sec_desc_print(struct cli_state *cli, FILE *f, + struct security_descriptor *sd, bool numeric) +{ + fstring sidstr; + uint32_t i; + + fprintf(f, "REVISION:%d\n", sd->revision); + print_acl_ctrl(f, sd->type, numeric); + + /* Print owner and group sid */ + + if (sd->owner_sid) { + SidToString(cli, sidstr, sd->owner_sid, numeric); + } else { + fstrcpy(sidstr, ""); + } + + fprintf(f, "OWNER:%s\n", sidstr); + + if (sd->group_sid) { + SidToString(cli, sidstr, sd->group_sid, numeric); + } else { + fstrcpy(sidstr, ""); + } + + fprintf(f, "GROUP:%s\n", sidstr); + + /* Print aces */ + for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + struct security_ace *ace = &sd->dacl->aces[i]; + fprintf(f, "ACL:"); + print_ace(cli, f, ace, numeric); + fprintf(f, "\n"); + } + +} diff --git a/source3/lib/util_sec.c b/source3/lib/util_sec.c new file mode 100644 index 0000000..ba6f109 --- /dev/null +++ b/source3/lib/util_sec.c @@ -0,0 +1,598 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Jeremy Allison 1998. + rewritten for version 2.0.6 by Tridge + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef AUTOCONF_TEST +#include "includes.h" +#include "system/passwd.h" /* uid_wrapper */ +#include "../lib/util/setid.h" + +#else +/* we are running this code in autoconf test mode to see which type of setuid + function works */ +#if defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> + +#ifdef HAVE_SYS_PRIV_H +#include <sys/priv.h> +#endif +#ifdef HAVE_SYS_ID_H +#include <sys/id.h> +#endif + +#define DEBUG(x, y) printf y +#define smb_panic(x) exit(1) +#endif + +/* are we running as non-root? This is used by the regression test code, + and potentially also for sites that want non-root smbd */ +static uid_t initial_uid; +static gid_t initial_gid; + +/**************************************************************************** +remember what uid we got started as - this allows us to run correctly +as non-root while catching trapdoor systems +****************************************************************************/ + +void sec_init(void) +{ + static int initialized; + + if (!initialized) { + +#ifndef AUTOCONF_TEST + if (uid_wrapper_enabled()) { + setenv("UID_WRAPPER_MYUID", "1", 1); + } +#endif + + initial_uid = geteuid(); + initial_gid = getegid(); + +#ifndef AUTOCONF_TEST + if (uid_wrapper_enabled()) { + unsetenv("UID_WRAPPER_MYUID"); + } +#endif + + initialized = 1; + } +} + +/**************************************************************************** +some code (eg. winbindd) needs to know what uid we started as +****************************************************************************/ +uid_t sec_initial_uid(void) +{ + return initial_uid; +} + +/**************************************************************************** +some code (eg. winbindd, profiling shm) needs to know what gid we started as +****************************************************************************/ +gid_t sec_initial_gid(void) +{ + return initial_gid; +} + +/** + * @brief Check if we are running in root mode. + * + * @return Return whether Samba has root privileges + */ +bool root_mode(void) +{ + uid_t euid; + + euid = geteuid(); + +#ifndef AUTOCONF_TEST + if (uid_wrapper_enabled()) { + return (euid == initial_uid || euid == (uid_t)0); + } +#endif + + return (initial_uid == euid); +} + +/**************************************************************************** +are we running in non-root mode? +****************************************************************************/ +bool non_root_mode(void) +{ + return (initial_uid != (uid_t)0); +} + +/**************************************************************************** +abort if we haven't set the uid correctly +****************************************************************************/ +static void assert_uid(uid_t ruid, uid_t euid) +{ + if ((euid != (uid_t)-1 && geteuid() != euid) || + (ruid != (uid_t)-1 && getuid() != ruid)) { + if (!non_root_mode()) { + DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n", + (int)ruid, (int)euid, + (int)getuid(), (int)geteuid())); + smb_panic("failed to set uid\n"); + exit(1); + } + } +} + +/**************************************************************************** +abort if we haven't set the gid correctly +****************************************************************************/ +static void assert_gid(gid_t rgid, gid_t egid) +{ + if ((egid != (gid_t)-1 && getegid() != egid) || + (rgid != (gid_t)-1 && getgid() != rgid)) { + if (!non_root_mode()) { + DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n", + (int)rgid, (int)egid, + (int)getgid(), (int)getegid(), + (int)getuid(), (int)geteuid())); + smb_panic("failed to set gid\n"); + exit(1); + } + } +} + +/**************************************************************************** + Gain root privilege before doing something. + We want to end up with ruid==euid==0 +****************************************************************************/ +void gain_root_privilege(void) +{ +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresuid(0,0,0); +#endif + +#if USE_SETEUID + samba_seteuid(0); +#endif + +#if USE_SETREUID + samba_setreuid(0, 0); +#endif + +#if USE_SETUIDX + samba_setuidx(ID_EFFECTIVE, 0); + samba_setuidx(ID_REAL, 0); +#endif + + /* this is needed on some systems */ + samba_setuid(0); + + assert_uid(0, 0); +} + + +/**************************************************************************** + Ensure our real and effective groups are zero. + we want to end up with rgid==egid==0 +****************************************************************************/ +void gain_root_group_privilege(void) +{ +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresgid(0,0,0); +#endif + +#if USE_SETREUID + samba_setregid(0,0); +#endif + +#if USE_SETEUID + samba_setegid(0); +#endif + +#if USE_SETUIDX + samba_setgidx(ID_EFFECTIVE, 0); + samba_setgidx(ID_REAL, 0); +#endif + + samba_setgid(0); + + assert_gid(0, 0); +} + + +/**************************************************************************** + Set effective uid, and possibly the real uid too. + We want to end up with either: + + ruid==uid and euid==uid + + or + + ruid==0 and euid==uid + + depending on what the local OS will allow us to regain root from. +****************************************************************************/ +void set_effective_uid(uid_t uid) +{ +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + /* Set the effective as well as the real uid. */ + if (samba_setresuid(uid,uid,-1) == -1) { + if (errno == EAGAIN) { + DEBUG(0, ("samba_setresuid failed with EAGAIN. uid(%d) " + "might be over its NPROC limit\n", + (int)uid)); + } + } +#endif + +#if USE_SETREUID + samba_setreuid(-1,uid); +#endif + +#if USE_SETEUID + samba_seteuid(uid); +#endif + +#if USE_SETUIDX + samba_setuidx(ID_EFFECTIVE, uid); +#endif + + assert_uid(-1, uid); +} + +/**************************************************************************** + Set *only* the effective gid. + we want to end up with rgid==0 and egid==gid +****************************************************************************/ +void set_effective_gid(gid_t gid) +{ +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresgid(-1,gid,-1); +#endif + +#if USE_SETREUID + samba_setregid(-1,gid); +#endif + +#if USE_SETEUID + samba_setegid(gid); +#endif + +#if USE_SETUIDX + samba_setgidx(ID_EFFECTIVE, gid); +#endif + + assert_gid(-1, gid); +} + +static uid_t saved_euid, saved_ruid; +static gid_t saved_egid, saved_rgid; + +/**************************************************************************** + save the real and effective uid for later restoration. Used by the quotas + code +****************************************************************************/ +void save_re_uid(void) +{ + saved_ruid = getuid(); + saved_euid = geteuid(); +} + + +/**************************************************************************** + and restore them! +****************************************************************************/ + +void restore_re_uid_fromroot(void) +{ +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresuid(saved_ruid, saved_euid, -1); +#elif USE_SETREUID + samba_setreuid(saved_ruid, -1); + samba_setreuid(-1,saved_euid); +#elif USE_SETUIDX + samba_setuidx(ID_REAL, saved_ruid); + samba_setuidx(ID_EFFECTIVE, saved_euid); +#else + set_effective_uid(saved_euid); + if (getuid() != saved_ruid) + samba_setuid(saved_ruid); + set_effective_uid(saved_euid); +#endif + + assert_uid(saved_ruid, saved_euid); +} + +void restore_re_uid(void) +{ + set_effective_uid(0); + restore_re_uid_fromroot(); +} + +/**************************************************************************** + save the real and effective gid for later restoration. Used by the + getgroups code +****************************************************************************/ +void save_re_gid(void) +{ + saved_rgid = getgid(); + saved_egid = getegid(); +} + +/**************************************************************************** + and restore them! +****************************************************************************/ +void restore_re_gid(void) +{ +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresgid(saved_rgid, saved_egid, -1); +#elif USE_SETREUID + samba_setregid(saved_rgid, -1); + samba_setregid(-1,saved_egid); +#elif USE_SETUIDX + samba_setgidx(ID_REAL, saved_rgid); + samba_setgidx(ID_EFFECTIVE, saved_egid); +#else + set_effective_gid(saved_egid); + if (getgid() != saved_rgid) + samba_setgid(saved_rgid); + set_effective_gid(saved_egid); +#endif + + assert_gid(saved_rgid, saved_egid); +} + + +/**************************************************************************** + set the real AND effective uid to the current effective uid in a way that + allows root to be regained. + This is only possible on some platforms. +****************************************************************************/ +int set_re_uid(void) +{ + uid_t uid = geteuid(); + +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresuid(uid, uid, -1); +#endif + +#if USE_SETREUID + samba_setreuid(0, 0); + samba_setreuid(uid, -1); + samba_setreuid(-1, uid); +#endif + +#if USE_SETEUID + /* can't be done */ + return -1; +#endif + +#if USE_SETUIDX + /* can't be done */ + return -1; +#endif + + assert_uid(uid, uid); + return 0; +} + + +/**************************************************************************** + Become the specified uid and gid - permanently ! + there should be no way back if possible +****************************************************************************/ +void become_user_permanently(uid_t uid, gid_t gid) +{ + /* + * First - gain root privilege. We do this to ensure + * we can lose it again. + */ + + gain_root_privilege(); + gain_root_group_privilege(); + +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresgid(gid,gid,gid); + samba_setgid(gid); + samba_setresuid(uid,uid,uid); + samba_setuid(uid); +#endif + +#if USE_SETREUID + samba_setregid(gid,gid); + samba_setgid(gid); + samba_setreuid(uid,uid); + samba_setuid(uid); +#endif + +#if USE_SETEUID + samba_setegid(gid); + samba_setgid(gid); + samba_setuid(uid); + samba_seteuid(uid); + samba_setuid(uid); +#endif + +#if USE_SETUIDX + samba_setgidx(ID_REAL, gid); + samba_setgidx(ID_EFFECTIVE, gid); + samba_setgid(gid); + samba_setuidx(ID_REAL, uid); + samba_setuidx(ID_EFFECTIVE, uid); + samba_setuid(uid); +#endif + + assert_uid(uid, uid); + assert_gid(gid, gid); +} + +#if defined(HAVE_LINUX_THREAD_CREDENTIALS) && defined(HAVE___THREAD) + struct set_thread_credentials_cache { + bool active; + uid_t uid; + gid_t gid; + size_t setlen; + uintptr_t gidset; + }; +static __thread struct set_thread_credentials_cache cache; +#endif + +/********************************************************** + Function to set thread specific credentials. Leave + saved-set uid/gid alone.Must be thread-safe code. +**********************************************************/ + +int set_thread_credentials(uid_t uid, + gid_t gid, + size_t setlen, + const gid_t *gidset) +{ +#if defined(HAVE_LINUX_THREAD_CREDENTIALS) + /* + * With Linux thread-specific credentials + * we know we have setresuid/setresgid + * available. + */ +#ifdef HAVE___THREAD + if (cache.active && + cache.uid == uid && + cache.gid == gid && + cache.setlen == setlen && + (const gid_t *)cache.gidset == gidset) + { + return 0; + } +#endif /* HAVE___THREAD */ + + /* Become root. */ + /* Set ru=0, eu=0 */ + if (samba_setresuid(0, 0, -1) != 0) { + return -1; + } + /* Set our primary gid. */ + /* Set rg=gid, eg=gid */ + if (samba_setresgid(gid, gid, -1) != 0) { + return -1; + } + /* Set extra groups list. */ + if (samba_setgroups(setlen, gidset) != 0) { + return -1; + } + /* Become the requested user. */ + /* Set ru=uid, eu=uid */ + if (samba_setresuid(uid, uid, -1) != 0) { + return -1; + } + if (geteuid() != uid || getuid() != uid || + getegid() != gid || getgid() != gid) { + smb_panic("set_thread_credentials failed\n"); + return -1; + } + +#ifdef HAVE___THREAD + cache.active = true; + cache.uid = uid; + cache.gid = gid; + cache.setlen = setlen; + cache.gidset = (uintptr_t)gidset; +#endif /* HAVE___THREAD */ + + return 0; +#else + errno = ENOSYS; + return -1; +#endif +} + +#ifdef AUTOCONF_TEST + +/**************************************************************************** +this function just checks that we don't get ENOSYS back +****************************************************************************/ +static int have_syscall(void) +{ + errno = 0; + +#if defined(USE_SETRESUID) || defined(HAVE_LINUX_THREAD_CREDENTIALS) + samba_setresuid(-1,-1,-1); +#endif + +#if USE_SETREUID + samba_setreuid(-1,-1); +#endif + +#if USE_SETEUID + samba_seteuid(-1); +#endif + +#if USE_SETUIDX + samba_setuidx(ID_EFFECTIVE, -1); +#endif + + if (errno == ENOSYS) { + return -1; + } + return 0; +} + +int main(void) +{ + if (getuid() != 0) { +#if (defined(AIX) && defined(USE_SETREUID)) + /* setreuid is badly broken on AIX 4.1, we avoid it completely */ + fprintf(stderr,"avoiding possibly broken setreuid\n"); + exit(1); +#endif + + /* if not running as root then at least check to see if we get ENOSYS - this + handles Linux 2.0.x with glibc 2.1 */ + fprintf(stderr,"not running as root: checking for ENOSYS\n"); + exit(have_syscall()); + } + + gain_root_privilege(); + gain_root_group_privilege(); + set_effective_gid(1); + set_effective_uid(1); + save_re_uid(); + restore_re_uid(); + gain_root_privilege(); + gain_root_group_privilege(); + become_user_permanently(1, 1); + samba_setuid(0); + if (getuid() == 0) { + fprintf(stderr,"uid not set permanently\n"); + exit(1); + } + + printf("OK\n"); + + exit(0); +} +#endif + +/**************************************************************************** +Check if we are setuid root. Used in libsmb and smbpasswd paranoia checks. +****************************************************************************/ +bool is_setuid_root(void) +{ + return (geteuid() == (uid_t)0) && (getuid() != (uid_t)0); +} diff --git a/source3/lib/util_sid.c b/source3/lib/util_sid.c new file mode 100644 index 0000000..fd767f9 --- /dev/null +++ b/source3/lib/util_sid.c @@ -0,0 +1,206 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Luke Kenneth Caseson Leighton 1998-1999 + Copyright (C) Jeremy Allison 1999 + Copyright (C) Stefan (metze) Metzmacher 2002 + Copyright (C) Simo Sorce 2002 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "../libcli/security/security.h" +#include "lib/util/string_wrappers.h" +#include "source3/lib/util_specialsids.h" + + +/***************************************************************** + Convert a SID to an ascii string. +*****************************************************************/ + +char *sid_to_fstring(fstring sidstr_out, const struct dom_sid *sid) +{ + struct dom_sid_buf buf; + fstrcpy(sidstr_out, dom_sid_str_buf(sid, &buf)); + return sidstr_out; +} + +/***************************************************************** + Write a sid out into on-the-wire format. +*****************************************************************/ + +bool sid_linearize(uint8_t *outbuf, size_t len, const struct dom_sid *sid) +{ + struct ndr_push ndr = { + .data = outbuf, .alloc_size = len, .fixed_buf_size = true, + }; + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_dom_sid(&ndr, NDR_SCALARS|NDR_BUFFERS, sid); + return NDR_ERR_CODE_IS_SUCCESS(ndr_err); +} + +/***************************************************************** + Returns true if SID is internal (and non-mappable). +*****************************************************************/ + +bool non_mappable_sid(struct dom_sid *sid) +{ + struct dom_sid dom; + + sid_copy(&dom, sid); + sid_split_rid(&dom, NULL); + + if (dom_sid_equal(&dom, &global_sid_Builtin)) + return True; + + if (dom_sid_equal(&dom, &global_sid_NT_Authority)) + return True; + + return False; +} + +/***************************************************************** + Return the binary string representation of a struct dom_sid. + Caller must free. +*****************************************************************/ + +char *sid_binstring_hex_talloc(TALLOC_CTX *mem_ctx, const struct dom_sid *sid) +{ + int len = ndr_size_dom_sid(sid, 0); + uint8_t buf[len]; + sid_linearize(buf, len, sid); + return hex_encode_talloc(mem_ctx, buf, len); +} + +NTSTATUS sid_array_from_info3(TALLOC_CTX *mem_ctx, + const struct netr_SamInfo3 *info3, + struct dom_sid **user_sids, + uint32_t *num_user_sids, + bool include_user_group_rid) +{ + NTSTATUS status; + struct dom_sid sid; + struct dom_sid *sid_array = NULL; + uint32_t num_sids = 0; + uint32_t i; + + if (include_user_group_rid) { + if (!sid_compose(&sid, info3->base.domain_sid, info3->base.rid)) { + DEBUG(3, ("could not compose user SID from rid 0x%x\n", + info3->base.rid)); + return NT_STATUS_INVALID_PARAMETER; + } + status = add_sid_to_array(mem_ctx, &sid, &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not append user SID from rid 0x%x\n", + info3->base.rid)); + return status; + } + } + + if (!sid_compose(&sid, info3->base.domain_sid, info3->base.primary_gid)) { + DEBUG(3, ("could not compose group SID from rid 0x%x\n", + info3->base.primary_gid)); + return NT_STATUS_INVALID_PARAMETER; + } + status = add_sid_to_array(mem_ctx, &sid, &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not append group SID from rid 0x%x\n", + info3->base.rid)); + return status; + } + + for (i = 0; i < info3->base.groups.count; i++) { + /* Don't add the primary group sid twice. */ + if (!sid_compose(&sid, info3->base.domain_sid, + info3->base.groups.rids[i].rid)) { + DEBUG(3, ("could not compose SID from additional group " + "rid 0x%x\n", info3->base.groups.rids[i].rid)); + return NT_STATUS_INVALID_PARAMETER; + } + status = add_sid_to_array(mem_ctx, &sid, &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not append SID from additional group " + "rid 0x%x\n", info3->base.groups.rids[i].rid)); + return status; + } + } + + /* Copy 'other' sids. We need to do sid filtering here to + prevent possible elevation of privileges. See: + + http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp + */ + + for (i = 0; i < info3->sidcount; i++) { + + if (sid_check_is_in_asserted_identity(info3->sids[i].sid)) { + continue; + } + + status = add_sid_to_array(mem_ctx, info3->sids[i].sid, + &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + struct dom_sid_buf buf; + DEBUG(3, ("could not add SID to array: %s\n", + dom_sid_str_buf(info3->sids[i].sid, &buf))); + return status; + } + } + + *user_sids = sid_array; + *num_user_sids = num_sids; + + return NT_STATUS_OK; +} + +bool security_token_find_npa_flags(const struct security_token *token, + uint32_t *_flags) +{ + const struct dom_sid *npa_flags_sid = NULL; + size_t num_npa_sids; + + num_npa_sids = + security_token_count_flag_sids(token, + &global_sid_Samba_NPA_Flags, + 1, + &npa_flags_sid); + if (num_npa_sids != 1) { + return false; + } + + sid_peek_rid(npa_flags_sid, _flags); + return true; +} + +void security_token_del_npa_flags(struct security_token *token) +{ + const struct dom_sid *npa_flags_sid = NULL; + size_t num_npa_sids; + + num_npa_sids = + security_token_count_flag_sids(token, + &global_sid_Samba_NPA_Flags, + 1, + &npa_flags_sid); + SMB_ASSERT(num_npa_sids == 1); + + del_sid_from_array(npa_flags_sid, &token->sids, &token->num_sids); +} diff --git a/source3/lib/util_sid_passdb.c b/source3/lib/util_sid_passdb.c new file mode 100644 index 0000000..e67a27d --- /dev/null +++ b/source3/lib/util_sid_passdb.c @@ -0,0 +1,131 @@ +/* + Unix SMB/CIFS implementation. + sid utility functions + + Copyright (C) Michael Adam 2012 + + 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/util_sid_passdb.h" +#include "lib/util_unixsids.h" +#include "passdb/machine_sid.h" +#include "passdb.h" + +/** + * check whether this is an object-sid that should + * be treated by the passdb, e.g. for id-mapping. + */ +bool sid_check_object_is_for_passdb(const struct dom_sid *sid) +{ + if (sid_check_is_in_our_sam(sid) && pdb_is_responsible_for_our_sam()) { + return true; + } + + if (sid_check_is_in_builtin(sid) && pdb_is_responsible_for_builtin()) { + return true; + } + + if (sid_check_is_in_wellknown_domain(sid) && + pdb_is_responsible_for_wellknown()) + { + return true; + } + + if (sid_check_is_in_unix_users(sid) && + pdb_is_responsible_for_unix_users()) + { + return true; + } + + if (sid_check_is_in_unix_groups(sid) && + pdb_is_responsible_for_unix_groups()) + { + return true; + } + + if (pdb_is_responsible_for_everything_else()) + { + return true; + } + + return false; +} + +/** + * check whether this is an object- or domain-sid that should + * be treated by the passdb, e.g. for id-mapping. + */ +bool sid_check_is_for_passdb(const struct dom_sid *sid) +{ + if (sid_check_is_our_sam(sid) && pdb_is_responsible_for_our_sam()) { + return true; + } + + if (sid_check_is_in_our_sam(sid) && pdb_is_responsible_for_our_sam()) { + return true; + } + + if (sid_check_is_builtin(sid) && pdb_is_responsible_for_builtin()) { + return true; + } + + if (sid_check_is_in_builtin(sid) && pdb_is_responsible_for_builtin()) { + return true; + } + + if (sid_check_is_wellknown_domain(sid, NULL) && + pdb_is_responsible_for_wellknown()) + { + return true; + } + + if (sid_check_is_in_wellknown_domain(sid) && + pdb_is_responsible_for_wellknown()) + { + return true; + } + + if (sid_check_is_unix_users(sid) && + pdb_is_responsible_for_unix_users()) + { + return true; + } + + if (sid_check_is_in_unix_users(sid) && + pdb_is_responsible_for_unix_users()) + { + return true; + } + + if (sid_check_is_unix_groups(sid) && + pdb_is_responsible_for_unix_groups()) + { + return true; + } + + if (sid_check_is_in_unix_groups(sid) && + pdb_is_responsible_for_unix_groups()) + { + return true; + } + + if (pdb_is_responsible_for_everything_else()) + { + return true; + } + + return false; +} diff --git a/source3/lib/util_sid_passdb.h b/source3/lib/util_sid_passdb.h new file mode 100644 index 0000000..381d63c --- /dev/null +++ b/source3/lib/util_sid_passdb.h @@ -0,0 +1,36 @@ +/* + Unix SMB/CIFS implementation. + sid utility functions + + Copyright (C) Michael Adam 2012 + + 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_UTIL_SID_PASSDB_H__ +#define __LIB_UTIL_SID_PASSDB_H__ + +/** + * check whether this is an object-sid that should + * be treated by the passdb, e.g. for id-mapping. + */ +bool sid_check_object_is_for_passdb(const struct dom_sid *sid); + +/** + * check whether this is an object- or domain-sid that should + * be treated by the passdb, e.g. for id-mapping. + */ +bool sid_check_is_for_passdb(const struct dom_sid *sid); + +#endif /* __LIB_UTIL_SID_PASSDB_H__ */ diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c new file mode 100644 index 0000000..5ff875c --- /dev/null +++ b/source3/lib/util_sock.c @@ -0,0 +1,1232 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Tim Potter 2000-2001 + Copyright (C) Jeremy Allison 1992-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "../lib/util/memcache.h" +#include "../lib/async_req/async_sock.h" +#include "../lib/util/select.h" +#include "lib/socket/interfaces.h" +#include "../lib/util/tevent_unix.h" +#include "../lib/util/tevent_ntstatus.h" +#include "../lib/tsocket/tsocket.h" +#include "lib/util/sys_rw.h" +#include "lib/util/sys_rw_data.h" +#include "source3/lib/util_tsock.h" + +/**************************************************************************** + Determine if a file descriptor is in fact a socket. +****************************************************************************/ + +bool is_a_socket(int fd) +{ + int v; + socklen_t l; + l = sizeof(int); + return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0); +} + +/**************************************************************************** + Read data from a file descriptor with a timeout in msec. + mincount = if timeout, minimum to read before returning + maxcount = number to be read. + time_out = timeout in milliseconds + NB. This can be called with a non-socket fd, don't change + sys_read() to sys_recv() or other socket call. +****************************************************************************/ + +NTSTATUS read_fd_with_timeout(int fd, char *buf, + size_t mincnt, size_t maxcnt, + unsigned int time_out, + size_t *size_ret) +{ + int pollrtn; + ssize_t readret; + size_t nread = 0; + + /* just checking .... */ + if (maxcnt <= 0) + return NT_STATUS_OK; + + /* Blocking read */ + if (time_out == 0) { + if (mincnt == 0) { + mincnt = maxcnt; + } + + while (nread < mincnt) { + readret = sys_read(fd, buf + nread, maxcnt - nread); + + if (readret == 0) { + DEBUG(5,("read_fd_with_timeout: " + "blocking read. EOF from client.\n")); + return NT_STATUS_END_OF_FILE; + } + + if (readret == -1) { + return map_nt_error_from_unix(errno); + } + nread += readret; + } + goto done; + } + + /* Most difficult - timeout read */ + /* If this is ever called on a disk file and + mincnt is greater then the filesize then + system performance will suffer severely as + select always returns true on disk files */ + + for (nread=0; nread < mincnt; ) { + int revents; + + pollrtn = poll_intr_one_fd(fd, POLLIN|POLLHUP, time_out, + &revents); + + /* Check if error */ + if (pollrtn == -1) { + return map_nt_error_from_unix(errno); + } + + /* Did we timeout ? */ + if ((pollrtn == 0) || + ((revents & (POLLIN|POLLHUP|POLLERR)) == 0)) { + DEBUG(10,("read_fd_with_timeout: timeout read. " + "select timed out.\n")); + return NT_STATUS_IO_TIMEOUT; + } + + readret = sys_read(fd, buf+nread, maxcnt-nread); + + if (readret == 0) { + /* we got EOF on the file descriptor */ + DEBUG(5,("read_fd_with_timeout: timeout read. " + "EOF from client.\n")); + return NT_STATUS_END_OF_FILE; + } + + if (readret == -1) { + return map_nt_error_from_unix(errno); + } + + nread += readret; + } + + done: + /* Return the number we got */ + if (size_ret) { + *size_ret = nread; + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Read data from an fd, reading exactly N bytes. + NB. This can be called with a non-socket fd, don't add dependencies + on socket calls. +****************************************************************************/ + +NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N) +{ + return read_fd_with_timeout(fd, buffer, N, N, 0, NULL); +} + +/**************************************************************************** + Read 4 bytes of a smb packet and return the smb length of the packet. + Store the result in the buffer. + This version of the function will return a length of zero on receiving + a keepalive packet. + Timeout is in milliseconds. +****************************************************************************/ + +NTSTATUS read_smb_length_return_keepalive(int fd, char *inbuf, + unsigned int timeout, + size_t *len) +{ + int msg_type; + NTSTATUS status; + + status = read_fd_with_timeout(fd, inbuf, 4, 4, timeout, NULL); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *len = smb_len(inbuf); + msg_type = CVAL(inbuf,0); + + if (msg_type == NBSSkeepalive) { + DEBUG(5,("Got keepalive packet\n")); + } + + DEBUG(10,("got smb length of %lu\n",(unsigned long)(*len))); + + return NT_STATUS_OK; +} + +/**************************************************************************** + Read an smb from a fd. + The timeout is in milliseconds. + This function will return on receipt of a session keepalive packet. + maxlen is the max number of bytes to return, not including the 4 byte + length. If zero it means buflen limit. + Doesn't check the MAC on signed packets. +****************************************************************************/ + +NTSTATUS receive_smb_raw(int fd, char *buffer, size_t buflen, unsigned int timeout, + size_t maxlen, size_t *p_len) +{ + size_t len; + NTSTATUS status; + + status = read_smb_length_return_keepalive(fd,buffer,timeout,&len); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("read_fd_with_timeout failed, read " + "error = %s.\n", nt_errstr(status))); + return status; + } + + if (len > buflen) { + DEBUG(0,("Invalid packet length! (%lu bytes).\n", + (unsigned long)len)); + return NT_STATUS_INVALID_PARAMETER; + } + + if(len > 0) { + if (maxlen) { + len = MIN(len,maxlen); + } + + status = read_fd_with_timeout( + fd, buffer+4, len, len, timeout, &len); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("read_fd_with_timeout failed, read error = " + "%s.\n", nt_errstr(status))); + return status; + } + + /* not all of samba3 properly checks for packet-termination + * of strings. This ensures that we don't run off into + * empty space. */ + SSVAL(buffer+4,len, 0); + } + + *p_len = len; + return NT_STATUS_OK; +} + +/* + * Open a socket of the specified type, port, and address for incoming data. + * + * Return sock or -errno + */ + +int open_socket_in( + int type, + const struct sockaddr_storage *paddr, + uint16_t port, + bool rebind) +{ + struct samba_sockaddr addr = { + .sa_socklen = sizeof(struct sockaddr_storage), + .u.ss = *paddr, + }; + int ret, sock = -1; + int val = rebind ? 1 : 0; + bool ok; + + switch (addr.u.sa.sa_family) { + case AF_INET6: + addr.sa_socklen = sizeof(struct sockaddr_in6); + break; + case AF_INET: + addr.sa_socklen = sizeof(struct sockaddr_in); + break; + } + + ok = samba_sockaddr_set_port(&addr, port); + if (!ok) { + ret = -EINVAL; + DBG_DEBUG("samba_sockaddr_set_port failed\n"); + goto fail; + } + + sock = socket(addr.u.ss.ss_family, type, 0 ); + if (sock == -1) { + ret = -errno; + DBG_DEBUG("socket() failed: %s\n", strerror(errno)); + goto fail; + } + + ret = setsockopt( + sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); + if (ret == -1) { + ret = -errno; + DBG_DEBUG("setsockopt(SO_REUSEADDR) failed: %s\n", + strerror(errno)); + goto fail; + } + +#ifdef SO_REUSEPORT + ret = setsockopt( + sock, SOL_SOCKET, SO_REUSEPORT, (char *)&val, sizeof(val)); + if (ret == -1) { + ret = -errno; + DBG_DEBUG("setsockopt(SO_REUSEPORT) failed: %s\n", + strerror(errno)); + goto fail; + } +#endif /* SO_REUSEPORT */ + +#ifdef HAVE_IPV6 + /* + * As IPV6_V6ONLY is the default on some systems, + * we better try to be consistent and always use it. + * + * This also avoids using IPv4 via AF_INET6 sockets + * and makes sure %I never resolves to a '::ffff:192.168.0.1' + * string. + */ + if (addr.u.ss.ss_family == AF_INET6) { + + val = 1; + + ret = setsockopt( + sock, + IPPROTO_IPV6, + IPV6_V6ONLY, + (const void *)&val, + sizeof(val)); + if (ret == -1) { + ret = -errno; + DBG_DEBUG("setsockopt(IPV6_V6ONLY) failed: %s\n", + strerror(errno)); + goto fail; + } + } +#endif + + /* now we've got a socket - we need to bind it */ + ret = bind(sock, &addr.u.sa, addr.sa_socklen); + if (ret == -1) { + char addrstr[INET6_ADDRSTRLEN]; + + ret = -errno; + + print_sockaddr(addrstr, sizeof(addrstr), &addr.u.ss); + DBG_DEBUG("bind for %s port %"PRIu16" failed: %s\n", + addrstr, + port, + strerror(-ret)); + goto fail; + } + + DBG_DEBUG("bind succeeded on port %"PRIu16"\n", port); + + return sock; + +fail: + if (sock != -1) { + close(sock); + sock = -1; + } + return ret; + } + +struct open_socket_out_state { + int fd; + struct tevent_context *ev; + struct sockaddr_storage ss; + socklen_t salen; + uint16_t port; + struct tevent_req *connect_subreq; +}; + +static void open_socket_out_connected(struct tevent_req *subreq); + +static void open_socket_out_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct open_socket_out_state *state = + tevent_req_data(req, struct open_socket_out_state); + + /* + * Make sure that the async_connect_send subreq has a chance to reset + * fcntl before the socket goes away. + */ + TALLOC_FREE(state->connect_subreq); + + if (req_state == TEVENT_REQ_DONE) { + /* + * we keep the socket open for the caller to use + */ + return; + } + + if (state->fd != -1) { + close(state->fd); + state->fd = -1; + } +} + +/**************************************************************************** + Create an outgoing socket. timeout is in milliseconds. +**************************************************************************/ + +struct tevent_req *open_socket_out_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct sockaddr_storage *pss, + uint16_t port, + int timeout) +{ + char addr[INET6_ADDRSTRLEN]; + struct tevent_req *req; + struct open_socket_out_state *state; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct open_socket_out_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ss = *pss; + state->port = port; + state->salen = -1; + + state->fd = socket(state->ss.ss_family, SOCK_STREAM, 0); + if (state->fd == -1) { + status = map_nt_error_from_unix(errno); + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + + tevent_req_set_cleanup_fn(req, open_socket_out_cleanup); + + if ((timeout != 0) && + !tevent_req_set_endtime( + req, ev, timeval_current_ofs_msec(timeout))) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + +#if defined(HAVE_IPV6) + if (pss->ss_family == AF_INET6) { + struct sockaddr_in6 *psa6; + psa6 = (struct sockaddr_in6 *)&state->ss; + psa6->sin6_port = htons(port); + if (psa6->sin6_scope_id == 0 + && IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) { + setup_linklocal_scope_id( + (struct sockaddr *)&(state->ss)); + } + state->salen = sizeof(struct sockaddr_in6); + } +#endif + if (pss->ss_family == AF_INET) { + struct sockaddr_in *psa; + psa = (struct sockaddr_in *)&state->ss; + psa->sin_port = htons(port); + state->salen = sizeof(struct sockaddr_in); + } + + if (pss->ss_family == AF_UNIX) { + state->salen = sizeof(struct sockaddr_un); + } + + print_sockaddr(addr, sizeof(addr), &state->ss); + DEBUG(3,("Connecting to %s at port %u\n", addr, (unsigned int)port)); + + state->connect_subreq = async_connect_send( + state, state->ev, state->fd, (struct sockaddr *)&state->ss, + state->salen, NULL, NULL, NULL); + if (tevent_req_nomem(state->connect_subreq, NULL)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->connect_subreq, + open_socket_out_connected, req); + return req; +} + +static void open_socket_out_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct open_socket_out_state *state = + tevent_req_data(req, struct open_socket_out_state); + int ret; + int sys_errno; + + ret = async_connect_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + state->connect_subreq = NULL; + if (ret == 0) { + tevent_req_done(req); + return; + } + + tevent_req_nterror(req, map_nt_error_from_unix(sys_errno)); +} + +NTSTATUS open_socket_out_recv(struct tevent_req *req, int *pfd) +{ + struct open_socket_out_state *state = + tevent_req_data(req, struct open_socket_out_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + *pfd = state->fd; + state->fd = -1; + tevent_req_received(req); + return NT_STATUS_OK; +} + +/** +* @brief open a socket +* +* @param pss a struct sockaddr_storage defining the address to connect to +* @param port to connect to +* @param timeout in MILLISECONDS +* @param pfd file descriptor returned +* +* @return NTSTATUS code +*/ +NTSTATUS open_socket_out(const struct sockaddr_storage *pss, uint16_t port, + int timeout, int *pfd) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = open_socket_out_send(frame, ev, pss, port, timeout); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + status = open_socket_out_recv(req, pfd); + fail: + TALLOC_FREE(frame); + return status; +} + +struct open_socket_out_defer_state { + struct tevent_context *ev; + struct sockaddr_storage ss; + uint16_t port; + int timeout; + int fd; +}; + +static void open_socket_out_defer_waited(struct tevent_req *subreq); +static void open_socket_out_defer_connected(struct tevent_req *subreq); + +struct tevent_req *open_socket_out_defer_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct timeval wait_time, + const struct sockaddr_storage *pss, + uint16_t port, + int timeout) +{ + struct tevent_req *req, *subreq; + struct open_socket_out_defer_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct open_socket_out_defer_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ss = *pss; + state->port = port; + state->timeout = timeout; + + subreq = tevent_wakeup_send( + state, ev, + timeval_current_ofs(wait_time.tv_sec, wait_time.tv_usec)); + if (subreq == NULL) { + goto fail; + } + tevent_req_set_callback(subreq, open_socket_out_defer_waited, req); + return req; + fail: + TALLOC_FREE(req); + return NULL; +} + +static void open_socket_out_defer_waited(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct open_socket_out_defer_state *state = tevent_req_data( + req, struct open_socket_out_defer_state); + bool ret; + + ret = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ret) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + subreq = open_socket_out_send(state, state->ev, &state->ss, + state->port, state->timeout); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, open_socket_out_defer_connected, req); +} + +static void open_socket_out_defer_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct open_socket_out_defer_state *state = tevent_req_data( + req, struct open_socket_out_defer_state); + NTSTATUS status; + + status = open_socket_out_recv(subreq, &state->fd); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS open_socket_out_defer_recv(struct tevent_req *req, int *pfd) +{ + struct open_socket_out_defer_state *state = tevent_req_data( + req, struct open_socket_out_defer_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfd = state->fd; + state->fd = -1; + return NT_STATUS_OK; +} + +/******************************************************************* + Return the IP addr of the remote end of a socket as a string. + Optionally return the struct sockaddr_storage. + ******************************************************************/ + +static const char *get_peer_addr_internal(int fd, + char *addr_buf, + size_t addr_buf_len, + struct sockaddr *pss, + socklen_t *plength) +{ + struct sockaddr_storage ss; + socklen_t length = sizeof(ss); + + strlcpy(addr_buf,"0.0.0.0",addr_buf_len); + + if (fd == -1) { + return addr_buf; + } + + if (pss == NULL) { + pss = (struct sockaddr *)&ss; + plength = &length; + } + + if (getpeername(fd, (struct sockaddr *)pss, plength) < 0) { + int level = (errno == ENOTCONN) ? 2 : 0; + DEBUG(level, ("getpeername failed. Error was %s\n", + strerror(errno))); + return addr_buf; + } + + print_sockaddr_len(addr_buf, + addr_buf_len, + pss, + *plength); + return addr_buf; +} + +/******************************************************************* + Matchname - determine if host name matches IP address. Used to + confirm a hostname lookup to prevent spoof attacks. +******************************************************************/ + +static bool matchname(const char *remotehost, + const struct sockaddr *pss, + socklen_t len) +{ + struct addrinfo *res = NULL; + struct addrinfo *ailist = NULL; + char addr_buf[INET6_ADDRSTRLEN]; + bool ret = interpret_string_addr_internal(&ailist, + remotehost, + AI_ADDRCONFIG|AI_CANONNAME); + + if (!ret || ailist == NULL) { + DEBUG(3,("matchname: getaddrinfo failed for " + "name %s [%s]\n", + remotehost, + gai_strerror(ret) )); + return false; + } + + /* + * Make sure that getaddrinfo() returns the "correct" host name. + */ + + if (ailist->ai_canonname == NULL || + (!strequal(remotehost, ailist->ai_canonname) && + !strequal(remotehost, "localhost"))) { + DEBUG(0,("matchname: host name/name mismatch: %s != %s\n", + remotehost, + ailist->ai_canonname ? + ailist->ai_canonname : "(NULL)")); + freeaddrinfo(ailist); + return false; + } + + /* Look up the host address in the address list we just got. */ + for (res = ailist; res; res = res->ai_next) { + if (!res->ai_addr) { + continue; + } + if (sockaddr_equal((const struct sockaddr *)res->ai_addr, + (const struct sockaddr *)pss)) { + freeaddrinfo(ailist); + return true; + } + } + + /* + * The host name does not map to the original host address. Perhaps + * someone has compromised a name server. More likely someone botched + * it, but that could be dangerous, too. + */ + + DEBUG(0,("matchname: host name/address mismatch: %s != %s\n", + print_sockaddr_len(addr_buf, + sizeof(addr_buf), + pss, + len), + ailist->ai_canonname ? ailist->ai_canonname : "(NULL)")); + + if (ailist) { + freeaddrinfo(ailist); + } + return false; +} + +/******************************************************************* + Deal with the singleton cache. +******************************************************************/ + +struct name_addr_pair { + struct sockaddr_storage ss; + const char *name; +}; + +/******************************************************************* + Lookup a name/addr pair. Returns memory allocated from memcache. +******************************************************************/ + +static bool lookup_nc(struct name_addr_pair *nc) +{ + DATA_BLOB tmp; + + ZERO_STRUCTP(nc); + + if (!memcache_lookup( + NULL, SINGLETON_CACHE, + data_blob_string_const_null("get_peer_name"), + &tmp)) { + return false; + } + + memcpy(&nc->ss, tmp.data, sizeof(nc->ss)); + nc->name = (const char *)tmp.data + sizeof(nc->ss); + return true; +} + +/******************************************************************* + Save a name/addr pair. +******************************************************************/ + +static void store_nc(const struct name_addr_pair *nc) +{ + DATA_BLOB tmp; + size_t namelen = strlen(nc->name); + + tmp = data_blob(NULL, sizeof(nc->ss) + namelen + 1); + if (!tmp.data) { + return; + } + memcpy(tmp.data, &nc->ss, sizeof(nc->ss)); + memcpy(tmp.data+sizeof(nc->ss), nc->name, namelen+1); + + memcache_add(NULL, SINGLETON_CACHE, + data_blob_string_const_null("get_peer_name"), + tmp); + data_blob_free(&tmp); +} + +/******************************************************************* + Return the IP addr of the remote end of a socket as a string. + ******************************************************************/ + +const char *get_peer_addr(int fd, char *addr, size_t addr_len) +{ + return get_peer_addr_internal(fd, addr, addr_len, NULL, NULL); +} + +int get_remote_hostname(const struct tsocket_address *remote_address, + char **name, + TALLOC_CTX *mem_ctx) +{ + char name_buf[MAX_DNS_NAME_LENGTH]; + char tmp_name[MAX_DNS_NAME_LENGTH]; + struct name_addr_pair nc; + struct sockaddr_storage ss; + ssize_t len; + int rc; + + if (!lp_hostname_lookups()) { + nc.name = tsocket_address_inet_addr_string(remote_address, + mem_ctx); + if (nc.name == NULL) { + return -1; + } + + len = tsocket_address_bsd_sockaddr(remote_address, + (struct sockaddr *) &nc.ss, + sizeof(struct sockaddr_storage)); + if (len < 0) { + return -1; + } + + store_nc(&nc); + lookup_nc(&nc); + + if (nc.name == NULL) { + *name = talloc_strdup(mem_ctx, "UNKNOWN"); + } else { + *name = talloc_strdup(mem_ctx, nc.name); + } + return 0; + } + + lookup_nc(&nc); + + ZERO_STRUCT(ss); + + len = tsocket_address_bsd_sockaddr(remote_address, + (struct sockaddr *) &ss, + sizeof(struct sockaddr_storage)); + if (len < 0) { + return -1; + } + + /* it might be the same as the last one - save some DNS work */ + if (sockaddr_equal((struct sockaddr *)&ss, (struct sockaddr *)&nc.ss)) { + if (nc.name == NULL) { + *name = talloc_strdup(mem_ctx, "UNKNOWN"); + } else { + *name = talloc_strdup(mem_ctx, nc.name); + } + return 0; + } + + /* Look up the remote host name. */ + rc = sys_getnameinfo((struct sockaddr *) &ss, + len, + name_buf, + sizeof(name_buf), + NULL, + 0, + 0); + if (rc < 0) { + char *p; + + p = tsocket_address_inet_addr_string(remote_address, mem_ctx); + if (p == NULL) { + return -1; + } + + DEBUG(1,("getnameinfo failed for %s with error %s\n", + p, + gai_strerror(rc))); + strlcpy(name_buf, p, sizeof(name_buf)); + + TALLOC_FREE(p); + } else { + if (!matchname(name_buf, (struct sockaddr *)&ss, len)) { + DEBUG(0,("matchname failed on %s\n", name_buf)); + strlcpy(name_buf, "UNKNOWN", sizeof(name_buf)); + } + } + + strlcpy(tmp_name, name_buf, sizeof(tmp_name)); + alpha_strcpy(name_buf, tmp_name, "_-.", sizeof(name_buf)); + if (strstr(name_buf,"..")) { + strlcpy(name_buf, "UNKNOWN", sizeof(name_buf)); + } + + nc.name = name_buf; + nc.ss = ss; + + store_nc(&nc); + lookup_nc(&nc); + + if (nc.name == NULL) { + *name = talloc_strdup(mem_ctx, "UNKNOWN"); + } else { + *name = talloc_strdup(mem_ctx, nc.name); + } + + return 0; +} + +/******************************************************************* + Create protected unix domain socket. + + Some unixes cannot set permissions on a ux-dom-sock, so we + have to make sure that the directory contains the protection + permissions instead. + ******************************************************************/ + +int create_pipe_sock(const char *socket_dir, + const char *socket_name, + mode_t dir_perms) +{ +#ifdef HAVE_UNIXSOCKET + struct sockaddr_un sunaddr; + bool ok; + int sock = -1; + mode_t old_umask; + char *path = NULL; + size_t path_len; + + old_umask = umask(0); + + ok = directory_create_or_exist_strict(socket_dir, + sec_initial_uid(), + dir_perms); + if (!ok) { + goto out_close; + } + + /* Create the socket file */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sock == -1) { + DEBUG(0, ("create_pipe_sock: socket error %s\n", + strerror(errno) )); + goto out_close; + } + + if (asprintf(&path, "%s/%s", socket_dir, socket_name) == -1) { + goto out_close; + } + + unlink(path); + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + + path_len = strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)); + if (path_len > sizeof(sunaddr.sun_path)) { + DBG_ERR("Refusing to attempt to create pipe socket " + "%s. Path is longer than permitted for a " + "unix domain socket. It would truncate to " + "%s\n", + path, + sunaddr.sun_path); + goto out_close; + } + + if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { + DEBUG(0, ("bind failed on pipe socket %s: %s\n", path, + strerror(errno))); + goto out_close; + } + + SAFE_FREE(path); + + umask(old_umask); + return sock; + +out_close: + SAFE_FREE(path); + if (sock != -1) + close(sock); + + umask(old_umask); + return -1; + +#else + DEBUG(0, ("create_pipe_sock: No Unix sockets on this system\n")); + return -1; +#endif /* HAVE_UNIXSOCKET */ +} + +/**************************************************************************** + Get my own canonical name, including domain. +****************************************************************************/ + +const char *get_mydnsfullname(void) +{ + struct addrinfo *res = NULL; + char my_hostname[HOST_NAME_MAX]; + bool ret; + DATA_BLOB tmp; + + if (memcache_lookup(NULL, SINGLETON_CACHE, + data_blob_string_const_null("get_mydnsfullname"), + &tmp)) { + SMB_ASSERT(tmp.length > 0); + return (const char *)tmp.data; + } + + /* get my host name */ + if (gethostname(my_hostname, sizeof(my_hostname)) == -1) { + DEBUG(0,("get_mydnsfullname: gethostname failed\n")); + return NULL; + } + + /* Ensure null termination. */ + my_hostname[sizeof(my_hostname)-1] = '\0'; + + ret = interpret_string_addr_internal(&res, + my_hostname, + AI_ADDRCONFIG|AI_CANONNAME); + + if (!ret || res == NULL) { + DEBUG(3,("get_mydnsfullname: getaddrinfo failed for " + "name %s [%s]\n", + my_hostname, + gai_strerror(ret) )); + return NULL; + } + + /* + * Make sure that getaddrinfo() returns the "correct" host name. + */ + + if (res->ai_canonname == NULL) { + DEBUG(3,("get_mydnsfullname: failed to get " + "canonical name for %s\n", + my_hostname)); + freeaddrinfo(res); + return NULL; + } + + /* This copies the data, so we must do a lookup + * afterwards to find the value to return. + */ + + memcache_add(NULL, SINGLETON_CACHE, + data_blob_string_const_null("get_mydnsfullname"), + data_blob_string_const_null(res->ai_canonname)); + + if (!memcache_lookup(NULL, SINGLETON_CACHE, + data_blob_string_const_null("get_mydnsfullname"), + &tmp)) { + tmp = data_blob_talloc(talloc_tos(), res->ai_canonname, + strlen(res->ai_canonname) + 1); + } + + freeaddrinfo(res); + + return (const char *)tmp.data; +} + +/************************************************************ + Is this my ip address ? +************************************************************/ + +static bool is_my_ipaddr(const char *ipaddr_str) +{ + struct sockaddr_storage ss; + struct iface_struct *nics; + int i, n; + + if (!interpret_string_addr(&ss, ipaddr_str, AI_NUMERICHOST)) { + return false; + } + + if (is_zero_addr(&ss)) { + return false; + } + + if (ismyaddr((struct sockaddr *)&ss) || + is_loopback_addr((struct sockaddr *)&ss)) { + return true; + } + + n = get_interfaces(talloc_tos(), &nics); + for (i=0; i<n; i++) { + if (sockaddr_equal((struct sockaddr *)&nics[i].ip, (struct sockaddr *)&ss)) { + TALLOC_FREE(nics); + return true; + } + } + TALLOC_FREE(nics); + return false; +} + +/************************************************************ + Is this my name ? +************************************************************/ + +bool is_myname_or_ipaddr(const char *s) +{ + TALLOC_CTX *ctx = talloc_tos(); + char *name = NULL; + const char *dnsname; + char *servername = NULL; + + if (!s) { + return false; + } + + /* Sanitize the string from '\\name' */ + name = talloc_strdup(ctx, s); + if (!name) { + return false; + } + + servername = strrchr_m(name, '\\' ); + if (!servername) { + servername = name; + } else { + servername++; + } + + /* Optimize for the common case */ + if (strequal(servername, lp_netbios_name())) { + return true; + } + + /* Check for an alias */ + if (is_myname(servername)) { + return true; + } + + /* Check for loopback */ + if (strequal(servername, "127.0.0.1") || + strequal(servername, "::1")) { + return true; + } + + if (strequal(servername, "localhost")) { + return true; + } + + /* Maybe it's my dns name */ + dnsname = get_mydnsfullname(); + if (dnsname && strequal(servername, dnsname)) { + return true; + } + + /* Maybe its an IP address? */ + if (is_ipaddress(servername)) { + return is_my_ipaddr(servername); + } + + /* Handle possible CNAME records - convert to an IP addr. list. */ + { + /* Use DNS to resolve the name, check all addresses. */ + struct addrinfo *p = NULL; + struct addrinfo *res = NULL; + + if (!interpret_string_addr_internal(&res, + servername, + AI_ADDRCONFIG)) { + return false; + } + + for (p = res; p; p = p->ai_next) { + char addr[INET6_ADDRSTRLEN]; + struct sockaddr_storage ss; + + ZERO_STRUCT(ss); + memcpy(&ss, p->ai_addr, p->ai_addrlen); + print_sockaddr(addr, + sizeof(addr), + &ss); + if (is_my_ipaddr(addr)) { + freeaddrinfo(res); + return true; + } + } + freeaddrinfo(res); + } + + /* No match */ + return false; +} + +int poll_one_fd(int fd, int events, int timeout, int *revents) +{ + struct pollfd pfd; + int ret; + + pfd.fd = fd; + pfd.events = events; + + ret = poll(&pfd, 1, timeout); + + /* + * Assign whatever poll did, even in the ret<=0 case. + */ + *revents = pfd.revents; + + return ret; +} + +int poll_intr_one_fd(int fd, int events, int timeout, int *revents) +{ + struct pollfd pfd; + int ret; + + pfd.fd = fd; + pfd.events = events; + + ret = sys_poll_intr(&pfd, 1, timeout); + if (ret <= 0) { + *revents = 0; + return ret; + } + *revents = pfd.revents; + return 1; +} diff --git a/source3/lib/util_specialsids.c b/source3/lib/util_specialsids.c new file mode 100644 index 0000000..10c189c --- /dev/null +++ b/source3/lib/util_specialsids.c @@ -0,0 +1,42 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Guenther Deschner 2016 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "libcli/security/dom_sid.h" +#include "../libcli/security/security.h" +#include "util_specialsids.h" + +bool sid_check_is_asserted_identity(const struct dom_sid *sid) +{ + return dom_sid_equal(sid, &global_sid_Asserted_Identity); +} + +bool sid_check_is_in_asserted_identity(const struct dom_sid *sid) +{ + struct dom_sid dom_sid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, NULL); + + return sid_check_is_asserted_identity(&dom_sid); +} + +const char *asserted_identity_domain_name(void) +{ + return "Asserted Identity"; +} diff --git a/source3/lib/util_specialsids.h b/source3/lib/util_specialsids.h new file mode 100644 index 0000000..c650737 --- /dev/null +++ b/source3/lib/util_specialsids.h @@ -0,0 +1,29 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Guenther Deschner 2016 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_UTIL_SPECIALSIDS_H__ +#define __LIB_UTIL_SPECIALSIDS_H__ + +#include "replace.h" + +struct dom_sid; +bool sid_check_is_asserted_identity(const struct dom_sid *sid); +bool sid_check_is_in_asserted_identity(const struct dom_sid *sid); +const char *asserted_identity_domain_name(void); + +#endif diff --git a/source3/lib/util_str.c b/source3/lib/util_str.c new file mode 100644 index 0000000..3ac1e55 --- /dev/null +++ b/source3/lib/util_str.c @@ -0,0 +1,762 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Martin Pool 2003 + Copyright (C) James Peach 2006 + Copyright (C) Jeremy Allison 1992-2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/param/loadparm.h" +#include "lib/util/smb_strtox.h" + +static const char toupper_ascii_fast_table[128] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f +}; + +/** + * Compare 2 strings up to and including the nth char. + * + * @note The comparison is case-insensitive. + **/ +bool strnequal(const char *s1,const char *s2,size_t n) +{ + if (s1 == s2) + return(true); + if (!s1 || !s2 || !n) + return(false); + + return(strncasecmp_m(s1,s2,n)==0); +} + +/** + Skip past a string in a buffer. Buffer may not be + null terminated. end_ptr points to the first byte after + then end of the buffer. +**/ + +char *skip_string(const char *base, size_t len, char *buf) +{ + const char *end_ptr = base + len; + + if (end_ptr < base || !base || !buf || buf >= end_ptr) { + return NULL; + } + + /* Skip the string */ + while (*buf) { + buf++; + if (buf >= end_ptr) { + return NULL; + } + } + /* Skip the '\0' */ + buf++; + return buf; +} + +/** + Count the number of characters in a string. Normally this will + be the same as the number of bytes in a string for single byte strings, + but will be different for multibyte. +**/ + +size_t str_charnum(const char *s) +{ + size_t ret, converted_size; + smb_ucs2_t *tmpbuf2 = NULL; + if (!push_ucs2_talloc(talloc_tos(), &tmpbuf2, s, &converted_size)) { + return 0; + } + ret = strlen_w(tmpbuf2); + TALLOC_FREE(tmpbuf2); + return ret; +} + +bool trim_char(char *s,char cfront,char cback) +{ + bool ret = false; + char *ep; + char *fp = s; + + /* Ignore null or empty strings. */ + if (!s || (s[0] == '\0')) + return false; + + if (cfront) { + while (*fp && *fp == cfront) + fp++; + if (!*fp) { + /* We ate the string. */ + s[0] = '\0'; + return true; + } + if (fp != s) + ret = true; + } + + ep = fp + strlen(fp) - 1; + if (cback) { + /* Attempt ascii only. Bail for mb strings. */ + while ((ep >= fp) && (*ep == cback)) { + ret = true; + if ((ep > fp) && (((unsigned char)ep[-1]) & 0x80)) { + /* Could be mb... bail back to tim_string. */ + char fs[2], bs[2]; + if (cfront) { + fs[0] = cfront; + fs[1] = '\0'; + } + bs[0] = cback; + bs[1] = '\0'; + return trim_string(s, cfront ? fs : NULL, bs); + } else { + ep--; + } + } + if (ep < fp) { + /* We ate the string. */ + s[0] = '\0'; + return true; + } + } + + ep[1] = '\0'; + memmove(s, fp, ep-fp+2); + return ret; +} + +/** + Check if a string is part of a list. +**/ + +bool in_list(const char *s, const char *list, bool casesensitive) +{ + char *tok = NULL; + bool ret = false; + TALLOC_CTX *frame; + + if (!list) { + return false; + } + + frame = talloc_stackframe(); + while (next_token_talloc(frame, &list, &tok,LIST_SEP)) { + if (casesensitive) { + if (strcmp(tok,s) == 0) { + ret = true; + break; + } + } else { + if (strcasecmp_m(tok,s) == 0) { + ret = true; + break; + } + } + } + TALLOC_FREE(frame); + return ret; +} + +/** + Truncate a string at a specified length. +**/ + +char *string_truncate(char *s, unsigned int length) +{ + if (s && strlen(s) > length) + s[length] = 0; + return s; +} + + +/*********************************************************************** + Return the equivalent of doing strrchr 'n' times - always going + backwards. +***********************************************************************/ + +char *strnrchr_m(const char *s, char c, unsigned int n) +{ + smb_ucs2_t *ws = NULL; + char *s2 = NULL; + smb_ucs2_t *p; + char *ret; + size_t converted_size; + + if (!push_ucs2_talloc(talloc_tos(), &ws, s, &converted_size)) { + /* Too hard to try and get right. */ + return NULL; + } + p = strnrchr_w(ws, UCS2_CHAR(c), n); + if (!p) { + TALLOC_FREE(ws); + return NULL; + } + *p = 0; + if (!pull_ucs2_talloc(talloc_tos(), &s2, ws, &converted_size)) { + TALLOC_FREE(ws); + /* Too hard to try and get right. */ + return NULL; + } + ret = discard_const_p(char, (s+strlen(s2))); + TALLOC_FREE(ws); + TALLOC_FREE(s2); + return ret; +} + +static bool unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen) +{ + size_t size; + smb_ucs2_t *buffer = NULL; + bool ret; + + if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_UTF16LE, src, srclen, + (void **)(void *)&buffer, &size)) + { + return false; + } + if (!strlower_w(buffer) && (dest == src)) { + TALLOC_FREE(buffer); + return true; + } + ret = convert_string(CH_UTF16LE, CH_UNIX, buffer, size, dest, destlen, &size); + TALLOC_FREE(buffer); + return ret; +} + +#if 0 /* Alternate function that avoid talloc calls for ASCII and non ASCII */ + +/** + Convert a string to lower case. +**/ +_PUBLIC_ void strlower_m(char *s) +{ + char *d; + struct smb_iconv_handle *iconv_handle; + + iconv_handle = get_iconv_handle(); + + d = s; + + while (*s) { + size_t c_size, c_size2; + codepoint_t c = next_codepoint_handle(iconv_handle, s, &c_size); + c_size2 = push_codepoint_handle(iconv_handle, d, tolower_m(c)); + if (c_size2 > c_size) { + DEBUG(0,("FATAL: codepoint 0x%x (0x%x) expanded from %d to %d bytes in strlower_m\n", + c, tolower_m(c), (int)c_size, (int)c_size2)); + smb_panic("codepoint expansion in strlower_m\n"); + } + s += c_size; + d += c_size2; + } + *d = 0; +} + +#endif + +/** + Convert a string to lower case. +**/ + +bool strlower_m(char *s) +{ + size_t len; + int errno_save; + bool ret = false; + + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + while (*s && !(((unsigned char)s[0]) & 0x80)) { + *s = tolower_m((unsigned char)*s); + s++; + } + + if (!*s) + return true; + + /* I assume that lowercased string takes the same number of bytes + * as source string even in UTF-8 encoding. (VIV) */ + len = strlen(s) + 1; + errno_save = errno; + errno = 0; + ret = unix_strlower(s,len,s,len); + /* Catch mb conversion errors that may not terminate. */ + if (errno) { + s[len-1] = '\0'; + } + errno = errno_save; + return ret; +} + +static bool unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen) +{ + size_t size; + smb_ucs2_t *buffer; + bool ret; + + if (!push_ucs2_talloc(talloc_tos(), &buffer, src, &size)) { + return false; + } + + if (!strupper_w(buffer) && (dest == src)) { + TALLOC_FREE(buffer); + return true; + } + + ret = convert_string(CH_UTF16LE, CH_UNIX, buffer, size, dest, destlen, &size); + TALLOC_FREE(buffer); + return ret; +} + +#if 0 /* Alternate function that avoid talloc calls for ASCII and non ASCII */ + +/** + Convert a string to UPPER case. +**/ +_PUBLIC_ void strupper_m(char *s) +{ + char *d; + struct smb_iconv_handle *iconv_handle; + + iconv_handle = get_iconv_handle(); + + d = s; + + while (*s) { + size_t c_size, c_size2; + codepoint_t c = next_codepoint_handle(iconv_handle, s, &c_size); + c_size2 = push_codepoint_handle(iconv_handle, d, toupper_m(c)); + if (c_size2 > c_size) { + DEBUG(0,("FATAL: codepoint 0x%x (0x%x) expanded from %d to %d bytes in strupper_m\n", + c, toupper_m(c), (int)c_size, (int)c_size2)); + smb_panic("codepoint expansion in strupper_m\n"); + } + s += c_size; + d += c_size2; + } + *d = 0; +} + +#endif + +/** + Convert a string to upper case. +**/ + +bool strupper_m(char *s) +{ + size_t len; + bool ret = false; + + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + while (*s && !(((unsigned char)s[0]) & 0x80)) { + *s = toupper_ascii_fast_table[(unsigned char)s[0]]; + s++; + } + + if (!*s) + return true; + + /* I assume that uppercased string takes the same number of bytes + * as source string even in multibyte encoding. (VIV) */ + len = strlen(s) + 1; + ret = unix_strupper(s,len,s,len); + /* Catch mb conversion errors that may not terminate. */ + if (!ret) { + s[len-1] = '\0'; + } + return ret; +} + +/** + Just a typesafety wrapper for snprintf into a fstring. +**/ + +int fstr_sprintf(fstring s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(s, FSTRING_LEN, fmt, ap); + va_end(ap); + return ret; +} + +/* read a SMB_BIG_UINT from a string */ +uint64_t STR_TO_SMB_BIG_UINT(const char *nptr, const char **entptr) +{ + + uint64_t val = (uint64_t)-1; + const char *p = nptr; + + if (!p) { + if (entptr) { + *entptr = p; + } + return val; + } + + while (*p && isspace(*p)) + p++; + + sscanf(p,"%"SCNu64,&val); + if (entptr) { + while (*p && isdigit(*p)) + p++; + *entptr = p; + } + + return val; +} + +/* Convert a size specification to a count of bytes. We accept the following + * suffixes: + * bytes if there is no suffix + * kK kibibytes + * mM mebibytes + * gG gibibytes + * tT tibibytes + * pP whatever the ISO name for petabytes is + * + * Returns 0 if the string can't be converted. + */ +uint64_t conv_str_size(const char * str) +{ + uint64_t lval; + char *end; + int error = 0; + + if (str == NULL || *str == '\0') { + return 0; + } + + lval = smb_strtoull(str, &end, 10, &error, SMB_STR_STANDARD); + + if (error != 0) { + return 0; + } + + if (*end == '\0') { + return lval; + } + + if (strwicmp(end, "K") == 0) { + lval *= 1024ULL; + } else if (strwicmp(end, "M") == 0) { + lval *= (1024ULL * 1024ULL); + } else if (strwicmp(end, "G") == 0) { + lval *= (1024ULL * 1024ULL * + 1024ULL); + } else if (strwicmp(end, "T") == 0) { + lval *= (1024ULL * 1024ULL * + 1024ULL * 1024ULL); + } else if (strwicmp(end, "P") == 0) { + lval *= (1024ULL * 1024ULL * + 1024ULL * 1024ULL * + 1024ULL); + } else { + return 0; + } + + return lval; +} + +char *talloc_asprintf_strupper_m(TALLOC_CTX *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + + if (ret == NULL) { + return NULL; + } + if (!strupper_m(ret)) { + TALLOC_FREE(ret); + return NULL; + } + return ret; +} + +char *talloc_asprintf_strlower_m(TALLOC_CTX *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + + if (ret == NULL) { + return NULL; + } + if (!strlower_m(ret)) { + TALLOC_FREE(ret); + return NULL; + } + return ret; +} + + +/******************************************************************** + Check a string for any occurrences of a specified list of invalid + characters. +********************************************************************/ + +bool validate_net_name( const char *name, + const char *invalid_chars, + int max_len) +{ + int i; + + if (!name) { + return false; + } + + for ( i=0; i<max_len && name[i]; i++ ) { + /* fail if strchr_m() finds one of the invalid characters */ + if ( name[i] && strchr_m( invalid_chars, name[i] ) ) { + return false; + } + } + + return true; +} + + +/******************************************************************* + Add a shell escape character '\' to any character not in a known list + of characters. UNIX charset format. +*******************************************************************/ + +#define INCLUDE_LIST "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_/ \t.," +#define INSIDE_DQUOTE_LIST "$`\n\"\\" + +char *escape_shell_string(const char *src) +{ + size_t srclen = strlen(src); + char *ret = SMB_MALLOC_ARRAY(char, (srclen * 2) + 1); + char *dest = ret; + bool in_s_quote = false; + bool in_d_quote = false; + bool next_escaped = false; + + if (!ret) { + return NULL; + } + + while (*src) { + size_t c_size; + codepoint_t c = next_codepoint(src, &c_size); + + if (c == INVALID_CODEPOINT) { + SAFE_FREE(ret); + return NULL; + } + + if (c_size > 1) { + memcpy(dest, src, c_size); + src += c_size; + dest += c_size; + next_escaped = false; + continue; + } + + /* + * Deal with backslash escaped state. + * This only lasts for one character. + */ + + if (next_escaped) { + *dest++ = *src++; + next_escaped = false; + continue; + } + + /* + * Deal with single quote state. The + * only thing we care about is exiting + * this state. + */ + + if (in_s_quote) { + if (*src == '\'') { + in_s_quote = false; + } + *dest++ = *src++; + continue; + } + + /* + * Deal with double quote state. The most + * complex state. We must cope with \, meaning + * possibly escape next char (depending what it + * is), ", meaning exit this state, and possibly + * add an \ escape to any unprotected character + * (listed in INSIDE_DQUOTE_LIST). + */ + + if (in_d_quote) { + if (*src == '\\') { + /* + * Next character might be escaped. + * We have to peek. Inside double + * quotes only INSIDE_DQUOTE_LIST + * characters are escaped by a \. + */ + + char nextchar; + + c = next_codepoint(&src[1], &c_size); + if (c == INVALID_CODEPOINT) { + SAFE_FREE(ret); + return NULL; + } + if (c_size > 1) { + /* + * Don't escape the next char. + * Just copy the \. + */ + *dest++ = *src++; + continue; + } + + nextchar = src[1]; + + if (nextchar && strchr(INSIDE_DQUOTE_LIST, + (int)nextchar)) { + next_escaped = true; + } + *dest++ = *src++; + continue; + } + + if (*src == '\"') { + /* Exit double quote state. */ + in_d_quote = false; + *dest++ = *src++; + continue; + } + + /* + * We know the character isn't \ or ", + * so escape it if it's any of the other + * possible unprotected characters. + */ + + if (strchr(INSIDE_DQUOTE_LIST, (int)*src)) { + *dest++ = '\\'; + } + *dest++ = *src++; + continue; + } + + /* + * From here to the end of the loop we're + * not in the single or double quote state. + */ + + if (*src == '\\') { + /* Next character must be escaped. */ + next_escaped = true; + *dest++ = *src++; + continue; + } + + if (*src == '\'') { + /* Go into single quote state. */ + in_s_quote = true; + *dest++ = *src++; + continue; + } + + if (*src == '\"') { + /* Go into double quote state. */ + in_d_quote = true; + *dest++ = *src++; + continue; + } + + /* Check if we need to escape the character. */ + + if (!strchr(INCLUDE_LIST, (int)*src)) { + *dest++ = '\\'; + } + *dest++ = *src++; + } + *dest++ = '\0'; + return ret; +} + +/* + * This routine improves performance for operations temporarily acting on a + * full path. It is equivalent to the much more expensive + * + * talloc_asprintf(talloc_tos(), "%s/%s", dir, name) + * + * This actually does make a difference in metadata-heavy workloads (i.e. the + * "standard" client.txt nbench run. + */ + +ssize_t full_path_tos(const char *dir, const char *name, + char *tmpbuf, size_t tmpbuf_len, + char **pdst, char **to_free) +{ + size_t dirlen, namelen, len; + char *dst; + + dirlen = strlen(dir); + namelen = strlen(name); + len = dirlen + namelen + 1; + + if (len < tmpbuf_len) { + dst = tmpbuf; + *to_free = NULL; + } else { + dst = talloc_array(talloc_tos(), char, len+1); + if (dst == NULL) { + return -1; + } + *to_free = dst; + } + + memcpy(dst, dir, dirlen); + dst[dirlen] = '/'; + memcpy(dst+dirlen+1, name, namelen+1); + *pdst = dst; + return len; +} diff --git a/source3/lib/util_tdb.c b/source3/lib/util_tdb.c new file mode 100644 index 0000000..d85f676 --- /dev/null +++ b/source3/lib/util_tdb.c @@ -0,0 +1,449 @@ +/* + Unix SMB/CIFS implementation. + tdb utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Rafal Szczesniak 2002 + Copyright (C) Michael Adam 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "util_tdb.h" +#include "cbuf.h" + +#undef malloc +#undef realloc +#undef calloc +#undef strdup + +/**************************************************************************** + Useful pair of routines for packing/unpacking data consisting of + integers and strings. +****************************************************************************/ + +static size_t tdb_pack_va(uint8_t *buf, int bufsize, const char *fmt, va_list ap) +{ + uint8_t bt; + uint16_t w; + uint32_t d; + int i; + void *p; + int len = 0; + char *s; + char c; + const char *fmt0 = fmt; + int bufsize0 = bufsize; + size_t to_write = 0; + while (*fmt) { + switch ((c = *fmt++)) { + case 'b': /* unsigned 8-bit integer */ + len = 1; + bt = (uint8_t)va_arg(ap, int); + if (bufsize && bufsize >= len) + SSVAL(buf, 0, bt); + break; + case 'w': /* unsigned 16-bit integer */ + len = 2; + w = (uint16_t)va_arg(ap, int); + if (bufsize && bufsize >= len) + SSVAL(buf, 0, w); + break; + case 'd': /* signed 32-bit integer (standard int in most systems) */ + len = 4; + d = va_arg(ap, uint32_t); + if (bufsize && bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'p': /* pointer */ + len = 4; + p = va_arg(ap, void *); + d = p?1:0; + if (bufsize && bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'P': /* null-terminated string */ + case 'f': /* null-terminated string */ + s = va_arg(ap,char *); + if (s == NULL) { + smb_panic("Invalid argument"); + } + w = strlen(s); + len = w + 1; + if (bufsize && bufsize >= len) + memcpy(buf, s, len); + break; + case 'B': /* fixed-length string */ + i = va_arg(ap, int); + s = va_arg(ap, char *); + len = 4+i; + if (bufsize && bufsize >= len) { + SIVAL(buf, 0, i); + if (s != NULL) { + memcpy(buf+4, s, i); + } + } + break; + default: + DEBUG(0,("Unknown tdb_pack format %c in %s\n", + c, fmt)); + len = 0; + break; + } + + to_write += len; + if (bufsize > 0) { + bufsize -= len; + buf += len; + } + if (bufsize < 0) + bufsize = 0; + } + + DEBUG(18,("tdb_pack_va(%s, %d) -> %d\n", + fmt0, bufsize0, (int)to_write)); + + return to_write; +} + +size_t tdb_pack(uint8_t *buf, int bufsize, const char *fmt, ...) +{ + va_list ap; + size_t result; + + va_start(ap, fmt); + result = tdb_pack_va(buf, bufsize, fmt, ap); + va_end(ap); + return result; +} + +/**************************************************************************** + Useful pair of routines for packing/unpacking data consisting of + integers and strings. +****************************************************************************/ + +int tdb_unpack(const uint8_t *buf, int in_bufsize, const char *fmt, ...) +{ + va_list ap; + uint8_t *bt; + uint16_t *w; + uint32_t *d; + size_t bufsize = in_bufsize; + size_t len; + uint32_t *i; + void **p; + char *s, **b, **ps; + char c; + const uint8_t *buf0 = buf; + const char *fmt0 = fmt; + + va_start(ap, fmt); + + while (*fmt) { + switch ((c=*fmt++)) { + case 'b': /* unsigned 8-bit integer */ + len = 1; + bt = va_arg(ap, uint8_t *); + if (bufsize < len) + goto no_space; + *bt = SVAL(buf, 0); + break; + case 'w': /* unsigned 16-bit integer */ + len = 2; + w = va_arg(ap, uint16_t *); + if (bufsize < len) + goto no_space; + *w = SVAL(buf, 0); + break; + case 'd': /* unsigned 32-bit integer (standard int in most systems) */ + len = 4; + d = va_arg(ap, uint32_t *); + if (bufsize < len) + goto no_space; + *d = IVAL(buf, 0); + break; + case 'p': /* pointer */ + len = 4; + p = va_arg(ap, void **); + if (bufsize < len) + goto no_space; + /* + * This isn't a real pointer - only a token (1 or 0) + * to mark the fact a pointer is present. + */ + + *p = (void *)(IVAL(buf, 0) ? (void *)1 : NULL); + break; + case 'P': /* null-terminated string */ + /* Return malloc'ed string. */ + ps = va_arg(ap,char **); + len = strnlen((const char *)buf, bufsize) + 1; + if (bufsize < len) + goto no_space; + if (ps != NULL) { + *ps = SMB_STRDUP((const char *)buf); + if (*ps == NULL) { + goto no_space; + } + } + break; + case 'f': /* null-terminated string */ + s = va_arg(ap,char *); + len = strnlen((const char *)buf, bufsize) + 1; + if (bufsize < len || len > sizeof(fstring)) + goto no_space; + if (s != NULL) { + memcpy(s, buf, len); + } + break; + case 'B': /* fixed-length string */ + i = va_arg(ap, uint32_t *); + b = va_arg(ap, char **); + len = 4; + if (bufsize < len) + goto no_space; + *i = IVAL(buf, 0); + if (! *i) { + *b = NULL; + break; + } + len += *i; + if (len < *i) { + goto no_space; + } + if (bufsize < len) + goto no_space; + if (b != NULL) { + *b = (char *)SMB_MALLOC(*i); + if (! *b) + goto no_space; + memcpy(*b, buf+4, *i); + } + break; + default: + DEBUG(0,("Unknown tdb_unpack format %c in %s\n", + c, fmt)); + + len = 0; + break; + } + + buf += len; + bufsize -= len; + } + + va_end(ap); + + DEBUG(18,("tdb_unpack(%s, %d) -> %d\n", + fmt0, in_bufsize, (int)PTR_DIFF(buf, buf0))); + + return PTR_DIFF(buf, buf0); + + no_space: + va_end(ap); + return -1; +} + + +/**************************************************************************** + Log tdb messages via DEBUG(). +****************************************************************************/ + +static void tdb_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, + const char *format, ...) PRINTF_ATTRIBUTE(3,4); + +static void tdb_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, const char *format, ...) +{ + va_list ap; + char *ptr = NULL; + int ret; + + va_start(ap, format); + ret = vasprintf(&ptr, format, ap); + va_end(ap); + + if ((ret == -1) || !*ptr) + return; + + DEBUG((int)level, ("tdb(%s): %s", tdb_name(tdb) ? tdb_name(tdb) : "unnamed", ptr)); + SAFE_FREE(ptr); +} + +/**************************************************************************** + Like tdb_open() but also setup a logging function that redirects to + the samba DEBUG() system. +****************************************************************************/ + +TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + TDB_CONTEXT *tdb; + struct tdb_logging_context log_ctx = { .log_fn = tdb_log }; + + if (!lp_use_mmap()) + tdb_flags |= TDB_NOMMAP; + + if ((hash_size == 0) && (name != NULL)) { + const char *base = strrchr_m(name, '/'); + if (base != NULL) { + base += 1; + } + else { + base = name; + } + hash_size = lp_parm_int(-1, "tdb_hashsize", base, 0); + } + + tdb = tdb_open_ex(name, hash_size, tdb_flags, + open_flags, mode, &log_ctx, NULL); + if (!tdb) + return NULL; + + return tdb; +} + +int tdb_data_cmp(TDB_DATA t1, TDB_DATA t2) +{ + int ret; + if (t1.dptr == NULL && t2.dptr != NULL) { + return -1; + } + if (t1.dptr != NULL && t2.dptr == NULL) { + return 1; + } + if (t1.dptr == t2.dptr) { + return t1.dsize - t2.dsize; + } + ret = memcmp(t1.dptr, t2.dptr, MIN(t1.dsize, t2.dsize)); + if (ret == 0) { + return t1.dsize - t2.dsize; + } + return ret; +} + +char *tdb_data_string(TALLOC_CTX *mem_ctx, TDB_DATA d) +{ + int len; + char *ret = NULL; + cbuf *ost = cbuf_new(mem_ctx); + + if (ost == NULL) { + return NULL; + } + + len = cbuf_printf(ost, "%zu:", d.dsize); + if (len == -1) { + goto done; + } + + if (d.dptr == NULL) { + len = cbuf_puts(ost, "<NULL>", -1); + } else { + len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize); + } + if (len == -1) { + goto done; + } + + cbuf_swapptr(ost, &ret, 0); + talloc_steal(mem_ctx, ret); + +done: + talloc_free(ost); + return ret; +} + +char *tdb_data_dbg(TDB_DATA d) +{ + return hex_encode_talloc(talloc_tos(), d.dptr, d.dsize); +} + +static sig_atomic_t gotalarm; + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ + +static void gotalarm_sig(int signum) +{ + gotalarm = 1; +} + +/**************************************************************************** + Lock a chain with timeout (in seconds). +****************************************************************************/ + +static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type) +{ + /* Allow tdb_chainlock to be interrupted by an alarm. */ + int ret; + gotalarm = 0; + + if (timeout) { + CatchSignal(SIGALRM, gotalarm_sig); + tdb_setalarm_sigptr(tdb, &gotalarm); + alarm(timeout); + } + + if (rw_type == F_RDLCK) + ret = tdb_chainlock_read(tdb, key); + else + ret = tdb_chainlock(tdb, key); + + if (timeout) { + alarm(0); + tdb_setalarm_sigptr(tdb, NULL); + CatchSignal(SIGALRM, SIG_IGN); + if (gotalarm && (ret != 0)) { + DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n", + timeout, key.dptr, tdb_name(tdb))); + /* TODO: If we time out waiting for a lock, it might + * be nice to use F_GETLK to get the pid of the + * process currently holding the lock and print that + * as part of the debugging message. -- mbp */ + return -1; + } + } + + return ret == 0 ? 0 : -1; +} + +/**************************************************************************** + Write lock a chain. Return non-zero if timeout or lock failed. +****************************************************************************/ + +int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout) +{ + return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK); +} + +int tdb_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval, + int timeout) +{ + TDB_DATA key = string_term_tdb_data(keyval); + + return tdb_chainlock_with_timeout(tdb, key, timeout); +} + +/**************************************************************************** + Read lock a chain by string. Return non-zero if timeout or lock failed. +****************************************************************************/ + +int tdb_read_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout) +{ + TDB_DATA key = string_term_tdb_data(keyval); + + return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK); +} diff --git a/source3/lib/util_transfer_file.c b/source3/lib/util_transfer_file.c new file mode 100644 index 0000000..5653906 --- /dev/null +++ b/source3/lib/util_transfer_file.c @@ -0,0 +1,119 @@ +/* + * Unix SMB/CIFS implementation. + * Utility functions to transfer files. + * + * Copyright (C) Jeremy Allison 2001-2002 + * Copyright (C) Michael Adam 2008 + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#include <includes.h> +#include "transfer_file.h" +#include "lib/util/sys_rw.h" + +/**************************************************************************** + Transfer some data between two fd's. +****************************************************************************/ + +#ifndef TRANSFER_BUF_SIZE +#define TRANSFER_BUF_SIZE 65536 +#endif + + +ssize_t transfer_file_internal(void *in_file, + void *out_file, + size_t n, + ssize_t (*pread_fn)(void *, void *, size_t, off_t), + ssize_t (*pwrite_fn)(void *, const void *, size_t, off_t)) +{ + char *buf; + size_t total = 0; + ssize_t read_ret; + ssize_t write_ret; + size_t num_to_read_thistime; + size_t num_written = 0; + off_t offset = 0; + + if (n == 0) { + return 0; + } + + if ((buf = SMB_MALLOC_ARRAY(char, TRANSFER_BUF_SIZE)) == NULL) { + return -1; + } + + do { + num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE); + + read_ret = (*pread_fn)(in_file, buf, num_to_read_thistime, offset); + if (read_ret == -1) { + DEBUG(0,("transfer_file_internal: read failure. " + "Error = %s\n", strerror(errno) )); + SAFE_FREE(buf); + return -1; + } + if (read_ret == 0) { + break; + } + + num_written = 0; + + while (num_written < read_ret) { + write_ret = (*pwrite_fn)(out_file, buf + num_written, + read_ret - num_written, + offset + num_written); + + if (write_ret == -1) { + DEBUG(0,("transfer_file_internal: " + "write failure. Error = %s\n", + strerror(errno) )); + SAFE_FREE(buf); + return -1; + } + if (write_ret == 0) { + return (ssize_t)total; + } + + num_written += (size_t)write_ret; + } + + total += (size_t)read_ret; + offset += (off_t)read_ret; + } while (total < n); + + SAFE_FREE(buf); + return (ssize_t)total; +} + +static ssize_t sys_pread_fn(void *file, void *buf, size_t len, off_t offset) +{ + int *fd = (int *)file; + + return sys_pread(*fd, buf, len, offset); +} + +static ssize_t sys_pwrite_fn(void *file, const void *buf, size_t len, off_t offset) +{ + int *fd = (int *)file; + + return sys_pwrite(*fd, buf, len, offset); +} + +off_t transfer_file(int infd, int outfd, off_t n) +{ + return (off_t)transfer_file_internal(&infd, &outfd, (size_t)n, + sys_pread_fn, sys_pwrite_fn); +} diff --git a/source3/lib/util_tsock.c b/source3/lib/util_tsock.c new file mode 100644 index 0000000..6432ce4 --- /dev/null +++ b/source3/lib/util_tsock.c @@ -0,0 +1,149 @@ +/* + Unix SMB/CIFS implementation. + Utilities around tsocket + Copyright (C) Volker Lendecke 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/network.h" +#include <tevent.h> +#include "lib/util_tsock.h" +#include "../lib/tsocket/tsocket.h" +#include "../lib/util/tevent_unix.h" + +struct tstream_read_packet_state { + struct tevent_context *ev; + struct tstream_context *stream; + ssize_t (*more)(uint8_t *buf, size_t buflen, void *private_data); + void *private_data; + uint8_t *buf; + struct iovec iov; +}; + +static void tstream_read_packet_done(struct tevent_req *subreq); + +struct tevent_req *tstream_read_packet_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + size_t initial, + ssize_t (*more)(uint8_t *buf, + size_t buflen, + void *private_data), + void *private_data) +{ + struct tevent_req *req, *subreq; + struct tstream_read_packet_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct tstream_read_packet_state); + if (req == NULL) { + return NULL; + } + state->buf = talloc_array(state, uint8_t, initial); + if (tevent_req_nomem(state->buf, req)) { + return tevent_req_post(req, ev); + } + state->iov.iov_base = (void *)state->buf; + state->iov.iov_len = initial; + + state->ev = ev; + state->stream = stream; + state->more = more; + state->private_data = private_data; + + subreq = tstream_readv_send(state, ev, stream, &state->iov, 1); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tstream_read_packet_done, req); + + return req; +} + +static void tstream_read_packet_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tstream_read_packet_state *state = tevent_req_data( + req, struct tstream_read_packet_state); + int ret, err; + size_t total; + ssize_t more; + uint8_t *tmp; + + ret = tstream_readv_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == 0) { + err = EPIPE; + } + if (ret <= 0) { + tevent_req_error(req, err); + return; + } + + if (state->more == NULL) { + /* Nobody to ask, this is a async read_data */ + tevent_req_done(req); + return; + } + total = talloc_array_length(state->buf); + + more = state->more(state->buf, total, state->private_data); + if (more == -1) { + /* We got an invalid packet, tell the caller */ + tevent_req_error(req, EIO); + return; + } + if (more == 0) { + /* We're done, full packet received */ + tevent_req_done(req); + return; + } + + if (total + more < total) { + tevent_req_error(req, EMSGSIZE); + return; + } + + tmp = talloc_realloc(state, state->buf, uint8_t, total+more); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->buf = tmp; + + state->iov.iov_base = (void *)(state->buf + total); + state->iov.iov_len = more; + + subreq = tstream_readv_send(state, state->ev, state->stream, + &state->iov, 1); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tstream_read_packet_done, req); +} + +ssize_t tstream_read_packet_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **pbuf, int *perrno) +{ + struct tstream_read_packet_state *state = + tevent_req_data(req, struct tstream_read_packet_state); + + if (tevent_req_is_unix_error(req, perrno)) { + return -1; + } + *pbuf = talloc_move(mem_ctx, &state->buf); + return talloc_array_length(*pbuf); +} diff --git a/source3/lib/util_tsock.h b/source3/lib/util_tsock.h new file mode 100644 index 0000000..de4381a --- /dev/null +++ b/source3/lib/util_tsock.h @@ -0,0 +1,38 @@ +/* + Unix SMB/CIFS implementation. + Utilities around tsocket + Copyright (C) Volker Lendecke 2009 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __UTIL_TSOCK_H__ +#define __UTIL_TSOCK_H__ + +#include "replace.h" +#include <tevent.h> + +struct tstream_context; +struct tevent_req *tstream_read_packet_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + size_t initial, + ssize_t (*more)(uint8_t *buf, + size_t buflen, + void *private_data), + void *private_data); +ssize_t tstream_read_packet_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **pbuf, int *perrno); + +#endif diff --git a/source3/lib/util_unixsids.c b/source3/lib/util_unixsids.c new file mode 100644 index 0000000..387232c --- /dev/null +++ b/source3/lib/util_unixsids.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + Translate unix-defined names to SIDs and vice versa + Copyright (C) Volker Lendecke 2005 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "util_unixsids.h" +#include "../libcli/security/security.h" + +bool sid_check_is_unix_users(const struct dom_sid *sid) +{ + return dom_sid_equal(sid, &global_sid_Unix_Users); +} + +bool sid_check_is_in_unix_users(const struct dom_sid *sid) +{ + struct dom_sid dom_sid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, NULL); + + return sid_check_is_unix_users(&dom_sid); +} + +void uid_to_unix_users_sid(uid_t uid, struct dom_sid *sid) +{ + /* + * This can never fail, we know that global_sid_Unix_Users is + * short enough for a domain sid. + */ + sid_compose(sid, &global_sid_Unix_Users, uid); +} + +void gid_to_unix_groups_sid(gid_t gid, struct dom_sid *sid) +{ + /* + * This can never fail, we know that global_sid_Unix_Groups is + * short enough for a domain sid. + */ + sid_compose(sid, &global_sid_Unix_Groups, gid); +} + +const char *unix_users_domain_name(void) +{ + return "Unix User"; +} + +bool sid_check_is_unix_groups(const struct dom_sid *sid) +{ + return dom_sid_equal(sid, &global_sid_Unix_Groups); +} + +bool sid_check_is_in_unix_groups(const struct dom_sid *sid) +{ + struct dom_sid dom_sid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, NULL); + + return sid_check_is_unix_groups(&dom_sid); +} + +const char *unix_groups_domain_name(void) +{ + return "Unix Group"; +} diff --git a/source3/lib/util_unixsids.h b/source3/lib/util_unixsids.h new file mode 100644 index 0000000..b90a746 --- /dev/null +++ b/source3/lib/util_unixsids.h @@ -0,0 +1,36 @@ +/* + Unix SMB/CIFS implementation. + Translate unix-defined names to SIDs and vice versa + Copyright (C) Volker Lendecke 2005 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __UTIL_UNIXSIDS_H__ +#define __UTIL_UNIXSIDS_H__ + +#include "replace.h" + +struct dom_sid; + +bool sid_check_is_unix_users(const struct dom_sid *sid); +bool sid_check_is_in_unix_users(const struct dom_sid *sid); +void uid_to_unix_users_sid(uid_t uid, struct dom_sid *sid); +void gid_to_unix_groups_sid(gid_t gid, struct dom_sid *sid); +const char *unix_users_domain_name(void); +bool sid_check_is_unix_groups(const struct dom_sid *sid); +bool sid_check_is_in_unix_groups(const struct dom_sid *sid); +const char *unix_groups_domain_name(void); + +#endif diff --git a/source3/lib/util_wellknown.c b/source3/lib/util_wellknown.c new file mode 100644 index 0000000..8cb7a36 --- /dev/null +++ b/source3/lib/util_wellknown.c @@ -0,0 +1,192 @@ +/* + Unix SMB/CIFS implementation. + Lookup routines for well-known SIDs + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Luke Kenneth Caseson Leighton 1998-1999 + Copyright (C) Jeremy Allison 1999 + Copyright (C) Volker Lendecke 2005 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/security/security.h" + +struct rid_name_map { + uint32_t rid; + const char *name; +}; + +struct sid_name_map_info +{ + const struct dom_sid *sid; + const char *name; + const struct rid_name_map *known_users; +}; + +static const struct rid_name_map everyone_users[] = { + { 0, "Everyone" }, + { 0, NULL}}; + +static const struct rid_name_map local_authority_users[] = { + { 0, "Local" }, + { 1, "Console Logon" }, + { 0, NULL}}; + +static const struct rid_name_map creator_owner_users[] = { + { 0, "Creator Owner" }, + { 1, "Creator Group" }, + { 2, "Creator Owner Server" }, + { 3, "Creator Group Server" }, + { 4, "Owner Rights" }, + { 0, NULL}}; + +static const struct rid_name_map nt_authority_users[] = { + { 1, "Dialup" }, + { 2, "Network"}, + { 3, "Batch"}, + { 4, "Interactive"}, + { 6, "Service"}, + { 7, "Anonymous Logon"}, + { 8, "Proxy"}, + { 9, "Enterprise Domain Controllers"}, + { 10, "Self"}, + { 11, "Authenticated Users"}, + { 12, "Restricted"}, + { 13, "Terminal Server User"}, + { 14, "Remote Interactive Logon"}, + { 15, "This Organization"}, + { 17, "IUSR"}, + { 18, "SYSTEM"}, + { 19, "Local Service"}, + { 20, "Network Service"}, + { 0, NULL}}; + +static struct sid_name_map_info special_domains[] = { + { &global_sid_World_Domain, "", everyone_users }, + { &global_sid_Local_Authority, "", local_authority_users }, + { &global_sid_Creator_Owner_Domain, "", creator_owner_users }, + { &global_sid_NT_Authority, "NT Authority", nt_authority_users }, + { NULL, NULL, NULL }}; + +bool sid_check_is_wellknown_domain(const struct dom_sid *sid, const char **name) +{ + int i; + + for (i=0; special_domains[i].sid != NULL; i++) { + if (dom_sid_equal(sid, special_domains[i].sid)) { + if (name != NULL) { + *name = special_domains[i].name; + } + return True; + } + } + return False; +} + +bool sid_check_is_in_wellknown_domain(const struct dom_sid *sid) +{ + struct dom_sid dom_sid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, NULL); + + return sid_check_is_wellknown_domain(&dom_sid, NULL); +} + +/************************************************************************** + Looks up a known username from one of the known domains. +***************************************************************************/ + +bool lookup_wellknown_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, + const char **domain, const char **name) +{ + int i; + struct dom_sid dom_sid; + uint32_t rid; + const struct rid_name_map *users = NULL; + struct dom_sid_buf buf; + + sid_copy(&dom_sid, sid); + if (!sid_split_rid(&dom_sid, &rid)) { + DEBUG(2, ("Could not split rid from SID\n")); + return False; + } + + for (i=0; special_domains[i].sid != NULL; i++) { + if (dom_sid_equal(&dom_sid, special_domains[i].sid)) { + *domain = talloc_strdup(mem_ctx, + special_domains[i].name); + users = special_domains[i].known_users; + break; + } + } + + if (users == NULL) { + DEBUG(10, ("SID %s is no special sid\n", + dom_sid_str_buf(sid, &buf))); + return False; + } + + for (i=0; users[i].name != NULL; i++) { + if (rid == users[i].rid) { + *name = talloc_strdup(mem_ctx, users[i].name); + return True; + } + } + + DEBUG(10, ("RID of special SID %s not found\n", + dom_sid_str_buf(sid, &buf))); + + return False; +} + +/************************************************************************** + Try and map a name to one of the well known SIDs. +***************************************************************************/ + +bool lookup_wellknown_name(TALLOC_CTX *mem_ctx, const char *name, + struct dom_sid *sid, const char **pdomain) +{ + int i, j; + const char *domain = *pdomain; + + DEBUG(10,("map_name_to_wellknown_sid: looking up %s\\%s\n", domain, name)); + + for (i=0; special_domains[i].sid != NULL; i++) { + const struct rid_name_map *users = + special_domains[i].known_users; + + if (domain[0] != '\0') { + if (!strequal(domain, special_domains[i].name)) { + continue; + } + } + + if (users == NULL) + continue; + + for (j=0; users[j].name != NULL; j++) { + if ( strequal(users[j].name, name) ) { + sid_compose(sid, special_domains[i].sid, + users[j].rid); + *pdomain = talloc_strdup( + mem_ctx, special_domains[i].name); + return True; + } + } + } + + return False; +} diff --git a/source3/lib/version.c b/source3/lib/version.c new file mode 100644 index 0000000..28fcd1b --- /dev/null +++ b/source3/lib/version.c @@ -0,0 +1,32 @@ +/* + Unix SMB/CIFS implementation. + Samba Version functions + + Copyright (C) Stefan Metzmacher 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "version.h" + +const char *samba_version_string(void) +{ + return SAMBA_VERSION_STRING; +} + +const char *samba_copyright_string(void) +{ + return SAMBA_COPYRIGHT_STRING; +} diff --git a/source3/lib/version_test.c b/source3/lib/version_test.c new file mode 100644 index 0000000..880cfeb --- /dev/null +++ b/source3/lib/version_test.c @@ -0,0 +1,26 @@ +/* + * Unix SMB/CIFS implementation. + * version_test - test program for samba_version_strion() + * Copyright (C) Michael Adam 2009 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +int main(void) +{ + printf("%s\n", samba_version_string()); + return 0; +} diff --git a/source3/lib/winbind_util.c b/source3/lib/winbind_util.c new file mode 100644 index 0000000..7e3f8ab --- /dev/null +++ b/source3/lib/winbind_util.c @@ -0,0 +1,403 @@ +/* + Unix SMB/CIFS implementation. + Winbind Utility functions + + Copyright (C) Gerald (Jerry) Carter 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/security/security.h" +#include "../lib/util/util_pw.h" +#include "nsswitch/libwbclient/wbclient.h" + +#include "lib/winbind_util.h" + +#if defined(WITH_WINBIND) + +struct passwd * winbind_getpwnam(const char * name) +{ + wbcErr result; + struct passwd * tmp_pwd = NULL; + struct passwd * pwd = NULL; + + result = wbcGetpwnam(name, &tmp_pwd); + if (result != WBC_ERR_SUCCESS) + return pwd; + + pwd = tcopy_passwd(talloc_tos(), tmp_pwd); + + wbcFreeMemory(tmp_pwd); + + return pwd; +} + +struct passwd * winbind_getpwsid(const struct dom_sid *sid) +{ + wbcErr result; + struct passwd * tmp_pwd = NULL; + struct passwd * pwd = NULL; + struct wbcDomainSid dom_sid; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcGetpwsid(&dom_sid, &tmp_pwd); + if (result != WBC_ERR_SUCCESS) + return pwd; + + pwd = tcopy_passwd(talloc_tos(), tmp_pwd); + + wbcFreeMemory(tmp_pwd); + + return pwd; +} + +/* Call winbindd to convert a name to a sid */ + +bool winbind_lookup_name(const char *dom_name, const char *name, struct dom_sid *sid, + enum lsa_SidType *name_type) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + enum wbcSidType type; + + result = wbcLookupName(dom_name, name, &dom_sid, &type); + if (result != WBC_ERR_SUCCESS) + return false; + + memcpy(sid, &dom_sid, sizeof(struct dom_sid)); + *name_type = (enum lsa_SidType)type; + + return true; +} + +/* Call winbindd to convert sid to name */ + +bool winbind_lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, + const char **domain, const char **name, + enum lsa_SidType *name_type) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + enum wbcSidType type; + char *domain_name = NULL; + char *account_name = NULL; + struct dom_sid_buf buf; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcLookupSid(&dom_sid, &domain_name, &account_name, &type); + if (result != WBC_ERR_SUCCESS) + return false; + + /* Copy out result */ + + if (domain) { + *domain = talloc_strdup(mem_ctx, domain_name); + } + if (name) { + *name = talloc_strdup(mem_ctx, account_name); + } + *name_type = (enum lsa_SidType)type; + + DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n", + dom_sid_str_buf(sid, &buf), domain_name, account_name)); + + wbcFreeMemory(domain_name); + wbcFreeMemory(account_name); + + if ((domain && !*domain) || (name && !*name)) { + DEBUG(0,("winbind_lookup_sid: talloc() failed!\n")); + return false; + } + + + return true; +} + +/* Ping winbindd to see it is alive */ + +bool winbind_ping(void) +{ + wbcErr result = wbcPing(); + + return (result == WBC_ERR_SUCCESS); +} + +/* Call winbindd to convert SID to uid */ + +bool winbind_sid_to_uid(uid_t *puid, const struct dom_sid *sid) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcSidToUid(&dom_sid, puid); + + return (result == WBC_ERR_SUCCESS); +} + +/* Call winbindd to convert SID to gid */ + +bool winbind_sid_to_gid(gid_t *pgid, const struct dom_sid *sid) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcSidToGid(&dom_sid, pgid); + + return (result == WBC_ERR_SUCCESS); +} + +bool winbind_xid_to_sid(struct dom_sid *sid, const struct unixid *xid) +{ + struct wbcUnixId wbc_xid; + struct wbcDomainSid dom_sid; + wbcErr result; + + switch (xid->type) { + case ID_TYPE_UID: + wbc_xid = (struct wbcUnixId) { + .type = WBC_ID_TYPE_UID, .id.uid = xid->id + }; + break; + case ID_TYPE_GID: + wbc_xid = (struct wbcUnixId) { + .type = WBC_ID_TYPE_GID, .id.gid = xid->id + }; + break; + default: + return false; + } + + result = wbcUnixIdsToSids(&wbc_xid, 1, &dom_sid); + if (result != WBC_ERR_SUCCESS) { + return false; + } + + memcpy(sid, &dom_sid, sizeof(struct dom_sid)); + return true; +} + +/* Check for a trusted domain */ + +wbcErr wb_is_trusted_domain(const char *domain) +{ + wbcErr result; + struct wbcDomainInfo *info = NULL; + + result = wbcDomainInfo(domain, &info); + + if (WBC_ERROR_IS_OK(result)) { + wbcFreeMemory(info); + } + + return result; +} + +/* Lookup a set of rids in a given domain */ + +bool winbind_lookup_rids(TALLOC_CTX *mem_ctx, + const struct dom_sid *domain_sid, + int num_rids, uint32_t *rids, + const char **domain_name, + const char ***names, enum lsa_SidType **types) +{ + const char *dom_name = NULL; + const char **namelist = NULL; + enum wbcSidType *name_types = NULL; + struct wbcDomainSid dom_sid; + wbcErr ret; + int i; + + memcpy(&dom_sid, domain_sid, sizeof(struct wbcDomainSid)); + + ret = wbcLookupRids(&dom_sid, num_rids, rids, + &dom_name, &namelist, &name_types); + if (ret != WBC_ERR_SUCCESS) { + return false; + } + + *domain_name = talloc_strdup(mem_ctx, dom_name); + *names = talloc_array(mem_ctx, const char*, num_rids); + *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids); + + for(i=0; i<num_rids; i++) { + (*names)[i] = talloc_strdup(*names, namelist[i]); + (*types)[i] = (enum lsa_SidType)name_types[i]; + } + + wbcFreeMemory(discard_const_p(char, dom_name)); + wbcFreeMemory(namelist); + wbcFreeMemory(name_types); + + return true; +} + +/* Ask Winbind to allocate a new uid for us */ + +bool winbind_allocate_uid(uid_t *uid) +{ + wbcErr ret; + + ret = wbcAllocateUid(uid); + + return (ret == WBC_ERR_SUCCESS); +} + +/* Ask Winbind to allocate a new gid for us */ + +bool winbind_allocate_gid(gid_t *gid) +{ + wbcErr ret; + + ret = wbcAllocateGid(gid); + + return (ret == WBC_ERR_SUCCESS); +} + +bool winbind_lookup_usersids(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + uint32_t *p_num_sids, + struct dom_sid **p_sids) +{ + wbcErr ret; + struct wbcDomainSid dom_sid; + struct wbcDomainSid *sid_list = NULL; + uint32_t num_sids; + + memcpy(&dom_sid, user_sid, sizeof(dom_sid)); + + ret = wbcLookupUserSids(&dom_sid, + false, + &num_sids, + &sid_list); + if (ret != WBC_ERR_SUCCESS) { + return false; + } + + *p_sids = talloc_array(mem_ctx, struct dom_sid, num_sids); + if (*p_sids == NULL) { + wbcFreeMemory(sid_list); + return false; + } + + memcpy(*p_sids, sid_list, sizeof(dom_sid) * num_sids); + + *p_num_sids = num_sids; + wbcFreeMemory(sid_list); + + return true; +} + +#else /* WITH_WINBIND */ + +struct passwd * winbind_getpwnam(const char * name) +{ + return NULL; +} + +struct passwd * winbind_getpwsid(const struct dom_sid *sid) +{ + return NULL; +} + +bool winbind_lookup_name(const char *dom_name, const char *name, struct dom_sid *sid, + enum lsa_SidType *name_type) +{ + return false; +} + +/* Call winbindd to convert sid to name */ + +bool winbind_lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, + const char **domain, const char **name, + enum lsa_SidType *name_type) +{ + return false; +} + +/* Ping winbindd to see it is alive */ + +bool winbind_ping(void) +{ + return false; +} + +/* Call winbindd to convert SID to uid */ + +bool winbind_sid_to_uid(uid_t *puid, const struct dom_sid *sid) +{ + return false; +} + +/* Call winbindd to convert SID to gid */ + +bool winbind_sid_to_gid(gid_t *pgid, const struct dom_sid *sid) +{ + return false; +} + +/* Call winbindd to convert uid or gid to SID */ + +bool winbind_xid_to_sid(struct dom_sid *sid, const struct unixid *xid) +{ + return false; +} + +/* Check for a trusted domain */ + +wbcErr wb_is_trusted_domain(const char *domain) +{ + return WBC_ERR_UNKNOWN_FAILURE; +} + +/* Lookup a set of rids in a given domain */ + +bool winbind_lookup_rids(TALLOC_CTX *mem_ctx, + const struct dom_sid *domain_sid, + int num_rids, uint32_t *rids, + const char **domain_name, + const char ***names, enum lsa_SidType **types) +{ + return false; +} + +/* Ask Winbind to allocate a new uid for us */ + +bool winbind_allocate_uid(uid_t *uid) +{ + return false; +} + +/* Ask Winbind to allocate a new gid for us */ + +bool winbind_allocate_gid(gid_t *gid) +{ + return false; +} + +bool winbind_lookup_usersids(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + uint32_t *p_num_sids, + struct dom_sid **p_sids) +{ + return false; +} + +#endif /* WITH_WINBIND */ diff --git a/source3/lib/winbind_util.h b/source3/lib/winbind_util.h new file mode 100644 index 0000000..6056190 --- /dev/null +++ b/source3/lib/winbind_util.h @@ -0,0 +1,56 @@ +/* + Unix SMB/CIFS implementation. + Winbind Utility functions + + Copyright (C) Gerald (Jerry) Carter 2007 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __LIB__WINBIND_UTIL_H__ +#define __LIB__WINBIND_UTIL_H__ + +#include "../librpc/gen_ndr/lsa.h" +#include "librpc/gen_ndr/idmap.h" + +/* needed for wbcErr below */ +#include "nsswitch/libwbclient/wbclient.h" + +/* The following definitions come from lib/winbind_util.c */ + +bool winbind_lookup_name(const char *dom_name, const char *name, struct dom_sid *sid, + enum lsa_SidType *name_type); +bool winbind_lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, + const char **domain, const char **name, + enum lsa_SidType *name_type); +bool winbind_ping(void); +bool winbind_sid_to_uid(uid_t *puid, const struct dom_sid *sid); +bool winbind_sid_to_gid(gid_t *pgid, const struct dom_sid *sid); +bool winbind_xid_to_sid(struct dom_sid *sid, const struct unixid *xid); +struct passwd * winbind_getpwnam(const char * sname); +struct passwd * winbind_getpwsid(const struct dom_sid *sid); +wbcErr wb_is_trusted_domain(const char *domain); +bool winbind_lookup_rids(TALLOC_CTX *mem_ctx, + const struct dom_sid *domain_sid, + int num_rids, uint32_t *rids, + const char **domain_name, + const char ***names, enum lsa_SidType **types); +bool winbind_allocate_uid(uid_t *uid); +bool winbind_allocate_gid(gid_t *gid); +bool winbind_lookup_usersids(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + uint32_t *p_num_sids, + struct dom_sid **p_sids); + +#endif /* __LIB__WINBIND_UTIL_H__ */ diff --git a/source3/lib/wins_srv.c b/source3/lib/wins_srv.c new file mode 100644 index 0000000..ea94dc1 --- /dev/null +++ b/source3/lib/wins_srv.c @@ -0,0 +1,409 @@ +/* + Unix SMB/CIFS implementation. + Samba wins server helper functions + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Christopher R. Hertel 2000 + Copyright (C) Tim Potter 2003 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/gencache.h" +#include "lib/util/string_wrappers.h" + +/* + This is pretty much a complete rewrite of the earlier code. The main + aim of the rewrite is to add support for having multiple wins server + lists, so Samba can register with multiple groups of wins servers + and each group has a failover list of wins servers. + + Central to the way it all works is the idea of a wins server + 'tag'. A wins tag is a label for a group of wins servers. For + example if you use + + wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61 + + then you would have two groups of wins servers, one tagged with the + name 'fred' and the other with the name 'mary'. I would usually + recommend using interface names instead of 'fred' and 'mary' but + they can be any alpha string. + + Now, how does it all work. Well, nmbd needs to register each of its + IPs with each of its names once with each group of wins servers. So + it tries registering with the first one mentioned in the list, then + if that fails it marks that WINS server dead and moves onto the next + one. + + In the client code things are a bit different. As each of the groups + of wins servers is a separate name space we need to try each of the + groups until we either succeed or we run out of wins servers to + try. If we get a negative response from a wins server then that + means the name doesn't exist in that group, so we give up on that + group and move to the next group. If we don't get a response at all + then maybe the wins server is down, in which case we need to + failover to the next one for that group. + + confused yet? (tridge) +*/ + +/* how long a server is marked dead for */ +#define DEATH_TIME 600 + +/* The list of dead wins servers is stored in gencache.tdb. Each server is + marked dead from the point of view of a given source address. We keep a + separate dead list for each src address to cope with multiple interfaces + that are not routable to each other. + */ + +#define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */ + +static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL; + + wins_ip_addr = SMB_STRDUP(inet_ntoa(wins_ip)); + src_ip_addr = SMB_STRDUP(inet_ntoa(src_ip)); + + if ( !wins_ip_addr || !src_ip_addr ) { + DEBUG(0,("wins_srv_keystr: malloc error\n")); + goto done; + } + + if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) { + DEBUG(0, (": ns_srv_keystr: malloc error for key string\n")); + } + +done: + SAFE_FREE(wins_ip_addr); + SAFE_FREE(src_ip_addr); + + return keystr; +} + +/* + see if an ip is on the dead list +*/ + +bool wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = wins_srv_keystr(wins_ip, src_ip); + bool result; + + /* If the key exists then the WINS server has been marked as dead */ + + result = gencache_get(keystr, NULL, NULL, NULL); + SAFE_FREE(keystr); + + DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip), + result ? "dead" : "alive")); + + return result; +} + + +/* + mark a wins server as being alive (for the moment) +*/ +void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = wins_srv_keystr(wins_ip, src_ip); + + gencache_del(keystr); + SAFE_FREE(keystr); + + DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n", + inet_ntoa(wins_ip))); +} + +/* + mark a wins server as temporarily dead +*/ +void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr; + + if (is_zero_ip_v4(wins_ip) || wins_srv_is_dead(wins_ip, src_ip)) + return; + + keystr = wins_srv_keystr(wins_ip, src_ip); + + gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME); + + SAFE_FREE(keystr); + + DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n", + inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip))); +} + +/* + return the total number of wins servers, dead or not +*/ +unsigned wins_srv_count(void) +{ + const char **list; + int count = 0; + + if (lp_we_are_a_wins_server()) { + /* simple - just talk to ourselves */ + return 1; + } + + list = lp_wins_server_list(); + for (count=0; list && list[count]; count++) + /* nop */ ; + + return count; +} + +/* an internal convenience structure for an IP with a short string tag + attached */ +struct tagged_ip { + fstring tag; + struct in_addr ip; +}; + +/* + parse an IP string that might be in tagged format + the result is a tagged_ip structure containing the tag + and the ip in in_addr format. If there is no tag then + use the tag '*' +*/ +static void parse_ip(struct tagged_ip *ip, const char *str) +{ + char *s = strchr(str, ':'); + if (!s) { + fstrcpy(ip->tag, "*"); + ip->ip = interpret_addr2(str); + return; + } + + ip->ip = interpret_addr2(s+1); + fstrcpy(ip->tag, str); + s = strchr(ip->tag, ':'); + if (s) { + *s = 0; + } +} + + + +/* + return the list of wins server tags. A 'tag' is used to distinguish + wins server as either belonging to the same name space or a separate + name space. Usually you would setup your 'wins server' option to + list one or more wins server per interface and use the interface + name as your tag, but you are free to use any tag you like. +*/ +char **wins_srv_tags(void) +{ + char **ret = NULL; + unsigned int count=0, i, j; + const char **list; + + if (lp_we_are_a_wins_server()) { + /* give the caller something to chew on. This makes + the rest of the logic simpler (ie. less special cases) */ + ret = SMB_MALLOC_ARRAY(char *, 2); + if (!ret) return NULL; + ret[0] = SMB_STRDUP("*"); + ret[1] = NULL; + return ret; + } + + list = lp_wins_server_list(); + if (!list) + return NULL; + + /* yes, this is O(n^2) but n is very small */ + for (i=0;list[i];i++) { + struct tagged_ip t_ip; + + parse_ip(&t_ip, list[i]); + + /* see if we already have it */ + for (j=0;j<count;j++) { + if (strcmp(ret[j], t_ip.tag) == 0) { + break; + } + } + + if (j != count) { + /* we already have it. Move along */ + continue; + } + + /* add it to the list */ + ret = SMB_REALLOC_ARRAY(ret, char *, count+2); + if (!ret) { + return NULL; + } + ret[count] = SMB_STRDUP(t_ip.tag); + if (!ret[count]) break; + count++; + } + + if (count) { + /* make sure we null terminate */ + ret[count] = NULL; + } + + return ret; +} + +/* free a list of wins server tags given by wins_srv_tags */ +void wins_srv_tags_free(char **list) +{ + int i; + if (!list) return; + for (i=0; list[i]; i++) { + free(list[i]); + } + free(list); +} + + +/* + return the IP of the currently active wins server for the given tag, + or the zero IP otherwise +*/ +struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip) +{ + const char **list; + int i; + struct tagged_ip t_ip; + + /* if we are a wins server then we always just talk to ourselves */ + if (lp_we_are_a_wins_server()) { + struct in_addr loopback_ip; + loopback_ip.s_addr = htonl(INADDR_LOOPBACK); + return loopback_ip; + } + + list = lp_wins_server_list(); + if (!list || !list[0]) { + struct in_addr ip; + zero_ip_v4(&ip); + return ip; + } + + /* find the first live one for this tag */ + for (i=0; list[i]; i++) { + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) != 0) { + /* not for the right tag. Move along */ + continue; + } + if (!wins_srv_is_dead(t_ip.ip, src_ip)) { + fstring src_name; + fstrcpy(src_name, inet_ntoa(src_ip)); + DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", + tag, + src_name, + inet_ntoa(t_ip.ip))); + return t_ip.ip; + } + } + + /* they're all dead - try the first one until they revive */ + for (i=0; list[i]; i++) { + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) != 0) { + continue; + } + return t_ip.ip; + } + + /* this can't happen?? */ + zero_ip_v4(&t_ip.ip); + return t_ip.ip; +} + +bool wins_server_tag_ips(const char *tag, TALLOC_CTX *mem_ctx, + struct in_addr **pservers, size_t *pnum_servers) +{ + const char **list; + size_t i, num_servers; + struct in_addr *servers; + + list = lp_wins_server_list(); + if ((list == NULL) || (list[0] == NULL)) { + return false; + } + + num_servers = 0; + + for (i=0; list[i] != NULL; i++) { + struct tagged_ip t_ip; + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) == 0) { + /* Wrap check. */ + if (num_servers + 1 < num_servers) { + return false; + } + num_servers += 1; + } + } + + servers = talloc_array(mem_ctx, struct in_addr, num_servers); + if (servers == NULL) { + return false; + } + + num_servers = 0; + + for (i=0; list[i] != NULL; i++) { + struct tagged_ip t_ip; + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) == 0) { + servers[num_servers] = t_ip.ip; + num_servers += 1; + } + } + *pnum_servers = num_servers; + *pservers = servers; + return true; +} + + +/* + return a count of the number of IPs for a particular tag, including + dead ones +*/ +unsigned wins_srv_count_tag(const char *tag) +{ + const char **list; + int i, count=0; + + /* if we are a wins server then we always just talk to ourselves */ + if (lp_we_are_a_wins_server()) { + return 1; + } + + list = lp_wins_server_list(); + if (!list || !list[0]) { + return 0; + } + + /* find the first live one for this tag */ + for (i=0; list[i]; i++) { + struct tagged_ip t_ip; + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) == 0) { + count++; + } + } + + return count; +} diff --git a/source3/lib/xattr_tdb.c b/source3/lib/xattr_tdb.c new file mode 100644 index 0000000..564cdd8 --- /dev/null +++ b/source3/lib/xattr_tdb.c @@ -0,0 +1,468 @@ +/* + * Store posix-level xattrs in a tdb + * + * Copyright (C) Andrew Bartlett 2011 + * + * extracted from vfs_xattr_tdb by + * + * Copyright (C) Volker Lendecke, 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "source3/include/includes.h" +#include "system/filesys.h" +#include "librpc/gen_ndr/xattr.h" +#include "librpc/gen_ndr/ndr_xattr.h" +#include "librpc/gen_ndr/file_id.h" +#include "dbwrap/dbwrap.h" +#include "lib/util/util_tdb.h" +#include "source3/lib/xattr_tdb.h" +#include "source3/lib/file_id.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_VFS + +/* + * unmarshall tdb_xattrs + */ + +static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx, + const TDB_DATA *data, + struct tdb_xattrs **presult) +{ + DATA_BLOB blob; + enum ndr_err_code ndr_err; + struct tdb_xattrs *result; + + if (!(result = talloc_zero(mem_ctx, struct tdb_xattrs))) { + return NT_STATUS_NO_MEMORY; + } + + if (data->dsize == 0) { + *presult = result; + return NT_STATUS_OK; + } + + blob = data_blob_const(data->dptr, data->dsize); + + ndr_err = ndr_pull_struct_blob(&blob, result, result, + (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n", + ndr_errstr(ndr_err))); + TALLOC_FREE(result); + return ndr_map_error2ntstatus(ndr_err); + } + + *presult = result; + return NT_STATUS_OK; +} + +/* + * marshall tdb_xattrs + */ + +static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx, + const struct tdb_xattrs *attribs, + TDB_DATA *data) +{ + DATA_BLOB blob; + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_struct_blob(&blob, mem_ctx, attribs, + (ndr_push_flags_fn_t)ndr_push_tdb_xattrs); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n", + ndr_errstr(ndr_err))); + return ndr_map_error2ntstatus(ndr_err); + } + + *data = make_tdb_data(blob.data, blob.length); + return NT_STATUS_OK; +} + +/* + * Load tdb_xattrs for a file from the tdb + */ + +static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx, + struct db_context *db_ctx, + const struct file_id *id, + struct tdb_xattrs **presult) +{ + uint8_t id_buf[16]; + NTSTATUS status; + TDB_DATA data; + + /* For backwards compatibility only store the dev/inode. */ + push_file_id_16((char *)id_buf, id); + + status = dbwrap_fetch(db_ctx, mem_ctx, + make_tdb_data(id_buf, sizeof(id_buf)), + &data); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + return status; + } + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + status = xattr_tdb_pull_attrs(mem_ctx, &data, presult); + TALLOC_FREE(data.dptr); + return status; +} + +/* + * fetch_lock the tdb_ea record for a file + */ + +static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx, + struct db_context *db_ctx, + const struct file_id *id) +{ + uint8_t id_buf[16]; + + /* For backwards compatibility only store the dev/inode. */ + push_file_id_16((char *)id_buf, id); + return dbwrap_fetch_locked(db_ctx, mem_ctx, + make_tdb_data(id_buf, sizeof(id_buf))); +} + +/* + * Save tdb_xattrs to a previously fetch_locked record + */ + +static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec, + const struct tdb_xattrs *attribs) +{ + TDB_DATA data = tdb_null; + NTSTATUS status; + + status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n", + nt_errstr(status))); + return status; + } + + status = dbwrap_record_store(rec, data, 0); + + TALLOC_FREE(data.dptr); + + return status; +} + +/* + * Worker routine for getxattr and fgetxattr + */ + +ssize_t xattr_tdb_getattr(struct db_context *db_ctx, + TALLOC_CTX *mem_ctx, + const struct file_id *id, + const char *name, DATA_BLOB *blob) +{ + struct tdb_xattrs *attribs; + uint32_t i; + ssize_t result = -1; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + struct file_id_buf buf; + + DBG_DEBUG("xattr_tdb_getattr called for file %s, name %s\n", + file_id_str_buf(*id, &buf), name); + + status = xattr_tdb_load_attrs(frame, db_ctx, id, &attribs); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n", + nt_errstr(status))); + TALLOC_FREE(frame); + errno = EINVAL; + return -1; + } + + for (i=0; i<attribs->num_eas; i++) { + if (strcmp(attribs->eas[i].name, name) == 0) { + break; + } + } + + if (i == attribs->num_eas) { + errno = ENOATTR; + goto fail; + } + + *blob = attribs->eas[i].value; + talloc_steal(mem_ctx, blob->data); + result = attribs->eas[i].value.length; + + fail: + TALLOC_FREE(frame); + return result; +} + +/* + * Worker routine for setxattr and fsetxattr + */ + +int xattr_tdb_setattr(struct db_context *db_ctx, + const struct file_id *id, const char *name, + const void *value, size_t size, int flags) +{ + NTSTATUS status; + struct db_record *rec; + struct tdb_xattrs *attribs; + uint32_t i; + TDB_DATA data; + TALLOC_CTX *frame = talloc_stackframe(); + struct file_id_buf buf; + + DBG_DEBUG("xattr_tdb_setattr called for file %s, name %s\n", + file_id_str_buf(*id, &buf), name); + + rec = xattr_tdb_lock_attrs(frame, db_ctx, id); + + if (rec == NULL) { + DEBUG(0, ("xattr_tdb_lock_attrs failed\n")); + errno = EINVAL; + return -1; + } + + data = dbwrap_record_get_value(rec); + + status = xattr_tdb_pull_attrs(rec, &data, &attribs); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n", + nt_errstr(status))); + TALLOC_FREE(frame); + return -1; + } + + for (i=0; i<attribs->num_eas; i++) { + if (strcmp(attribs->eas[i].name, name) == 0) { + if (flags & XATTR_CREATE) { + TALLOC_FREE(frame); + errno = EEXIST; + return -1; + } + break; + } + } + + if (i == attribs->num_eas) { + struct xattr_EA *tmp; + + if (flags & XATTR_REPLACE) { + TALLOC_FREE(frame); + errno = ENOATTR; + return -1; + } + + tmp = talloc_realloc( + attribs, attribs->eas, struct xattr_EA, + attribs->num_eas+ 1); + + if (tmp == NULL) { + DEBUG(0, ("talloc_realloc failed\n")); + TALLOC_FREE(frame); + errno = ENOMEM; + return -1; + } + + attribs->eas = tmp; + attribs->num_eas += 1; + } + + attribs->eas[i].name = name; + attribs->eas[i].value.data = discard_const_p(uint8_t, value); + attribs->eas[i].value.length = size; + + status = xattr_tdb_save_attrs(rec, attribs); + + TALLOC_FREE(frame); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("save failed: %s\n", nt_errstr(status))); + return -1; + } + + return 0; +} + +/* + * Worker routine for listxattr and flistxattr + */ + +ssize_t xattr_tdb_listattr(struct db_context *db_ctx, + const struct file_id *id, char *list, + size_t size) +{ + NTSTATUS status; + struct tdb_xattrs *attribs; + uint32_t i; + size_t len = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + status = xattr_tdb_load_attrs(frame, db_ctx, id, &attribs); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) + { + DEBUG(0, ("xattr_tdb_fetch_attrs failed: %s\n", + nt_errstr(status))); + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + TALLOC_FREE(frame); + return 0; + } + + DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n", + attribs->num_eas)); + + for (i=0; i<attribs->num_eas; i++) { + size_t tmp; + + DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n", + attribs->eas[i].name)); + + tmp = strlen(attribs->eas[i].name); + + /* + * Try to protect against overflow + */ + + if (len + (tmp+1) < len) { + TALLOC_FREE(frame); + errno = EINVAL; + return -1; + } + + /* + * Take care of the terminating NULL + */ + len += (tmp + 1); + } + + if (len > size) { + TALLOC_FREE(frame); + errno = ERANGE; + return len; + } + + len = 0; + + for (i=0; i<attribs->num_eas; i++) { + strlcpy(list+len, attribs->eas[i].name, + size-len); + len += (strlen(attribs->eas[i].name) + 1); + } + + TALLOC_FREE(frame); + return len; +} + +/* + * Worker routine for removexattr and fremovexattr + */ + +int xattr_tdb_removeattr(struct db_context *db_ctx, + const struct file_id *id, const char *name) +{ + NTSTATUS status; + struct db_record *rec; + struct tdb_xattrs *attribs; + uint32_t i; + TDB_DATA value; + + rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id); + + if (rec == NULL) { + DEBUG(0, ("xattr_tdb_lock_attrs failed\n")); + errno = EINVAL; + return -1; + } + + value = dbwrap_record_get_value(rec); + + status = xattr_tdb_pull_attrs(rec, &value, &attribs); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n", + nt_errstr(status))); + TALLOC_FREE(rec); + return -1; + } + + for (i=0; i<attribs->num_eas; i++) { + if (strcmp(attribs->eas[i].name, name) == 0) { + break; + } + } + + if (i == attribs->num_eas) { + TALLOC_FREE(rec); + errno = ENOATTR; + return -1; + } + + attribs->eas[i] = + attribs->eas[attribs->num_eas-1]; + attribs->num_eas -= 1; + + if (attribs->num_eas == 0) { + dbwrap_record_delete(rec); + TALLOC_FREE(rec); + return 0; + } + + status = xattr_tdb_save_attrs(rec, attribs); + + TALLOC_FREE(rec); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("save failed: %s\n", nt_errstr(status))); + return -1; + } + + return 0; +} + +/* + * Worker routine for unlink and rmdir + */ + +void xattr_tdb_remove_all_attrs(struct db_context *db_ctx, + const struct file_id *id) +{ + struct db_record *rec; + rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id); + + /* + * If rec == NULL there's not much we can do about it + */ + + if (rec != NULL) { + dbwrap_record_delete(rec); + TALLOC_FREE(rec); + } +} diff --git a/source3/lib/xattr_tdb.h b/source3/lib/xattr_tdb.h new file mode 100644 index 0000000..03bc43a --- /dev/null +++ b/source3/lib/xattr_tdb.h @@ -0,0 +1,41 @@ +/* + * Store posix-level xattrs in a tdb + * + * Copyright (C) Andrew Bartlett 2011 + * + * extracted from vfs_xattr_tdb by + * + * Copyright (C) Volker Lendecke, 2007 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "librpc/gen_ndr/file_id.h" + +/* The following definitions come from lib/util/xattr_tdb.c */ + +ssize_t xattr_tdb_getattr(struct db_context *db_ctx, + TALLOC_CTX *mem_ctx, + const struct file_id *id, + const char *name, DATA_BLOB *blob); +int xattr_tdb_setattr(struct db_context *db_ctx, + const struct file_id *id, const char *name, + const void *value, size_t size, int flags); +ssize_t xattr_tdb_listattr(struct db_context *db_ctx, + const struct file_id *id, char *list, + size_t size); +int xattr_tdb_removeattr(struct db_context *db_ctx, + const struct file_id *id, const char *name); +void xattr_tdb_remove_all_attrs(struct db_context *db_ctx, + const struct file_id *id); |