diff options
Diffstat (limited to 'source3/utils/net_ads.c')
-rw-r--r-- | source3/utils/net_ads.c | 4091 |
1 files changed, 4091 insertions, 0 deletions
diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c new file mode 100644 index 0000000..70d05dc --- /dev/null +++ b/source3/utils/net_ads.c @@ -0,0 +1,4091 @@ +/* + Samba Unix/Linux SMB client library + net ads commands + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com) + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2006 Gerald (Jerry) Carter (jerry@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" +#include "utils/net.h" +#include "libsmb/namequery.h" +#include "rpc_client/cli_pipe.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "../librpc/gen_ndr/ndr_spoolss.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "ads.h" +#include "libads/cldap.h" +#include "../lib/addns/dnsquery.h" +#include "../libds/common/flags.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "libnet/libnet_join.h" +#include "smb_krb5.h" +#include "secrets.h" +#include "krb5_env.h" +#include "../libcli/security/security.h" +#include "libsmb/libsmb.h" +#include "lib/param/loadparm.h" +#include "utils/net_dns.h" +#include "auth/kerberos/pac_utils.h" +#include "lib/util/string_wrappers.h" + +#ifdef HAVE_JANSSON +#include <jansson.h> +#include "audit_logging.h" /* various JSON helpers */ +#include "auth/common_auth.h" +#endif /* [HAVE_JANSSON] */ + +#ifdef HAVE_ADS + +/* when we do not have sufficient input parameters to contact a remote domain + * we always fall back to our own realm - Guenther*/ + +static const char *assume_own_realm(struct net_context *c) +{ + if (!c->opt_host && strequal(lp_workgroup(), c->opt_target_workgroup)) { + return lp_realm(); + } + + return NULL; +} + +#ifdef HAVE_JANSSON + +/* + * note: JSON output deliberately bypasses gettext so as to provide the same + * output irrespective of the locale. + */ + +static int output_json(const struct json_object *jsobj) +{ + TALLOC_CTX *ctx = NULL; + char *json = NULL; + + if (json_is_invalid(jsobj)) { + return -1; + } + + ctx = talloc_new(NULL); + if (ctx == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + return -1; + } + + json = json_to_string(ctx, jsobj); + if (!json) { + d_fprintf(stderr, _("error encoding to JSON\n")); + return -1; + } + + d_printf("%s\n", json); + TALLOC_FREE(ctx); + + return 0; +} + +static int net_ads_cldap_netlogon_json + (ADS_STRUCT *ads, + const char *addr, + const struct NETLOGON_SAM_LOGON_RESPONSE_EX *reply) +{ + struct json_object jsobj = json_new_object(); + struct json_object flagsobj = json_new_object(); + char response_type [32] = { '\0' }; + int ret = 0; + + if (json_is_invalid(&jsobj) || json_is_invalid(&flagsobj)) { + d_fprintf(stderr, _("error setting up JSON value\n")); + + goto failure; + } + + switch (reply->command) { + case LOGON_SAM_LOGON_USER_UNKNOWN_EX: + strncpy(response_type, + "LOGON_SAM_LOGON_USER_UNKNOWN_EX", + sizeof(response_type)); + break; + case LOGON_SAM_LOGON_RESPONSE_EX: + strncpy(response_type, "LOGON_SAM_LOGON_RESPONSE_EX", + sizeof(response_type)); + break; + default: + snprintf(response_type, sizeof(response_type), "0x%x", + reply->command); + break; + } + + ret = json_add_string(&jsobj, "Information for Domain Controller", + addr); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Response Type", response_type); + if (ret != 0) { + goto failure; + } + + ret = json_add_guid(&jsobj, "GUID", &reply->domain_uuid); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is a PDC", + reply->server_type & NBT_SERVER_PDC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is a GC of the forest", + reply->server_type & NBT_SERVER_GC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is an LDAP server", + reply->server_type & NBT_SERVER_LDAP); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Supports DS", + reply->server_type & NBT_SERVER_DS); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is running a KDC", + reply->server_type & NBT_SERVER_KDC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is running time services", + reply->server_type & NBT_SERVER_TIMESERV); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is the closest DC", + reply->server_type & NBT_SERVER_CLOSEST); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is writable", + reply->server_type & NBT_SERVER_WRITABLE); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Has a hardware clock", + reply->server_type & NBT_SERVER_GOOD_TIMESERV); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, + "Is a non-domain NC serviced by LDAP server", + reply->server_type & NBT_SERVER_NDNC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool + (&flagsobj, "Is NT6 DC that has some secrets", + reply->server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool + (&flagsobj, "Is NT6 DC that has all secrets", + reply->server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Runs Active Directory Web Services", + reply->server_type & NBT_SERVER_ADS_WEB_SERVICE); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Runs on Windows 2012 or later", + reply->server_type & NBT_SERVER_DS_8); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Forest", reply->forest); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Domain", reply->dns_domain); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Domain Controller", reply->pdc_dns_name); + if (ret != 0) { + goto failure; + } + + + ret = json_add_string(&jsobj, "Pre-Win2k Domain", reply->domain_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Pre-Win2k Hostname", reply->pdc_name); + if (ret != 0) { + goto failure; + } + + if (*reply->user_name) { + ret = json_add_string(&jsobj, "User name", reply->user_name); + if (ret != 0) { + goto failure; + } + } + + ret = json_add_string(&jsobj, "Server Site Name", reply->server_site); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Client Site Name", reply->client_site); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&jsobj, "NT Version", reply->nt_version); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&jsobj, "LMNT Token", reply->lmnt_token); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&jsobj, "LM20 Token", reply->lm20_token); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "Flags", &flagsobj); + if (ret != 0) { + goto failure; + } + + ret = output_json(&jsobj); + json_free(&jsobj); /* frees flagsobj recursively */ + + return ret; + +failure: + json_free(&flagsobj); + json_free(&jsobj); + + return ret; +} + +#else /* [HAVE_JANSSON] */ + +static int net_ads_cldap_netlogon_json + (ADS_STRUCT *ads, + const char *addr, + const struct NETLOGON_SAM_LOGON_RESPONSE_EX * reply) +{ + d_fprintf(stderr, _("JSON support not available\n")); + + return -1; +} + +#endif /* [HAVE_JANSSON] */ + +/* + do a cldap netlogon query +*/ +static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads) +{ + char addr[INET6_ADDRSTRLEN]; + struct NETLOGON_SAM_LOGON_RESPONSE_EX reply; + + print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); + + if ( !ads_cldap_netlogon_5(talloc_tos(), &ads->ldap.ss, ads->server.realm, &reply ) ) { + d_fprintf(stderr, _("CLDAP query failed!\n")); + return -1; + } + + if (c->opt_json) { + return net_ads_cldap_netlogon_json(ads, addr, &reply); + } + + d_printf(_("Information for Domain Controller: %s\n\n"), + addr); + + d_printf(_("Response Type: ")); + switch (reply.command) { + case LOGON_SAM_LOGON_USER_UNKNOWN_EX: + d_printf("LOGON_SAM_LOGON_USER_UNKNOWN_EX\n"); + break; + case LOGON_SAM_LOGON_RESPONSE_EX: + d_printf("LOGON_SAM_LOGON_RESPONSE_EX\n"); + break; + default: + d_printf("0x%x\n", reply.command); + break; + } + + d_printf(_("GUID: %s\n"), GUID_string(talloc_tos(),&reply.domain_uuid)); + + d_printf(_("Flags:\n" + "\tIs a PDC: %s\n" + "\tIs a GC of the forest: %s\n" + "\tIs an LDAP server: %s\n" + "\tSupports DS: %s\n" + "\tIs running a KDC: %s\n" + "\tIs running time services: %s\n" + "\tIs the closest DC: %s\n" + "\tIs writable: %s\n" + "\tHas a hardware clock: %s\n" + "\tIs a non-domain NC serviced by LDAP server: %s\n" + "\tIs NT6 DC that has some secrets: %s\n" + "\tIs NT6 DC that has all secrets: %s\n" + "\tRuns Active Directory Web Services: %s\n" + "\tRuns on Windows 2012 or later: %s\n"), + (reply.server_type & NBT_SERVER_PDC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_GC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_LDAP) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_DS) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_KDC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_TIMESERV) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_CLOSEST) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_WRITABLE) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_NDNC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_ADS_WEB_SERVICE) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_DS_8) ? _("yes") : _("no")); + + + printf(_("Forest: %s\n"), reply.forest); + printf(_("Domain: %s\n"), reply.dns_domain); + printf(_("Domain Controller: %s\n"), reply.pdc_dns_name); + + printf(_("Pre-Win2k Domain: %s\n"), reply.domain_name); + printf(_("Pre-Win2k Hostname: %s\n"), reply.pdc_name); + + if (*reply.user_name) printf(_("User name: %s\n"), reply.user_name); + + printf(_("Server Site Name: %s\n"), reply.server_site); + printf(_("Client Site Name: %s\n"), reply.client_site); + + d_printf(_("NT Version: %d\n"), reply.nt_version); + d_printf(_("LMNT Token: %.2x\n"), reply.lmnt_token); + d_printf(_("LM20 Token: %.2x\n"), reply.lm20_token); + + return 0; +} + +/* + this implements the CLDAP based netlogon lookup requests + for finding the domain controller of a ADS domain +*/ +static int net_ads_lookup(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n" + "net ads lookup\n" + " %s", + _("Usage:"), + _("Find the ADS DC using CLDAP lookup.\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup_nobind(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Didn't find the cldap server!\n")); + goto out; + } + + if (!ads->config.realm) { + ads->config.realm = talloc_strdup(ads, c->opt_target_workgroup); + if (ads->config.realm == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + ads->ldap.port = 389; + } + + ret = net_ads_cldap_netlogon(c, ads); +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + + +#ifdef HAVE_JANSSON + +static int net_ads_info_json(ADS_STRUCT *ads) +{ + int ret = 0; + char addr[INET6_ADDRSTRLEN]; + time_t pass_time; + struct json_object jsobj = json_new_object(); + + if (json_is_invalid(&jsobj)) { + d_fprintf(stderr, _("error setting up JSON value\n")); + + goto failure; + } + + pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup); + + print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); + + ret = json_add_string (&jsobj, "LDAP server", addr); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "LDAP server name", + ads->config.ldap_server_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "Realm", ads->config.realm); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "Bind Path", ads->config.bind_path); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "LDAP port", ads->ldap.port); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "Server time", ads->config.current_time); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "KDC server", ads->auth.kdc_server); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "Server time offset", + ads->auth.time_offset); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "Last machine account password change", + pass_time); + if (ret != 0) { + goto failure; + } + + ret = output_json(&jsobj); +failure: + json_free(&jsobj); + + return ret; +} + +#else /* [HAVE_JANSSON] */ + +static int net_ads_info_json(ADS_STRUCT *ads) +{ + d_fprintf(stderr, _("JSON support not available\n")); + + return -1; +} + +#endif /* [HAVE_JANSSON] */ + + + +static int net_ads_info(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + char addr[INET6_ADDRSTRLEN]; + time_t pass_time; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n" + "net ads info\n" + " %s", + _("Usage:"), + _("Display information about an Active Directory " + "server.\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup_nobind(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Didn't find the ldap server!\n")); + goto out; + } + + if (!ads || !ads->config.realm) { + d_fprintf(stderr, _("Didn't find the ldap server!\n")); + goto out; + } + + /* Try to set the server's current time since we didn't do a full + TCP LDAP session initially */ + + if ( !ADS_ERR_OK(ads_current_time( ads )) ) { + d_fprintf( stderr, _("Failed to get server's current time!\n")); + } + + if (c->opt_json) { + ret = net_ads_info_json(ads); + goto out; + } + + pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup); + + print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); + + d_printf(_("LDAP server: %s\n"), addr); + d_printf(_("LDAP server name: %s\n"), ads->config.ldap_server_name); + d_printf(_("Realm: %s\n"), ads->config.realm); + d_printf(_("Bind Path: %s\n"), ads->config.bind_path); + d_printf(_("LDAP port: %d\n"), ads->ldap.port); + d_printf(_("Server time: %s\n"), + http_timestring(tmp_ctx, ads->config.current_time)); + + d_printf(_("KDC server: %s\n"), ads->auth.kdc_server ); + d_printf(_("Server time offset: %d\n"), ads->auth.time_offset ); + + d_printf(_("Last machine account password change: %s\n"), + http_timestring(tmp_ctx, pass_time)); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static ADS_STATUS ads_startup_int(struct net_context *c, + bool only_own_domain, + uint32_t auth_flags, + TALLOC_CTX *mem_ctx, + ADS_STRUCT **ads_ret) +{ + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool need_password = false; + bool second_time = false; + char *cp; + const char *realm = NULL; + bool tried_closest_dc = false; + enum credentials_use_kerberos krb5_state = + CRED_USE_KERBEROS_DISABLED; + + /* lp_realm() should be handled by a command line param, + However, the join requires that realm be set in smb.conf + and compares our realm with the remote server's so this is + ok until someone needs more flexibility */ + + *ads_ret = NULL; + +retry_connect: + if (only_own_domain) { + realm = lp_realm(); + } else { + realm = assume_own_realm(c); + } + + ads = ads_init(mem_ctx, + realm, + c->opt_target_workgroup, + c->opt_host, + ADS_SASL_PLAIN); + if (ads == NULL) { + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + if (!c->opt_user_name) { + c->opt_user_name = "administrator"; + } + + if (c->opt_user_specified) { + need_password = true; + } + +retry: + if (!c->opt_password && need_password && !c->opt_machine_pass) { + c->opt_password = net_prompt_pass(c, c->opt_user_name); + if (!c->opt_password) { + TALLOC_FREE(ads); + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + + if (c->opt_password) { + use_in_memory_ccache(); + TALLOC_FREE(ads->auth.password); + ads->auth.password = talloc_strdup(ads, c->opt_password); + if (ads->auth.password == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + } + + TALLOC_FREE(ads->auth.user_name); + ads->auth.user_name = talloc_strdup(ads, c->opt_user_name); + if (ads->auth.user_name == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + ads->auth.flags |= auth_flags; + + /* The ADS code will handle FIPS mode */ + krb5_state = cli_credentials_get_kerberos_state(c->creds); + switch (krb5_state) { + case CRED_USE_KERBEROS_REQUIRED: + ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS; + ads->auth.flags &= ~ADS_AUTH_ALLOW_NTLMSSP; + break; + case CRED_USE_KERBEROS_DESIRED: + ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS; + ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP; + break; + case CRED_USE_KERBEROS_DISABLED: + ads->auth.flags |= ADS_AUTH_DISABLE_KERBEROS; + ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP; + break; + } + + /* + * If the username is of the form "name@realm", + * extract the realm and convert to upper case. + * This is only used to establish the connection. + */ + if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) { + *cp++ = '\0'; + TALLOC_FREE(ads->auth.realm); + ads->auth.realm = talloc_asprintf_strupper_m(ads, "%s", cp); + if (ads->auth.realm == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR(LDAP_NO_MEMORY); + } + } else if (ads->auth.realm == NULL) { + const char *c_realm = cli_credentials_get_realm(c->creds); + + if (c_realm != NULL) { + ads->auth.realm = talloc_strdup(ads, c_realm); + if (ads->auth.realm == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + } + + status = ads_connect(ads); + + if (!ADS_ERR_OK(status)) { + + if (NT_STATUS_EQUAL(ads_ntstatus(status), + NT_STATUS_NO_LOGON_SERVERS)) { + DEBUG(0,("ads_connect: %s\n", ads_errstr(status))); + TALLOC_FREE(ads); + return status; + } + + if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) { + need_password = true; + second_time = true; + goto retry; + } else { + TALLOC_FREE(ads); + return status; + } + } + + /* when contacting our own domain, make sure we use the closest DC. + * This is done by reconnecting to ADS because only the first call to + * ads_connect will give us our own sitename */ + + if ((only_own_domain || !c->opt_host) && !tried_closest_dc) { + + tried_closest_dc = true; /* avoid loop */ + + if (!ads_closest_dc(ads)) { + + namecache_delete(ads->server.realm, 0x1C); + namecache_delete(ads->server.workgroup, 0x1C); + + TALLOC_FREE(ads); + + goto retry_connect; + } + } + + *ads_ret = talloc_move(mem_ctx, &ads); + return status; +} + +ADS_STATUS ads_startup(struct net_context *c, + bool only_own_domain, + TALLOC_CTX *mem_ctx, + ADS_STRUCT **ads) +{ + return ads_startup_int(c, only_own_domain, 0, mem_ctx, ads); +} + +ADS_STATUS ads_startup_nobind(struct net_context *c, + bool only_own_domain, + TALLOC_CTX *mem_ctx, + ADS_STRUCT **ads) +{ + return ads_startup_int(c, + only_own_domain, + ADS_AUTH_NO_BIND, + mem_ctx, + ads); +} + +/* + Check to see if connection can be made via ads. + ads_startup() stores the password in opt_password if it needs to so + that rpc or rap can use it without re-prompting. +*/ +static int net_ads_check_int(struct net_context *c, + const char *realm, + const char *workgroup, + const char *host) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads; + ADS_STATUS status; + int ret = -1; + + ads = ads_init(tmp_ctx, realm, workgroup, host, ADS_SASL_PLAIN); + if (ads == NULL) { + goto out; + } + + ads->auth.flags |= ADS_AUTH_NO_BIND; + + status = ads_connect(ads); + if ( !ADS_ERR_OK(status) ) { + goto out; + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_check_our_domain(struct net_context *c) +{ + return net_ads_check_int(c, lp_realm(), lp_workgroup(), NULL); +} + +int net_ads_check(struct net_context *c) +{ + return net_ads_check_int(c, NULL, c->opt_workgroup, c->opt_host); +} + +/* + determine the netbios workgroup name for a domain + */ +static int net_ads_workgroup(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + struct NETLOGON_SAM_LOGON_RESPONSE_EX reply; + bool ok = false; + int ret = -1; + + if (c->display_usage) { + d_printf ("%s\n" + "net ads workgroup\n" + " %s\n", + _("Usage:"), + _("Print the workgroup name")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup_nobind(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Didn't find the cldap server!\n")); + goto out; + } + + if (!ads->config.realm) { + ads->config.realm = talloc_strdup(ads, c->opt_target_workgroup); + if (ads->config.realm == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + ads->ldap.port = 389; + } + + ok = ads_cldap_netlogon_5(tmp_ctx, + &ads->ldap.ss, ads->server.realm, &reply); + if (!ok) { + d_fprintf(stderr, _("CLDAP query failed!\n")); + goto out; + } + + d_printf(_("Workgroup: %s\n"), reply.domain_name); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +} + + + +static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area) +{ + char **disp_fields = (char **) data_area; + + if (!field) { /* must be end of record */ + if (disp_fields[0]) { + if (!strchr_m(disp_fields[0], '$')) { + if (disp_fields[1]) + d_printf("%-21.21s %s\n", + disp_fields[0], disp_fields[1]); + else + d_printf("%s\n", disp_fields[0]); + } + } + SAFE_FREE(disp_fields[0]); + SAFE_FREE(disp_fields[1]); + return true; + } + if (!values) /* must be new field, indicate string field */ + return true; + if (strcasecmp_m(field, "sAMAccountName") == 0) { + disp_fields[0] = SMB_STRDUP((char *) values[0]); + } + if (strcasecmp_m(field, "description") == 0) + disp_fields[1] = SMB_STRDUP((char *) values[0]); + return true; +} + +static int net_ads_user_usage(struct net_context *c, int argc, const char **argv) +{ + return net_user_usage(c, argc, argv); +} + +static int ads_user_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + char *upn, *userdn; + LDAPMessage *res=NULL; + int rc = -1; + char *ou_str = NULL; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_user_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto done; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_user_add: %s\n"), ads_errstr(status)); + goto done; + } + + if (ads_count_replies(ads, res)) { + d_fprintf(stderr, _("ads_user_add: User %s already exists\n"), + argv[0]); + goto done; + } + + if (c->opt_container) { + ou_str = SMB_STRDUP(c->opt_container); + } else { + ou_str = ads_default_ou_string(ads, DS_GUID_USERS_CONTAINER); + } + + status = ads_add_user_acct(ads, argv[0], ou_str, c->opt_comment); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Could not add user %s: %s\n"), argv[0], + ads_errstr(status)); + goto done; + } + + /* if no password is to be set, we're done */ + if (argc == 1) { + d_printf(_("User %s added\n"), argv[0]); + rc = 0; + goto done; + } + + /* try setting the password */ + upn = talloc_asprintf(tmp_ctx, + "%s@%s", + argv[0], + ads->config.realm); + if (upn == NULL) { + goto done; + } + + status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1], + ads->auth.time_offset); + if (ADS_ERR_OK(status)) { + d_printf(_("User %s added\n"), argv[0]); + rc = 0; + goto done; + } + TALLOC_FREE(upn); + + /* password didn't set, delete account */ + d_fprintf(stderr, _("Could not add user %s. " + "Error setting password %s\n"), + argv[0], ads_errstr(status)); + + ads_msgfree(ads, res); + res = NULL; + + status=ads_find_user_acct(ads, &res, argv[0]); + if (ADS_ERR_OK(status)) { + userdn = ads_get_dn(ads, tmp_ctx, res); + ads_del_dn(ads, userdn); + TALLOC_FREE(userdn); + } + + done: + ads_msgfree(ads, res); + SAFE_FREE(ou_str); + TALLOC_FREE(tmp_ctx); + return rc; +} + +static int ads_user_info(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + wbcErr wbc_status; + const char *attrs[] = {"memberOf", "primaryGroupID", NULL}; + char *searchstring = NULL; + char **grouplist = NULL; + char *primary_group = NULL; + char *escaped_user = NULL; + struct dom_sid primary_group_sid; + uint32_t group_rid; + enum wbcSidType type; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_user_usage(c, argc, argv); + } + + escaped_user = escape_ldap_string(tmp_ctx, argv[0]); + if (!escaped_user) { + d_fprintf(stderr, + _("ads_user_info: failed to escape user %s\n"), + argv[0]); + goto out; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + searchstring = talloc_asprintf(tmp_ctx, + "(sAMAccountName=%s)", + escaped_user); + if (searchstring == NULL) { + goto out; + } + + status = ads_search(ads, &res, searchstring, attrs); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_search: %s\n"), ads_errstr(status)); + goto out; + } + + if (!ads_pull_uint32(ads, res, "primaryGroupID", &group_rid)) { + d_fprintf(stderr, _("ads_pull_uint32 failed\n")); + goto out; + } + + status = ads_domain_sid(ads, &primary_group_sid); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_domain_sid: %s\n"), ads_errstr(status)); + goto out; + } + + sid_append_rid(&primary_group_sid, group_rid); + + wbc_status = wbcLookupSid((struct wbcDomainSid *)&primary_group_sid, + NULL, /* don't look up domain */ + &primary_group, + &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { + d_fprintf(stderr, "wbcLookupSid: %s\n", + wbcErrorString(wbc_status)); + goto out; + } + + d_printf("%s\n", primary_group); + + wbcFreeMemory(primary_group); + + grouplist = ldap_get_values((LDAP *)ads->ldap.ld, + (LDAPMessage *)res, "memberOf"); + + if (grouplist) { + int i; + char **groupname; + for (i=0;grouplist[i];i++) { + groupname = ldap_explode_dn(grouplist[i], 1); + d_printf("%s\n", groupname[0]); + ldap_value_free(groupname); + } + ldap_value_free(grouplist); + } + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int ads_user_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + char *userdn = NULL; + int ret = -1; + + if (argc < 1) { + TALLOC_FREE(tmp_ctx); + return net_ads_user_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status) || ads_count_replies(ads, res) != 1) { + d_printf(_("User %s does not exist.\n"), argv[0]); + goto out; + } + + userdn = ads_get_dn(ads, tmp_ctx, res); + if (userdn == NULL) { + goto out; + } + + status = ads_del_dn(ads, userdn); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Error deleting user %s: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + + d_printf(_("User %s deleted\n"), argv[0]); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_user(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + ads_user_add, + NET_TRANSPORT_ADS, + N_("Add an AD user"), + N_("net ads user add\n" + " Add an AD user") + }, + { + "info", + ads_user_info, + NET_TRANSPORT_ADS, + N_("Display information about an AD user"), + N_("net ads user info\n" + " Display information about an AD user") + }, + { + "delete", + ads_user_delete, + NET_TRANSPORT_ADS, + N_("Delete an AD user"), + N_("net ads user delete\n" + " Delete an AD user") + }, + {NULL, NULL, 0, NULL, NULL} + }; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *shortattrs[] = {"sAMAccountName", NULL}; + const char *longattrs[] = {"sAMAccountName", "description", NULL}; + char *disp_fields[2] = {NULL, NULL}; + int ret = -1; + + if (argc > 0) { + TALLOC_FREE(tmp_ctx); + return net_run_function(c, argc, argv, "net ads user", func); + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads user\n" + " %s\n", + _("Usage:"), + _("List AD users")); + net_display_usage_from_functable(func); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (c->opt_long_list_entries) + d_printf(_("\nUser name Comment" + "\n-----------------------------\n")); + + status = ads_do_search_all_fn(ads, + ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + "(objectCategory=user)", + c->opt_long_list_entries ? + longattrs : shortattrs, + usergrp_display, + disp_fields); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_group_usage(struct net_context *c, int argc, const char **argv) +{ + return net_group_usage(c, argc, argv); +} + +static int ads_group_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + char *ou_str = NULL; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_group_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_group_add: %s\n"), ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res)) { + d_fprintf(stderr, _("ads_group_add: Group %s already exists\n"), argv[0]); + goto out; + } + + if (c->opt_container) { + ou_str = SMB_STRDUP(c->opt_container); + } else { + ou_str = ads_default_ou_string(ads, DS_GUID_USERS_CONTAINER); + } + + status = ads_add_group_acct(ads, argv[0], ou_str, c->opt_comment); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Could not add group %s: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + + d_printf(_("Group %s added\n"), argv[0]); + + ret = 0; + out: + ads_msgfree(ads, res); + SAFE_FREE(ou_str); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int ads_group_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + char *groupdn = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_group_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status) || ads_count_replies(ads, res) != 1) { + d_printf(_("Group %s does not exist.\n"), argv[0]); + goto out; + } + + groupdn = ads_get_dn(ads, tmp_ctx, res); + if (groupdn == NULL) { + goto out; + } + + status = ads_del_dn(ads, groupdn); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Error deleting group %s: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + d_printf(_("Group %s deleted\n"), argv[0]); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_group(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + ads_group_add, + NET_TRANSPORT_ADS, + N_("Add an AD group"), + N_("net ads group add\n" + " Add an AD group") + }, + { + "delete", + ads_group_delete, + NET_TRANSPORT_ADS, + N_("Delete an AD group"), + N_("net ads group delete\n" + " Delete an AD group") + }, + {NULL, NULL, 0, NULL, NULL} + }; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *shortattrs[] = {"sAMAccountName", NULL}; + const char *longattrs[] = {"sAMAccountName", "description", NULL}; + char *disp_fields[2] = {NULL, NULL}; + int ret = -1; + + if (argc >= 0) { + TALLOC_FREE(tmp_ctx); + return net_run_function(c, argc, argv, "net ads group", func); + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads group\n" + " %s\n", + _("Usage:"), + _("List AD groups")); + net_display_usage_from_functable(func); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (c->opt_long_list_entries) + d_printf(_("\nGroup name Comment" + "\n-----------------------------\n")); + + status = ads_do_search_all_fn(ads, + ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + "(objectCategory=group)", + c->opt_long_list_entries ? + longattrs : shortattrs, + usergrp_display, + disp_fields); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_status(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads status\n" + " %s\n", + _("Usage:"), + _("Display machine account details")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + net_warn_member_options(); + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_machine_acct(ads, &res, lp_netbios_name()); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_find_machine_acct: %s\n"), + ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("No machine account for '%s' found\n"), + lp_netbios_name()); + goto out; + } + + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +/******************************************************************* + Leave an AD domain. Windows XP disables the machine account. + We'll try the same. The old code would do an LDAP delete. + That only worked using the machine creds because added the machine + with full control to the computer object's ACL. +*******************************************************************/ + +static int net_ads_leave(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct libnet_UnjoinCtx *r = NULL; + WERROR werr; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads leave [--keep-account]\n" + " %s\n", + _("Usage:"), + _("Leave an AD domain")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!*lp_realm()) { + d_fprintf(stderr, _("No realm set, are we joined ?\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!c->opt_kerberos) { + use_in_memory_ccache(); + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + goto done; + } + + werr = libnet_init_UnjoinCtx(tmp_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("Could not initialise unjoin context.\n")); + goto done; + } + + r->in.debug = true; + r->in.use_kerberos = c->opt_kerberos; + r->in.dc_name = c->opt_host; + r->in.domain_name = lp_realm(); + r->in.admin_account = c->opt_user_name; + r->in.admin_password = net_prompt_pass(c, c->opt_user_name); + r->in.modify_config = lp_config_backend_is_registry(); + + /* Try to delete it, but if that fails, disable it. The + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable */ + r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE; + if (c->opt_keep_account) { + r->in.delete_machine_account = false; + } else { + r->in.delete_machine_account = true; + } + + r->in.msg_ctx = c->msg_ctx; + + werr = libnet_Unjoin(tmp_ctx, r); + if (!W_ERROR_IS_OK(werr)) { + d_printf(_("Failed to leave domain: %s\n"), + r->out.error_string ? r->out.error_string : + get_friendly_werror_msg(werr)); + goto done; + } + + if (r->out.deleted_machine_account) { + d_printf(_("Deleted account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + ret = 0; + goto done; + } + + /* We couldn't delete it - see if the disable succeeded. */ + if (r->out.disabled_machine_account) { + d_printf(_("Disabled account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + ret = 0; + goto done; + } + + /* Based on what we requested, we shouldn't get here, but if + we did, it means the secrets were removed, and therefore + we have left the domain */ + d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + + ret = 0; + done: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static ADS_STATUS net_ads_join_ok(struct net_context *c) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + fstring dc_name; + struct sockaddr_storage dcip; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + TALLOC_FREE(tmp_ctx); + return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED); + } + + net_warn_member_options(); + + net_use_krb_machine_account(c); + + get_dc_name(lp_workgroup(), lp_realm(), dc_name, &dcip); + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ADS_ERROR_NT(NT_STATUS_OK); +out: + TALLOC_FREE(tmp_ctx); + return status; +} + +/* + check that an existing join is OK + */ +int net_ads_testjoin(struct net_context *c, int argc, const char **argv) +{ + ADS_STATUS status; + use_in_memory_ccache(); + + if (c->display_usage) { + d_printf( "%s\n" + "net ads testjoin\n" + " %s\n", + _("Usage:"), + _("Test if the existing join is ok")); + return -1; + } + + net_warn_member_options(); + + /* Display success or failure */ + status = net_ads_join_ok(c); + if (!ADS_ERR_OK(status)) { + fprintf(stderr, _("Join to domain is not valid: %s\n"), + get_friendly_nt_error_msg(ads_ntstatus(status))); + return -1; + } + + printf(_("Join is OK\n")); + return 0; +} + +/******************************************************************* + Simple config checks before beginning the join + ********************************************************************/ + +static WERROR check_ads_config( void ) +{ + if (lp_server_role() != ROLE_DOMAIN_MEMBER ) { + d_printf(_("Host is not configured as a member server.\n")); + return WERR_INVALID_DOMAIN_ROLE; + } + + if (strlen(lp_netbios_name()) > 15) { + d_printf(_("Our netbios name can be at most 15 chars long, " + "\"%s\" is %u chars long\n"), lp_netbios_name(), + (unsigned int)strlen(lp_netbios_name())); + return WERR_INVALID_COMPUTERNAME; + } + + if ( lp_security() == SEC_ADS && !*lp_realm()) { + d_fprintf(stderr, _("realm must be set in in %s for ADS " + "join to succeed.\n"), get_dyn_CONFIGFILE()); + return WERR_INVALID_PARAMETER; + } + + return WERR_OK; +} + +/******************************************************************* + ********************************************************************/ + +static int net_ads_join_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net ads join [--no-dns-updates] [options]\n" + "Valid options:\n")); + d_printf(_(" dnshostname=FQDN Set the dnsHostName attribute during the join.\n" + " The default is in the form netbiosname.dnsdomain\n")); + d_printf(_(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n" + " The default UPN is in the form host/netbiosname@REALM.\n")); + d_printf(_(" createcomputer=OU Precreate the computer account in a specific OU.\n" + " The OU string read from top to bottom without RDNs\n" + " and delimited by a '/'.\n" + " E.g. \"createcomputer=Computers/Servers/Unix\"\n" + " NB: A backslash '\\' is used as escape at multiple\n" + " levels and may need to be doubled or even\n" + " quadrupled. It is not used as a separator.\n")); + d_printf(_(" machinepass=PASS Set the machine password to a specific value during\n" + " the join. The default password is random.\n")); + d_printf(_(" osName=string Set the operatingSystem attribute during the join.\n")); + d_printf(_(" osVer=string Set the operatingSystemVersion attribute during join.\n" + " NB: osName and osVer must be specified together for\n" + " either to take effect. The operatingSystemService\n" + " attribute is then also set along with the two\n" + " other attributes.\n")); + d_printf(_(" osServicePack=string Set the operatingSystemServicePack attribute\n" + " during the join.\n" + " NB: If not specified then by default the samba\n" + " version string is used instead.\n")); + return -1; +} + + +int net_ads_join(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct libnet_JoinCtx *r = NULL; + const char *domain = lp_realm(); + WERROR werr = WERR_NERR_SETUPNOTJOINED; + bool createupn = false; + const char *dnshostname = NULL; + const char *machineupn = NULL; + const char *machine_password = NULL; + const char *create_in_ou = NULL; + int i; + const char *os_name = NULL; + const char *os_version = NULL; + const char *os_servicepack = NULL; + bool modify_config = lp_config_backend_is_registry(); + enum libnetjoin_JoinDomNameType domain_name_type = JoinDomNameTypeDNS; + int ret = -1; + + if (c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_join_usage(c, argc, argv); + } + + net_warn_member_options(); + + if (!modify_config) { + werr = check_ads_config(); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("Invalid configuration. Exiting....\n")); + goto fail; + } + } + + if (!c->opt_kerberos) { + use_in_memory_ccache(); + } + + werr = libnet_init_JoinCtx(tmp_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* process additional command line args */ + + for ( i=0; i<argc; i++ ) { + if ( !strncasecmp_m(argv[i], "dnshostname", strlen("dnshostname")) ) { + dnshostname = get_string_param(argv[i]); + } + else if ( !strncasecmp_m(argv[i], "createupn", strlen("createupn")) ) { + createupn = true; + machineupn = get_string_param(argv[i]); + } + else if ( !strncasecmp_m(argv[i], "createcomputer", strlen("createcomputer")) ) { + if ( (create_in_ou = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid OU path.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "osName", strlen("osName")) ) { + if ( (os_name = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a operating system name.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "osVer", strlen("osVer")) ) { + if ( (os_version = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid operating system version.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "osServicePack", strlen("osServicePack")) ) { + if ( (os_servicepack = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid servicepack identifier.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "machinepass", strlen("machinepass")) ) { + if ( (machine_password = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid password to set as trust account password.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } else { + domain = argv[i]; + if (strchr(domain, '.') == NULL) { + domain_name_type = JoinDomNameTypeUnknown; + } else { + domain_name_type = JoinDomNameTypeDNS; + } + } + } + + if (!*domain) { + d_fprintf(stderr, _("Please supply a valid domain name\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + werr = WERR_ACCESS_DENIED; + goto fail; + } + + /* Do the domain join here */ + + r->in.domain_name = domain; + r->in.domain_name_type = domain_name_type; + r->in.create_upn = createupn; + r->in.upn = machineupn; + r->in.dnshostname = dnshostname; + r->in.account_ou = create_in_ou; + r->in.os_name = os_name; + r->in.os_version = os_version; + r->in.os_servicepack = os_servicepack; + r->in.dc_name = c->opt_host; + r->in.admin_account = c->opt_user_name; + r->in.admin_password = net_prompt_pass(c, c->opt_user_name); + r->in.machine_password = machine_password; + r->in.debug = true; + r->in.use_kerberos = c->opt_kerberos; + r->in.modify_config = modify_config; + r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | + WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; + r->in.msg_ctx = c->msg_ctx; + + werr = libnet_Join(tmp_ctx, r); + if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND) && + strequal(domain, lp_realm())) { + r->in.domain_name = lp_workgroup(); + r->in.domain_name_type = JoinDomNameTypeNBT; + werr = libnet_Join(tmp_ctx, r); + } + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* Check the short name of the domain */ + + if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) { + d_printf(_("The workgroup in %s does not match the short\n" + "domain name obtained from the server.\n" + "Using the name [%s] from the server.\n" + "You should set \"workgroup = %s\" in %s.\n"), + get_dyn_CONFIGFILE(), r->out.netbios_domain_name, + r->out.netbios_domain_name, get_dyn_CONFIGFILE()); + } + + d_printf(_("Using short domain name -- %s\n"), r->out.netbios_domain_name); + + if (r->out.dns_domain_name) { + d_printf(_("Joined '%s' to dns domain '%s'\n"), r->in.machine_name, + r->out.dns_domain_name); + } else { + d_printf(_("Joined '%s' to domain '%s'\n"), r->in.machine_name, + r->out.netbios_domain_name); + } + + /* print out informative error string in case there is one */ + if (r->out.error_string != NULL) { + d_printf("%s\n", r->out.error_string); + } + + /* + * We try doing the dns update (if it was compiled in + * and if it was not disabled on the command line). + * If the dns update fails, we still consider the join + * operation as succeeded if we came this far. + */ + if (!c->opt_no_dns_updates) { + net_ads_join_dns_updates(c, tmp_ctx, r); + } + + ret = 0; + +fail: + if (ret != 0) { + /* issue an overall failure message at the end. */ + d_printf(_("Failed to join domain: %s\n"), + r && r->out.error_string ? r->out.error_string : + get_friendly_werror_msg(werr)); + } + + TALLOC_FREE(tmp_ctx); + + return ret; +} + +/******************************************************************* + ********************************************************************/ + +static int net_ads_dns_register(struct net_context *c, int argc, const char **argv) +{ +#if defined(HAVE_KRB5) + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + NTSTATUS ntstatus; + const char *hostname = NULL; + const char **addrs_list = NULL; + struct sockaddr_storage *addrs = NULL; + int num_addrs = 0; + int count; + int ret = -1; + +#ifdef DEVELOPER + talloc_enable_leak_report(); +#endif + + if (argc <= 1 && lp_clustering() && lp_cluster_addresses() == NULL) { + d_fprintf(stderr, _("Refusing DNS updates with automatic " + "detection of addresses in a clustered " + "setup.\n")); + c->display_usage = true; + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads dns register [hostname [IP [IP...]]]\n" + " %s\n", + _("Usage:"), + _("Register hostname with DNS\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (argc >= 1) { + hostname = argv[0]; + } + + if (argc > 1) { + num_addrs = argc - 1; + addrs_list = &argv[1]; + } else if (lp_clustering()) { + addrs_list = lp_cluster_addresses(); + num_addrs = str_list_length(addrs_list); + } + + if (num_addrs > 0) { + addrs = talloc_zero_array(tmp_ctx, + struct sockaddr_storage, + num_addrs); + if (addrs == NULL) { + d_fprintf(stderr, _("Error allocating memory!\n")); + goto out; + } + } + + for (count = 0; count < num_addrs; count++) { + if (!interpret_string_addr(&addrs[count], addrs_list[count], 0)) { + d_fprintf(stderr, "%s '%s'.\n", + _("Cannot interpret address"), + addrs_list[count]); + goto out; + } + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if ( !ADS_ERR_OK(status) ) { + DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status))); + goto out; + } + + ntstatus = net_update_dns_ext(c, + tmp_ctx, + ads, + hostname, + addrs, + num_addrs, + false); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_fprintf( stderr, _("DNS update failed!\n") ); + goto out; + } + + d_fprintf( stderr, _("Successfully registered hostname with DNS\n") ); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +#else + d_fprintf(stderr, + _("DNS update support not enabled at compile time!\n")); + return -1; +#endif +} + +static int net_ads_dns_unregister(struct net_context *c, + int argc, + const char **argv) +{ +#if defined(HAVE_KRB5) + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + NTSTATUS ntstatus; + const char *hostname = NULL; + int ret = -1; + +#ifdef DEVELOPER + talloc_enable_leak_report(); +#endif + + if (argc != 1) { + c->display_usage = true; + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads dns unregister [hostname]\n" + " %s\n", + _("Usage:"), + _("Remove all IP Address entires for a given\n" + " hostname from the Active Directory server.\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + /* Get the hostname for un-registering */ + hostname = argv[0]; + + status = ads_startup(c, true, tmp_ctx, &ads); + if ( !ADS_ERR_OK(status) ) { + DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status))); + goto out; + } + + ntstatus = net_update_dns_ext(c, + tmp_ctx, + ads, + hostname, + NULL, + 0, + true); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_fprintf( stderr, _("DNS update failed!\n") ); + goto out; + } + + d_fprintf( stderr, _("Successfully un-registered hostname from DNS\n")); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +#else + d_fprintf(stderr, + _("DNS update support not enabled at compile time!\n")); + return -1; +#endif +} + + +static int net_ads_dns_async(struct net_context *c, int argc, const char **argv) +{ + size_t num_names = 0; + char **hostnames = NULL; + size_t i = 0; + struct samba_sockaddr *addrs = NULL; + NTSTATUS status; + + if (argc != 1 || c->display_usage) { + d_printf( "%s\n" + " %s\n" + " %s\n", + _("Usage:"), + _("net ads dns async <name>\n"), + _(" Async look up hostname from the DNS server\n" + " hostname\tName to look up\n")); + return -1; + } + + status = ads_dns_lookup_a(talloc_tos(), + argv[0], + &num_names, + &hostnames, + &addrs); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Looking up A record for %s got error %s\n", + argv[0], + nt_errstr(status)); + return -1; + } + d_printf("Async A record lookup - got %u names for %s\n", + (unsigned int)num_names, + argv[0]); + for (i = 0; i < num_names; i++) { + char addr_buf[INET6_ADDRSTRLEN]; + print_sockaddr(addr_buf, + sizeof(addr_buf), + &addrs[i].u.ss); + d_printf("hostname[%u] = %s, IPv4addr = %s\n", + (unsigned int)i, + hostnames[i], + addr_buf); + } + +#if defined(HAVE_IPV6) + status = ads_dns_lookup_aaaa(talloc_tos(), + argv[0], + &num_names, + &hostnames, + &addrs); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Looking up AAAA record for %s got error %s\n", + argv[0], + nt_errstr(status)); + return -1; + } + d_printf("Async AAAA record lookup - got %u names for %s\n", + (unsigned int)num_names, + argv[0]); + for (i = 0; i < num_names; i++) { + char addr_buf[INET6_ADDRSTRLEN]; + print_sockaddr(addr_buf, + sizeof(addr_buf), + &addrs[i].u.ss); + d_printf("hostname[%u] = %s, IPv6addr = %s\n", + (unsigned int)i, + hostnames[i], + addr_buf); + } +#endif + return 0; +} + + +static int net_ads_dns(struct net_context *c, int argc, const char *argv[]) +{ + struct functable func[] = { + { + "register", + net_ads_dns_register, + NET_TRANSPORT_ADS, + N_("Add host dns entry to AD"), + N_("net ads dns register\n" + " Add host dns entry to AD") + }, + { + "unregister", + net_ads_dns_unregister, + NET_TRANSPORT_ADS, + N_("Remove host dns entry from AD"), + N_("net ads dns unregister\n" + " Remove host dns entry from AD") + }, + { + "async", + net_ads_dns_async, + NET_TRANSPORT_ADS, + N_("Look up host"), + N_("net ads dns async\n" + " Look up host using async DNS") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads dns", func); +} + +/******************************************************************* + ********************************************************************/ + +int net_ads_printer_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( +"\nnet ads printer search <printer>" +"\n\tsearch for a printer in the directory\n" +"\nnet ads printer info <printer> <server>" +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish <printername>" +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove <printername>" +"\n\tremove printer from directory" +"\n\t(note: printer name is required)\n")); + return -1; +} + +/******************************************************************* + ********************************************************************/ + +static int net_ads_printer_search(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads printer search\n" + " %s\n", + _("Usage:"), + _("List printers in the AD")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_printers(ads, &res); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_find_printer: %s\n"), + ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("No results found\n")); + goto out; + } + + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_printer_info(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *servername = NULL; + const char *printername = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads printer info [printername [servername]]\n" + " Display printer info from AD\n" + " printername\tPrinter name or wildcard\n" + " servername\tName of the print server\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 0) { + printername = argv[0]; + } else { + printername = "*"; + } + + if (argc > 1) { + servername = argv[1]; + } else { + servername = lp_netbios_name(); + } + + status = ads_find_printer_on_server(ads, &res, printername, servername); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Server '%s' not found: %s\n"), + servername, ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("Printer '%s' not found\n"), printername); + goto out; + } + + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_printer_publish(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *servername = NULL; + const char *printername = NULL; + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + struct sockaddr_storage server_ss = { 0 }; + NTSTATUS nt_status; + ADS_MODLIST mods = NULL; + char *prt_dn = NULL; + char *srv_dn = NULL; + char **srv_cn = NULL; + char *srv_cn_escaped = NULL; + char *printername_escaped = NULL; + LDAPMessage *res = NULL; + bool ok; + int ret = -1; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads printer publish <printername> [servername]\n" + " Publish printer in AD\n" + " printername\tName of the printer\n" + " servername\tName of the print server\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + mods = ads_init_mods(tmp_ctx); + if (mods == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + printername = argv[0]; + + if (argc == 2) { + servername = argv[1]; + } else { + servername = lp_netbios_name(); + } + + /* Get printer data from SPOOLSS */ + + ok = resolve_name(servername, &server_ss, 0x20, false); + if (!ok) { + d_fprintf(stderr, _("Could not find server %s\n"), + servername); + goto out; + } + + cli_credentials_set_kerberos_state(c->creds, + CRED_USE_KERBEROS_REQUIRED, + CRED_SPECIFIED); + + nt_status = cli_full_connection_creds(&cli, lp_netbios_name(), servername, + &server_ss, 0, + "IPC$", "IPC", + c->creds, + CLI_FULL_CONNECTION_IPC); + + if (NT_STATUS_IS_ERR(nt_status)) { + d_fprintf(stderr, _("Unable to open a connection to %s to " + "obtain data for %s\n"), + servername, printername); + goto out; + } + + /* Publish on AD server */ + + ads_find_machine_acct(ads, &res, servername); + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("Could not find machine account for server " + "%s\n"), + servername); + goto out; + } + + srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res); + srv_cn = ldap_explode_dn(srv_dn, 1); + + srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]); + printername_escaped = escape_rdn_val_string_alloc(printername); + if (!srv_cn_escaped || !printername_escaped) { + SAFE_FREE(srv_cn_escaped); + SAFE_FREE(printername_escaped); + d_fprintf(stderr, _("Internal error, out of memory!")); + goto out; + } + + prt_dn = talloc_asprintf(tmp_ctx, + "cn=%s-%s,%s", + srv_cn_escaped, + printername_escaped, + srv_dn); + if (prt_dn == NULL) { + SAFE_FREE(srv_cn_escaped); + SAFE_FREE(printername_escaped); + d_fprintf(stderr, _("Internal error, out of memory!")); + goto out; + } + + SAFE_FREE(srv_cn_escaped); + SAFE_FREE(printername_escaped); + + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss, &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, _("Unable to open a connection to the spoolss pipe on %s\n"), + servername); + goto out; + } + + if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, + tmp_ctx, + &mods, + printername))) { + goto out; + } + + status = ads_add_printer_entry(ads, prt_dn, tmp_ctx, &mods); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, "ads_publish_printer: %s\n", + ads_errstr(status)); + goto out; + } + + d_printf("published printer\n"); + + ret = 0; +out: + talloc_destroy(tmp_ctx); + + return ret; +} + +static int net_ads_printer_remove(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *servername = NULL; + char *prt_dn = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads printer remove <printername> [servername]\n" + " Remove a printer from the AD\n" + " printername\tName of the printer\n" + " servername\tName of the print server\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 1) { + servername = argv[1]; + } else { + servername = lp_netbios_name(); + } + + status = ads_find_printer_on_server(ads, &res, argv[0], servername); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_find_printer_on_server: %s\n"), + ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("Printer '%s' not found\n"), argv[1]); + goto out; + } + + prt_dn = ads_get_dn(ads, tmp_ctx, res); + if (prt_dn == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + status = ads_del_dn(ads, prt_dn); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_del_dn: %s\n"), ads_errstr(status)); + goto out; + } + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_printer(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "search", + net_ads_printer_search, + NET_TRANSPORT_ADS, + N_("Search for a printer"), + N_("net ads printer search\n" + " Search for a printer") + }, + { + "info", + net_ads_printer_info, + NET_TRANSPORT_ADS, + N_("Display printer information"), + N_("net ads printer info\n" + " Display printer information") + }, + { + "publish", + net_ads_printer_publish, + NET_TRANSPORT_ADS, + N_("Publish a printer"), + N_("net ads printer publish\n" + " Publish a printer") + }, + { + "remove", + net_ads_printer_remove, + NET_TRANSPORT_ADS, + N_("Delete a printer"), + N_("net ads printer remove\n" + " Delete a printer") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads printer", func); +} + + +static int net_ads_password(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + const char *auth_principal = cli_credentials_get_username(c->creds); + const char *auth_password = cli_credentials_get_password(c->creds); + const char *realm = NULL; + char *new_password = NULL; + char *chr = NULL; + char *prompt = NULL; + const char *user = NULL; + char pwd[256] = {0}; + ADS_STATUS status; + int ret = 0; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads password <username>\n" + " Change password for user\n" + " username\tName of user to change password for\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (auth_principal == NULL || auth_password == NULL) { + d_fprintf(stderr, _("You must supply an administrator " + "username/password\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (argc < 1) { + d_fprintf(stderr, _("ERROR: You must say which username to " + "change password for\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (strchr_m(argv[0], '@')) { + user = talloc_strdup(tmp_ctx, argv[0]); + } else { + user = talloc_asprintf(tmp_ctx, "%s@%s", argv[0], lp_realm()); + } + if (user == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + use_in_memory_ccache(); + chr = strchr_m(auth_principal, '@'); + if (chr) { + realm = ++chr; + } else { + realm = lp_realm(); + } + + /* use the realm so we can eventually change passwords for users + in realms other than default */ + ads = ads_init(tmp_ctx, + realm, + c->opt_workgroup, + c->opt_host, + ADS_SASL_PLAIN); + if (ads == NULL) { + goto out; + } + + /* we don't actually need a full connect, but it's the easy way to + fill in the KDC's addresss */ + ads_connect(ads); + + if (!ads->config.realm) { + d_fprintf(stderr, _("Didn't find the kerberos server!\n")); + goto out; + } + + if (argv[1] != NULL) { + new_password = talloc_strdup(tmp_ctx, argv[1]); + } else { + int rc; + + prompt = talloc_asprintf(tmp_ctx, _("Enter new password for %s:"), user); + if (prompt == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + rc = samba_getpass(prompt, pwd, sizeof(pwd), false, true); + if (rc < 0) { + goto out; + } + new_password = talloc_strdup(tmp_ctx, pwd); + memset(pwd, '\0', sizeof(pwd)); + } + + if (new_password == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + status = kerberos_set_password(ads->auth.kdc_server, + auth_principal, + auth_password, + user, + new_password, + ads->auth.time_offset); + memset(new_password, '\0', strlen(new_password)); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Password change failed: %s\n"), + ads_errstr(status)); + goto out; + } + + d_printf(_("Password change for %s completed.\n"), user); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + char *host_principal = NULL; + char *my_name = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads changetrustpw\n" + " %s\n", + _("Usage:"), + _("Change the machine account's trust password")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + goto out; + } + + net_warn_member_options(); + + net_use_krb_machine_account(c); + + use_in_memory_ccache(); + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + my_name = talloc_asprintf_strlower_m(tmp_ctx, "%s", lp_netbios_name()); + if (my_name == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + host_principal = talloc_asprintf(tmp_ctx, "%s$@%s", my_name, ads->config.realm); + if (host_principal == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + d_printf(_("Changing password for principal: %s\n"), host_principal); + + status = ads_change_trust_account_password(ads, host_principal); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf(_("Password change for principal %s succeeded.\n"), host_principal); + + if (USE_SYSTEM_KEYTAB) { + d_printf(_("Attempting to update system keytab with new password.\n")); + if (ads_keytab_create_default(ads)) { + d_printf(_("Failed to update system keytab.\n")); + } + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +} + +/* + help for net ads search +*/ +static int net_ads_search_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet ads search <expression> <attributes...>\n" + "\nPerform a raw LDAP search on a ADS server and dump the results.\n" + "The expression is a standard LDAP search expression, and the\n" + "attributes are a list of LDAP fields to show in the results.\n\n" + "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n" + )); + net_common_flags_usage(c, argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_search(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *ldap_exp = NULL; + const char **attrs = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_search_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ldap_exp = argv[0]; + attrs = (argv + 1); + + status = ads_do_search_retry(ads, + ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + ldap_exp, + attrs, + &res); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res)); + + /* dump the results */ + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + + +/* + help for net ads search +*/ +static int net_ads_dn_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet ads dn <dn> <attributes...>\n" + "\nperform a raw LDAP search on a ADS server and dump the results\n" + "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n" + "to show in the results\n\n" + "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n" + "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n" + )); + net_common_flags_usage(c, argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_dn(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *dn = NULL; + const char **attrs = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_dn_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + dn = argv[0]; + attrs = (argv + 1); + + status = ads_do_search_all(ads, + dn, + LDAP_SCOPE_BASE, + "(objectclass=*)", + attrs, + &res); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf("Got %d replies\n\n", ads_count_replies(ads, res)); + + /* dump the results */ + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +/* + help for net ads sid search +*/ +static int net_ads_sid_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet ads sid <sid> <attributes...>\n" + "\nperform a raw LDAP search on a ADS server and dump the results\n" + "The SID is in string format, and the attributes are a list of LDAP fields \n" + "to show in the results\n\n" + "Example: net ads sid 'S-1-5-32' distinguishedName\n\n" + )); + net_common_flags_usage(c, argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_sid(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *sid_string = NULL; + const char **attrs = NULL; + LDAPMessage *res = NULL; + struct dom_sid sid = { 0 }; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_sid_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + sid_string = argv[0]; + attrs = (argv + 1); + + if (!string_to_sid(&sid, sid_string)) { + d_fprintf(stderr, _("could not convert sid\n")); + goto out; + } + + status = ads_search_retry_sid(ads, &res, &sid, attrs); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res)); + + /* dump the results */ + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_flush(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads keytab flush\n" + " %s\n", + _("Usage:"), + _("Delete the whole keytab")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = ads_keytab_flush(ads); +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_add(struct net_context *c, + int argc, + const char **argv, + bool update_ads) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int i; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads keytab add <principal> [principal ...]\n" + " Add principals to local keytab\n" + " principal\tKerberos principal to add to " + "keytab\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + net_warn_member_options(); + + d_printf(_("Processing principals to add...\n")); + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + for (ret = 0, i = 0; i < argc; i++) { + ret |= ads_keytab_add_entry(ads, argv[i], update_ads); + } +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_add_default(struct net_context *c, + int argc, + const char **argv) +{ + return net_ads_keytab_add(c, argc, argv, false); +} + +static int net_ads_keytab_add_update_ads(struct net_context *c, + int argc, + const char **argv) +{ + return net_ads_keytab_add(c, argc, argv, true); +} + +static int net_ads_keytab_create(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads keytab create\n" + " %s\n", + _("Usage:"), + _("Create new default keytab")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + net_warn_member_options(); + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = ads_keytab_create_default(ads); +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_list(struct net_context *c, int argc, const char **argv) +{ + const char *keytab = NULL; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads keytab list [keytab]\n" + " List a local keytab\n" + " keytab\tKeytab to list\n")); + return -1; + } + + if (argc >= 1) { + keytab = argv[0]; + } + + return ads_keytab_list(keytab); +} + + +int net_ads_keytab(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + net_ads_keytab_add_default, + NET_TRANSPORT_ADS, + N_("Add a service principal"), + N_("net ads keytab add\n" + " Add a service principal, updates keytab file only.") + }, + { + "add_update_ads", + net_ads_keytab_add_update_ads, + NET_TRANSPORT_ADS, + N_("Add a service principal"), + N_("net ads keytab add_update_ads\n" + " Add a service principal, depending on the param passed may update ADS computer object in addition to the keytab file.") + }, + { + "create", + net_ads_keytab_create, + NET_TRANSPORT_ADS, + N_("Create a fresh keytab"), + N_("net ads keytab create\n" + " Create a fresh keytab or update existing one.") + }, + { + "flush", + net_ads_keytab_flush, + NET_TRANSPORT_ADS, + N_("Remove all keytab entries"), + N_("net ads keytab flush\n" + " Remove all keytab entries") + }, + { + "list", + net_ads_keytab_list, + NET_TRANSPORT_ADS, + N_("List a keytab"), + N_("net ads keytab list\n" + " List a keytab") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (!USE_KERBEROS_KEYTAB) { + d_printf(_("\nWarning: \"kerberos method\" must be set to a " + "keytab method to use keytab functions.\n")); + } + + return net_run_function(c, argc, argv, "net ads keytab", func); +} + +static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos renew\n" + " %s\n", + _("Usage:"), + _("Renew TGT from existing credential cache")); + return -1; + } + + ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL); + if (ret) { + d_printf(_("failed to renew kerberos ticket: %s\n"), + error_message(ret)); + } + return ret; +} + +static int net_ads_kerberos_pac_common(struct net_context *c, int argc, const char **argv, + struct PAC_DATA_CTR **pac_data_ctr) +{ + NTSTATUS status; + int ret = -1; + const char *impersonate_princ_s = NULL; + const char *local_service = NULL; + int i; + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "impersonate", strlen("impersonate"))) { + impersonate_princ_s = get_string_param(argv[i]); + if (impersonate_princ_s == NULL) { + return -1; + } + } + if (strnequal(argv[i], "local_service", strlen("local_service"))) { + local_service = get_string_param(argv[i]); + if (local_service == NULL) { + return -1; + } + } + } + + if (local_service == NULL) { + local_service = talloc_asprintf(c, "%s$@%s", + lp_netbios_name(), lp_realm()); + if (local_service == NULL) { + goto out; + } + } + + c->opt_password = net_prompt_pass(c, c->opt_user_name); + + status = kerberos_return_pac(c, + c->opt_user_name, + c->opt_password, + 0, + NULL, + NULL, + NULL, + true, + true, + 2592000, /* one month */ + impersonate_princ_s, + local_service, + NULL, + NULL, + pac_data_ctr); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("failed to query kerberos PAC: %s\n"), + nt_errstr(status)); + goto out; + } + + ret = 0; + out: + return ret; +} + +static int net_ads_kerberos_pac_dump(struct net_context *c, int argc, const char **argv) +{ + struct PAC_DATA_CTR *pac_data_ctr = NULL; + int i, num_buffers; + int ret = -1; + enum PAC_TYPE type = 0; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos pac dump [impersonate=string] [local_service=string] [pac_buffer_type=int]\n" + " %s\n", + _("Usage:"), + _("Dump the Kerberos PAC")); + return -1; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "pac_buffer_type", strlen("pac_buffer_type"))) { + type = get_int_param(argv[i]); + } + } + + ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr); + if (ret) { + return ret; + } + + if (type == 0) { + + char *s = NULL; + + s = NDR_PRINT_STRUCT_STRING(c, PAC_DATA, + pac_data_ctr->pac_data); + if (s != NULL) { + d_printf(_("The Pac: %s\n"), s); + talloc_free(s); + } + + return 0; + } + + num_buffers = pac_data_ctr->pac_data->num_buffers; + + for (i=0; i<num_buffers; i++) { + + char *s = NULL; + + if (pac_data_ctr->pac_data->buffers[i].type != type) { + continue; + } + + s = NDR_PRINT_UNION_STRING(c, PAC_INFO, type, + pac_data_ctr->pac_data->buffers[i].info); + if (s != NULL) { + d_printf(_("The Pac: %s\n"), s); + talloc_free(s); + } + break; + } + + return 0; +} + +static int net_ads_kerberos_pac_save(struct net_context *c, int argc, const char **argv) +{ + struct PAC_DATA_CTR *pac_data_ctr = NULL; + char *filename = NULL; + int ret = -1; + int i; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos pac save [impersonate=string] [local_service=string] [filename=string]\n" + " %s\n", + _("Usage:"), + _("Save the Kerberos PAC")); + return -1; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "filename", strlen("filename"))) { + filename = get_string_param(argv[i]); + if (filename == NULL) { + return -1; + } + } + } + + ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr); + if (ret) { + return ret; + } + + if (filename == NULL) { + d_printf(_("please define \"filename=<filename>\" to save the PAC\n")); + return -1; + } + + /* save the raw format */ + if (!file_save(filename, pac_data_ctr->pac_blob.data, pac_data_ctr->pac_blob.length)) { + d_printf(_("failed to save PAC in %s\n"), filename); + return -1; + } + + return 0; +} + +static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "dump", + net_ads_kerberos_pac_dump, + NET_TRANSPORT_ADS, + N_("Dump Kerberos PAC"), + N_("net ads kerberos pac dump\n" + " Dump a Kerberos PAC to stdout") + }, + { + "save", + net_ads_kerberos_pac_save, + NET_TRANSPORT_ADS, + N_("Save Kerberos PAC"), + N_("net ads kerberos pac save\n" + " Save a Kerberos PAC in a file") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads kerberos pac", func); +} + +static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + NTSTATUS status; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos kinit\n" + " %s\n", + _("Usage:"), + _("Get Ticket Granting Ticket (TGT) for the user")); + return -1; + } + + c->opt_password = net_prompt_pass(c, c->opt_user_name); + + ret = kerberos_kinit_password_ext(c->opt_user_name, + c->opt_password, + 0, + NULL, + NULL, + NULL, + true, + true, + 2592000, /* one month */ + NULL, + NULL, + NULL, + &status); + if (ret) { + d_printf(_("failed to kinit password: %s\n"), + nt_errstr(status)); + } + return ret; +} + +int net_ads_kerberos(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "kinit", + net_ads_kerberos_kinit, + NET_TRANSPORT_ADS, + N_("Retrieve Ticket Granting Ticket (TGT)"), + N_("net ads kerberos kinit\n" + " Receive Ticket Granting Ticket (TGT)") + }, + { + "renew", + net_ads_kerberos_renew, + NET_TRANSPORT_ADS, + N_("Renew Ticket Granting Ticket from credential cache"), + N_("net ads kerberos renew\n" + " Renew Ticket Granting Ticket (TGT) from " + "credential cache") + }, + { + "pac", + net_ads_kerberos_pac, + NET_TRANSPORT_ADS, + N_("Dump Kerberos PAC"), + N_("net ads kerberos pac\n" + " Dump Kerberos PAC") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads kerberos", func); +} + +static int net_ads_setspn_list(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool ok = false; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads setspn list <machinename>\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc) { + ok = ads_setspn_list(ads, argv[0]); + } else { + ok = ads_setspn_list(ads, lp_netbios_name()); + } + + ret = ok ? 0 : -1; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_setspn_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool ok = false; + int ret = -1; + + if (c->display_usage || argc < 1) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads setspn add <machinename> SPN\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 1) { + ok = ads_setspn_add(ads, argv[0], argv[1]); + } else { + ok = ads_setspn_add(ads, lp_netbios_name(), argv[0]); + } + + ret = ok ? 0 : -1; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_setspn_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool ok = false; + int ret = -1; + + if (c->display_usage || argc < 1) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads setspn delete <machinename> SPN\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 1) { + ok = ads_setspn_delete(ads, argv[0], argv[1]); + } else { + ok = ads_setspn_delete(ads, lp_netbios_name(), argv[0]); + } + + ret = ok ? 0 : -1; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_setspn(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_ads_setspn_list, + NET_TRANSPORT_ADS, + N_("List Service Principal Names (SPN)"), + N_("net ads setspn list machine\n" + " List Service Principal Names (SPN)") + }, + { + "add", + net_ads_setspn_add, + NET_TRANSPORT_ADS, + N_("Add Service Principal Names (SPN)"), + N_("net ads setspn add machine spn\n" + " Add Service Principal Names (SPN)") + }, + { + "delete", + net_ads_setspn_delete, + NET_TRANSPORT_ADS, + N_("Delete Service Principal Names (SPN)"), + N_("net ads setspn delete machine spn\n" + " Delete Service Principal Names (SPN)") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads setspn", func); +} + +static int net_ads_enctype_lookup_account(struct net_context *c, + ADS_STRUCT *ads, + const char *account, + LDAPMessage **res, + const char **enctype_str) +{ + const char *filter; + const char *attrs[] = { + "msDS-SupportedEncryptionTypes", + NULL + }; + int count; + int ret = -1; + ADS_STATUS status; + + filter = talloc_asprintf(c, "(&(objectclass=user)(sAMAccountName=%s))", + account); + if (filter == NULL) { + goto done; + } + + status = ads_search(ads, res, filter, attrs); + if (!ADS_ERR_OK(status)) { + d_printf(_("no account found with filter: %s\n"), filter); + goto done; + } + + count = ads_count_replies(ads, *res); + switch (count) { + case 1: + break; + case 0: + d_printf(_("no account found with filter: %s\n"), filter); + goto done; + default: + d_printf(_("multiple accounts found with filter: %s\n"), filter); + goto done; + } + + if (enctype_str) { + *enctype_str = ads_pull_string(ads, c, *res, + "msDS-SupportedEncryptionTypes"); + if (*enctype_str == NULL) { + d_printf(_("no msDS-SupportedEncryptionTypes attribute found\n")); + goto done; + } + } + + ret = 0; + done: + return ret; +} + +static void net_ads_enctype_dump_enctypes(const char *username, + const char *enctype_str) +{ + int enctypes = atoi(enctype_str); + + d_printf(_("'%s' uses \"msDS-SupportedEncryptionTypes\": %d (0x%08x)\n"), + username, enctypes, enctypes); + + printf("[%s] 0x%08x DES-CBC-CRC\n", + enctypes & ENC_CRC32 ? "X" : " ", + ENC_CRC32); + printf("[%s] 0x%08x DES-CBC-MD5\n", + enctypes & ENC_RSA_MD5 ? "X" : " ", + ENC_RSA_MD5); + printf("[%s] 0x%08x RC4-HMAC\n", + enctypes & ENC_RC4_HMAC_MD5 ? "X" : " ", + ENC_RC4_HMAC_MD5); + printf("[%s] 0x%08x AES128-CTS-HMAC-SHA1-96\n", + enctypes & ENC_HMAC_SHA1_96_AES128 ? "X" : " ", + ENC_HMAC_SHA1_96_AES128); + printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96\n", + enctypes & ENC_HMAC_SHA1_96_AES256 ? "X" : " ", + ENC_HMAC_SHA1_96_AES256); + printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96-SK\n", + enctypes & ENC_HMAC_SHA1_96_AES256_SK ? "X" : " ", + ENC_HMAC_SHA1_96_AES256_SK); + printf("[%s] 0x%08x RESOURCE-SID-COMPRESSION-DISABLED\n", + enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED ? "X" : " ", + KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED); +} + +static int net_ads_enctypes_list(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STATUS status; + ADS_STRUCT *ads = NULL; + LDAPMessage *res = NULL; + const char *str = NULL; + int ret = -1; + + if (c->display_usage || (argc < 1)) { + d_printf( "%s\n" + "net ads enctypes list\n" + " %s\n", + _("Usage:"), + _("List supported enctypes")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str); + if (ret) { + goto out; + } + + net_ads_enctype_dump_enctypes(argv[0], str); + + ret = 0; + out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_enctypes_set(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + int ret = -1; + ADS_STATUS status; + ADS_STRUCT *ads = NULL; + LDAPMessage *res = NULL; + const char *etype_list_str = NULL; + const char *dn = NULL; + ADS_MODLIST mods = NULL; + uint32_t etype_list; + const char *str = NULL; + + if (c->display_usage || argc < 1) { + d_printf( "%s\n" + "net ads enctypes set <sAMAccountName> [enctypes]\n" + " %s\n", + _("Usage:"), + _("Set supported enctypes")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto done; + } + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL); + if (ret) { + goto done; + } + + dn = ads_get_dn(ads, tmp_ctx, res); + if (dn == NULL) { + goto done; + } + + etype_list = 0; + etype_list |= ENC_RC4_HMAC_MD5; + etype_list |= ENC_HMAC_SHA1_96_AES128; + etype_list |= ENC_HMAC_SHA1_96_AES256; + + if (argv[1] != NULL) { + sscanf(argv[1], "%i", &etype_list); + } + + etype_list_str = talloc_asprintf(tmp_ctx, "%d", etype_list); + if (!etype_list_str) { + goto done; + } + + mods = ads_init_mods(tmp_ctx); + if (!mods) { + goto done; + } + + status = ads_mod_str(tmp_ctx, &mods, "msDS-SupportedEncryptionTypes", + etype_list_str); + if (!ADS_ERR_OK(status)) { + goto done; + } + + status = ads_gen_mod(ads, dn, mods); + if (!ADS_ERR_OK(status)) { + d_printf(_("failed to add msDS-SupportedEncryptionTypes: %s\n"), + ads_errstr(status)); + goto done; + } + + ads_msgfree(ads, res); + res = NULL; + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str); + if (ret) { + goto done; + } + + net_ads_enctype_dump_enctypes(argv[0], str); + + ret = 0; + done: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_enctypes_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + int ret = -1; + ADS_STATUS status; + ADS_STRUCT *ads = NULL; + LDAPMessage *res = NULL; + const char *dn = NULL; + ADS_MODLIST mods = NULL; + + if (c->display_usage || argc < 1) { + d_printf( "%s\n" + "net ads enctypes delete <sAMAccountName>\n" + " %s\n", + _("Usage:"), + _("Delete supported enctypes")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto done; + } + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL); + if (ret) { + goto done; + } + + dn = ads_get_dn(ads, tmp_ctx, res); + if (dn == NULL) { + goto done; + } + + mods = ads_init_mods(tmp_ctx); + if (!mods) { + goto done; + } + + status = ads_mod_str(tmp_ctx, &mods, "msDS-SupportedEncryptionTypes", NULL); + if (!ADS_ERR_OK(status)) { + goto done; + } + + status = ads_gen_mod(ads, dn, mods); + if (!ADS_ERR_OK(status)) { + d_printf(_("failed to remove msDS-SupportedEncryptionTypes: %s\n"), + ads_errstr(status)); + goto done; + } + + ret = 0; + + done: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_enctypes(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_ads_enctypes_list, + NET_TRANSPORT_ADS, + N_("List the supported encryption types"), + N_("net ads enctypes list\n" + " List the supported encryption types") + }, + { + "set", + net_ads_enctypes_set, + NET_TRANSPORT_ADS, + N_("Set the supported encryption types"), + N_("net ads enctypes set\n" + " Set the supported encryption types") + }, + { + "delete", + net_ads_enctypes_delete, + NET_TRANSPORT_ADS, + N_("Delete the supported encryption types"), + N_("net ads enctypes delete\n" + " Delete the supported encryption types") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads enctypes", func); +} + + +int net_ads(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "info", + net_ads_info, + NET_TRANSPORT_ADS, + N_("Display details on remote ADS server"), + N_("net ads info\n" + " Display details on remote ADS server") + }, + { + "join", + net_ads_join, + NET_TRANSPORT_ADS, + N_("Join the local machine to ADS realm"), + N_("net ads join\n" + " Join the local machine to ADS realm") + }, + { + "testjoin", + net_ads_testjoin, + NET_TRANSPORT_ADS, + N_("Validate machine account"), + N_("net ads testjoin\n" + " Validate machine account") + }, + { + "leave", + net_ads_leave, + NET_TRANSPORT_ADS, + N_("Remove the local machine from ADS"), + N_("net ads leave\n" + " Remove the local machine from ADS") + }, + { + "status", + net_ads_status, + NET_TRANSPORT_ADS, + N_("Display machine account details"), + N_("net ads status\n" + " Display machine account details") + }, + { + "user", + net_ads_user, + NET_TRANSPORT_ADS, + N_("List/modify users"), + N_("net ads user\n" + " List/modify users") + }, + { + "group", + net_ads_group, + NET_TRANSPORT_ADS, + N_("List/modify groups"), + N_("net ads group\n" + " List/modify groups") + }, + { + "dns", + net_ads_dns, + NET_TRANSPORT_ADS, + N_("Issue dynamic DNS update"), + N_("net ads dns\n" + " Issue dynamic DNS update") + }, + { + "password", + net_ads_password, + NET_TRANSPORT_ADS, + N_("Change user passwords"), + N_("net ads password\n" + " Change user passwords") + }, + { + "changetrustpw", + net_ads_changetrustpw, + NET_TRANSPORT_ADS, + N_("Change trust account password"), + N_("net ads changetrustpw\n" + " Change trust account password") + }, + { + "printer", + net_ads_printer, + NET_TRANSPORT_ADS, + N_("List/modify printer entries"), + N_("net ads printer\n" + " List/modify printer entries") + }, + { + "search", + net_ads_search, + NET_TRANSPORT_ADS, + N_("Issue LDAP search using filter"), + N_("net ads search\n" + " Issue LDAP search using filter") + }, + { + "dn", + net_ads_dn, + NET_TRANSPORT_ADS, + N_("Issue LDAP search by DN"), + N_("net ads dn\n" + " Issue LDAP search by DN") + }, + { + "sid", + net_ads_sid, + NET_TRANSPORT_ADS, + N_("Issue LDAP search by SID"), + N_("net ads sid\n" + " Issue LDAP search by SID") + }, + { + "workgroup", + net_ads_workgroup, + NET_TRANSPORT_ADS, + N_("Display workgroup name"), + N_("net ads workgroup\n" + " Display the workgroup name") + }, + { + "lookup", + net_ads_lookup, + NET_TRANSPORT_ADS, + N_("Perform CLDAP query on DC"), + N_("net ads lookup\n" + " Find the ADS DC using CLDAP lookups") + }, + { + "keytab", + net_ads_keytab, + NET_TRANSPORT_ADS, + N_("Manage local keytab file"), + N_("net ads keytab\n" + " Manage local keytab file") + }, + { + "setspn", + net_ads_setspn, + NET_TRANSPORT_ADS, + N_("Manage Service Principal Names (SPN)s"), + N_("net ads spnset\n" + " Manage Service Principal Names (SPN)s") + }, + { + "gpo", + net_ads_gpo, + NET_TRANSPORT_ADS, + N_("Manage group policy objects"), + N_("net ads gpo\n" + " Manage group policy objects") + }, + { + "kerberos", + net_ads_kerberos, + NET_TRANSPORT_ADS, + N_("Manage kerberos keytab"), + N_("net ads kerberos\n" + " Manage kerberos keytab") + }, + { + "enctypes", + net_ads_enctypes, + NET_TRANSPORT_ADS, + N_("List/modify supported encryption types"), + N_("net ads enctypes\n" + " List/modify enctypes") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads", func); +} + +#else + +static int net_ads_noads(void) +{ + d_fprintf(stderr, _("ADS support not compiled in\n")); + return -1; +} + +int net_ads_keytab(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_kerberos(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_setspn(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_join(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_user(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_group(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_gpo(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +/* this one shouldn't display a message */ +int net_ads_check(struct net_context *c) +{ + return -1; +} + +int net_ads_check_our_domain(struct net_context *c) +{ + return -1; +} + +int net_ads(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +#endif /* HAVE_ADS */ |