mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
85fdd88e3c
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14979
Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit 00b1f44a7e
)
3870 lines
90 KiB
C
3870 lines
90 KiB
C
/*
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
int ret;
|
|
|
|
if (c->display_usage) {
|
|
d_printf("%s\n"
|
|
"net ads lookup\n"
|
|
" %s",
|
|
_("Usage:"),
|
|
_("Find the ADS DC using CLDAP lookup.\n"));
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
|
|
d_fprintf(stderr, _("Didn't find the cldap server!\n"));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (!ads->config.realm) {
|
|
ads->config.realm = discard_const_p(char, c->opt_target_workgroup);
|
|
ads->ldap.port = 389;
|
|
}
|
|
|
|
ret = net_ads_cldap_netlogon(c, ads);
|
|
ads_destroy(&ads);
|
|
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);
|
|
ads_destroy(&ads);
|
|
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
char addr[INET6_ADDRSTRLEN];
|
|
time_t pass_time;
|
|
|
|
if (c->display_usage) {
|
|
d_printf("%s\n"
|
|
"net ads info\n"
|
|
" %s",
|
|
_("Usage:"),
|
|
_("Display information about an Active Directory "
|
|
"server.\n"));
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
|
|
d_fprintf(stderr, _("Didn't find the ldap server!\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (!ads || !ads->config.realm) {
|
|
d_fprintf(stderr, _("Didn't find the ldap server!\n"));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
/* 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) {
|
|
return net_ads_info_json(ads);
|
|
}
|
|
|
|
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(talloc_tos(), 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(talloc_tos(), pass_time));
|
|
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
|
|
static ADS_STATUS ads_startup_int(struct net_context *c, bool only_own_domain,
|
|
uint32_t auth_flags, 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(realm,
|
|
c->opt_target_workgroup,
|
|
c->opt_host,
|
|
ADS_SASL_PLAIN);
|
|
|
|
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) {
|
|
ads_destroy(&ads);
|
|
return ADS_ERROR(LDAP_NO_MEMORY);
|
|
}
|
|
}
|
|
|
|
if (c->opt_password) {
|
|
use_in_memory_ccache();
|
|
SAFE_FREE(ads->auth.password);
|
|
ads->auth.password = smb_xstrdup(c->opt_password);
|
|
}
|
|
|
|
SAFE_FREE(ads->auth.user_name);
|
|
ads->auth.user_name = smb_xstrdup(c->opt_user_name);
|
|
|
|
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';
|
|
SAFE_FREE(ads->auth.realm);
|
|
ads->auth.realm = smb_xstrdup(cp);
|
|
if (!strupper_m(ads->auth.realm)) {
|
|
ads_destroy(&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)));
|
|
ads_destroy(&ads);
|
|
return status;
|
|
}
|
|
|
|
if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
|
|
need_password = true;
|
|
second_time = true;
|
|
goto retry;
|
|
} else {
|
|
ads_destroy(&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);
|
|
|
|
ads_destroy(&ads);
|
|
ads = NULL;
|
|
|
|
goto retry_connect;
|
|
}
|
|
}
|
|
|
|
*ads_ret = ads;
|
|
return status;
|
|
}
|
|
|
|
ADS_STATUS ads_startup(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
|
|
{
|
|
return ads_startup_int(c, only_own_domain, 0, ads);
|
|
}
|
|
|
|
ADS_STATUS ads_startup_nobind(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
|
|
{
|
|
return ads_startup_int(c, only_own_domain, ADS_AUTH_NO_BIND, 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(const char *realm, const char *workgroup, const char *host)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS status;
|
|
|
|
ads = ads_init(realm, workgroup, host, ADS_SASL_PLAIN);
|
|
if (ads == NULL ) {
|
|
return -1;
|
|
}
|
|
|
|
ads->auth.flags |= ADS_AUTH_NO_BIND;
|
|
|
|
status = ads_connect(ads);
|
|
if ( !ADS_ERR_OK(status) ) {
|
|
return -1;
|
|
}
|
|
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
|
|
int net_ads_check_our_domain(struct net_context *c)
|
|
{
|
|
return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
|
|
}
|
|
|
|
int net_ads_check(struct net_context *c)
|
|
{
|
|
return net_ads_check_int(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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
|
|
|
|
if (c->display_usage) {
|
|
d_printf ("%s\n"
|
|
"net ads workgroup\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Print the workgroup name"));
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
|
|
d_fprintf(stderr, _("Didn't find the cldap server!\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (!ads->config.realm) {
|
|
ads->config.realm = discard_const_p(char, c->opt_target_workgroup);
|
|
ads->ldap.port = 389;
|
|
}
|
|
|
|
if ( !ads_cldap_netlogon_5(talloc_tos(), &ads->ldap.ss, ads->server.realm, &reply ) ) {
|
|
d_fprintf(stderr, _("CLDAP query failed!\n"));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
d_printf(_("Workgroup: %s\n"), reply.domain_name);
|
|
|
|
ads_destroy(&ads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS status;
|
|
char *upn, *userdn;
|
|
LDAPMessage *res=NULL;
|
|
int rc = -1;
|
|
char *ou_str = NULL;
|
|
|
|
if (argc < 1 || c->display_usage)
|
|
return net_ads_user_usage(c, argc, argv);
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
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 */
|
|
if (asprintf(&upn, "%s@%s", argv[0], ads->config.realm) == -1) {
|
|
goto done;
|
|
}
|
|
status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
|
|
ads->auth.time_offset);
|
|
SAFE_FREE(upn);
|
|
if (ADS_ERR_OK(status)) {
|
|
d_printf(_("User %s added\n"), argv[0]);
|
|
rc = 0;
|
|
goto done;
|
|
}
|
|
|
|
/* 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);
|
|
status=ads_find_user_acct(ads, &res, argv[0]);
|
|
if (ADS_ERR_OK(status)) {
|
|
userdn = ads_get_dn(ads, talloc_tos(), res);
|
|
ads_del_dn(ads, userdn);
|
|
TALLOC_FREE(userdn);
|
|
}
|
|
|
|
done:
|
|
if (res)
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
SAFE_FREE(ou_str);
|
|
return rc;
|
|
}
|
|
|
|
static int ads_user_info(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads = NULL;
|
|
ADS_STATUS rc;
|
|
LDAPMessage *res = NULL;
|
|
TALLOC_CTX *frame;
|
|
int ret = 0;
|
|
wbcErr wbc_status;
|
|
const char *attrs[] = {"memberOf", "primaryGroupID", NULL};
|
|
char *searchstring=NULL;
|
|
char **grouplist;
|
|
char *primary_group;
|
|
char *escaped_user;
|
|
struct dom_sid primary_group_sid;
|
|
uint32_t group_rid;
|
|
enum wbcSidType type;
|
|
|
|
if (argc < 1 || c->display_usage) {
|
|
return net_ads_user_usage(c, argc, argv);
|
|
}
|
|
|
|
frame = talloc_new(talloc_tos());
|
|
if (frame == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
escaped_user = escape_ldap_string(frame, argv[0]);
|
|
if (!escaped_user) {
|
|
d_fprintf(stderr,
|
|
_("ads_user_info: failed to escape user %s\n"),
|
|
argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
if (asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user) == -1) {
|
|
ret =-1;
|
|
goto error;
|
|
}
|
|
rc = ads_search(ads, &res, searchstring, attrs);
|
|
SAFE_FREE(searchstring);
|
|
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("ads_search: %s\n"), ads_errstr(rc));
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
if (!ads_pull_uint32(ads, res, "primaryGroupID", &group_rid)) {
|
|
d_fprintf(stderr, _("ads_pull_uint32 failed\n"));
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
rc = ads_domain_sid(ads, &primary_group_sid);
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("ads_domain_sid: %s\n"), ads_errstr(rc));
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
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));
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
error:
|
|
if (res) ads_msgfree(ads, res);
|
|
if (ads) ads_destroy(&ads);
|
|
TALLOC_FREE(frame);
|
|
return ret;
|
|
}
|
|
|
|
static int ads_user_delete(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
LDAPMessage *res = NULL;
|
|
char *userdn;
|
|
|
|
if (argc < 1) {
|
|
return net_ads_user_usage(c, argc, argv);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
rc = ads_find_user_acct(ads, &res, argv[0]);
|
|
if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
|
|
d_printf(_("User %s does not exist.\n"), argv[0]);
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
userdn = ads_get_dn(ads, talloc_tos(), res);
|
|
ads_msgfree(ads, res);
|
|
rc = ads_del_dn(ads, userdn);
|
|
TALLOC_FREE(userdn);
|
|
if (ADS_ERR_OK(rc)) {
|
|
d_printf(_("User %s deleted\n"), argv[0]);
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
d_fprintf(stderr, _("Error deleting user %s: %s\n"), argv[0],
|
|
ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
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}
|
|
};
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *shortattrs[] = {"sAMAccountName", NULL};
|
|
const char *longattrs[] = {"sAMAccountName", "description", NULL};
|
|
char *disp_fields[2] = {NULL, NULL};
|
|
|
|
if (argc == 0) {
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads user\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("List AD users"));
|
|
net_display_usage_from_functable(func);
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
if (c->opt_long_list_entries)
|
|
d_printf(_("\nUser name Comment"
|
|
"\n-----------------------------\n"));
|
|
|
|
rc = 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);
|
|
ads_destroy(&ads);
|
|
return ADS_ERR_OK(rc) ? 0 : -1;
|
|
}
|
|
|
|
return net_run_function(c, argc, argv, "net ads user", func);
|
|
}
|
|
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS status;
|
|
LDAPMessage *res=NULL;
|
|
int rc = -1;
|
|
char *ou_str = NULL;
|
|
|
|
if (argc < 1 || c->display_usage) {
|
|
return net_ads_group_usage(c, argc, argv);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
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 done;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res)) {
|
|
d_fprintf(stderr, _("ads_group_add: Group %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_group_acct(ads, argv[0], ou_str, c->opt_comment);
|
|
|
|
if (ADS_ERR_OK(status)) {
|
|
d_printf(_("Group %s added\n"), argv[0]);
|
|
rc = 0;
|
|
} else {
|
|
d_fprintf(stderr, _("Could not add group %s: %s\n"), argv[0],
|
|
ads_errstr(status));
|
|
}
|
|
|
|
done:
|
|
if (res)
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
SAFE_FREE(ou_str);
|
|
return rc;
|
|
}
|
|
|
|
static int ads_group_delete(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
LDAPMessage *res = NULL;
|
|
char *groupdn;
|
|
|
|
if (argc < 1 || c->display_usage) {
|
|
return net_ads_group_usage(c, argc, argv);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
rc = ads_find_user_acct(ads, &res, argv[0]);
|
|
if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
|
|
d_printf(_("Group %s does not exist.\n"), argv[0]);
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
groupdn = ads_get_dn(ads, talloc_tos(), res);
|
|
ads_msgfree(ads, res);
|
|
rc = ads_del_dn(ads, groupdn);
|
|
TALLOC_FREE(groupdn);
|
|
if (ADS_ERR_OK(rc)) {
|
|
d_printf(_("Group %s deleted\n"), argv[0]);
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
d_fprintf(stderr, _("Error deleting group %s: %s\n"), argv[0],
|
|
ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
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}
|
|
};
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *shortattrs[] = {"sAMAccountName", NULL};
|
|
const char *longattrs[] = {"sAMAccountName", "description", NULL};
|
|
char *disp_fields[2] = {NULL, NULL};
|
|
|
|
if (argc == 0) {
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads group\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("List AD groups"));
|
|
net_display_usage_from_functable(func);
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
if (c->opt_long_list_entries)
|
|
d_printf(_("\nGroup name Comment"
|
|
"\n-----------------------------\n"));
|
|
rc = 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);
|
|
|
|
ads_destroy(&ads);
|
|
return ADS_ERR_OK(rc) ? 0 : -1;
|
|
}
|
|
return net_run_function(c, argc, argv, "net ads group", func);
|
|
}
|
|
|
|
static int net_ads_status(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
LDAPMessage *res;
|
|
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads status\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Display machine account details"));
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
rc = ads_find_machine_acct(ads, &res, lp_netbios_name());
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("ads_find_machine_acct: %s\n"), ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) == 0) {
|
|
d_fprintf(stderr, _("No machine account for '%s' found\n"), lp_netbios_name());
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
ads_dump(ads, res);
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************
|
|
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 *ctx;
|
|
struct libnet_UnjoinCtx *r = NULL;
|
|
WERROR werr;
|
|
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads leave [--keep-account]\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Leave an AD domain"));
|
|
return 0;
|
|
}
|
|
|
|
if (!*lp_realm()) {
|
|
d_fprintf(stderr, _("No realm set, are we joined ?\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(ctx = talloc_init("net_ads_leave"))) {
|
|
d_fprintf(stderr, _("Could not initialise talloc context.\n"));
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
werr = libnet_init_UnjoinCtx(ctx, &r);
|
|
if (!W_ERROR_IS_OK(werr)) {
|
|
d_fprintf(stderr, _("Could not initialise unjoin context.\n"));
|
|
return -1;
|
|
}
|
|
|
|
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(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);
|
|
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);
|
|
werr = WERR_OK;
|
|
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);
|
|
|
|
done:
|
|
TALLOC_FREE(r);
|
|
TALLOC_FREE(ctx);
|
|
|
|
if (W_ERROR_IS_OK(werr)) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static NTSTATUS net_ads_join_ok(struct net_context *c)
|
|
{
|
|
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"));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
net_use_krb_machine_account(c);
|
|
|
|
get_dc_name(lp_workgroup(), lp_realm(), dc_name, &dcip);
|
|
|
|
status = ads_startup(c, true, &ads);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return ads_ntstatus(status);
|
|
}
|
|
|
|
ads_destroy(&ads);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/*
|
|
check that an existing join is OK
|
|
*/
|
|
int net_ads_testjoin(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
NTSTATUS 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 0;
|
|
}
|
|
|
|
/* Display success or failure */
|
|
status = net_ads_join_ok(c);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
fprintf(stderr, _("Join to domain is not valid: %s\n"),
|
|
get_friendly_nt_error_msg(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 *ctx = NULL;
|
|
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;
|
|
|
|
if (c->display_usage)
|
|
return net_ads_join_usage(c, argc, argv);
|
|
|
|
if (!modify_config) {
|
|
|
|
werr = check_ads_config();
|
|
if (!W_ERROR_IS_OK(werr)) {
|
|
d_fprintf(stderr, _("Invalid configuration. Exiting....\n"));
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (!(ctx = talloc_init("net_ads_join"))) {
|
|
d_fprintf(stderr, _("Could not initialise talloc context.\n"));
|
|
werr = WERR_NOT_ENOUGH_MEMORY;
|
|
goto fail;
|
|
}
|
|
|
|
if (!c->opt_kerberos) {
|
|
use_in_memory_ccache();
|
|
}
|
|
|
|
werr = libnet_init_JoinCtx(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(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(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, ctx, r);
|
|
}
|
|
|
|
TALLOC_FREE(r);
|
|
TALLOC_FREE( ctx );
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
/* 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( ctx );
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*******************************************************************
|
|
********************************************************************/
|
|
|
|
static int net_ads_dns_register(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
#if defined(HAVE_KRB5)
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS status;
|
|
NTSTATUS ntstatus;
|
|
TALLOC_CTX *ctx;
|
|
const char *hostname = NULL;
|
|
const char **addrs_list = NULL;
|
|
struct sockaddr_storage *addrs = NULL;
|
|
int num_addrs = 0;
|
|
int count;
|
|
|
|
#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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(ctx = talloc_init("net_ads_dns"))) {
|
|
d_fprintf(stderr, _("Could not initialise talloc context\n"));
|
|
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(ctx, struct sockaddr_storage, num_addrs);
|
|
if (addrs == NULL) {
|
|
d_fprintf(stderr, _("Error allocating memory!\n"));
|
|
talloc_free(ctx);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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]);
|
|
talloc_free(ctx);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
status = ads_startup(c, true, &ads);
|
|
if ( !ADS_ERR_OK(status) ) {
|
|
DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
|
|
TALLOC_FREE(ctx);
|
|
return -1;
|
|
}
|
|
|
|
ntstatus = net_update_dns_ext(c, ctx, ads, hostname, addrs, num_addrs, false);
|
|
if (!NT_STATUS_IS_OK(ntstatus)) {
|
|
d_fprintf( stderr, _("DNS update failed!\n") );
|
|
ads_destroy( &ads );
|
|
TALLOC_FREE( ctx );
|
|
return -1;
|
|
}
|
|
|
|
d_fprintf( stderr, _("Successfully registered hostname with DNS\n") );
|
|
|
|
ads_destroy(&ads);
|
|
TALLOC_FREE( ctx );
|
|
|
|
return 0;
|
|
#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)
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS status;
|
|
NTSTATUS ntstatus;
|
|
TALLOC_CTX *ctx;
|
|
const char *hostname = NULL;
|
|
|
|
#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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(ctx = talloc_init("net_ads_dns"))) {
|
|
d_fprintf(stderr, _("Could not initialise talloc context\n"));
|
|
return -1;
|
|
}
|
|
|
|
/* Get the hostname for un-registering */
|
|
hostname = argv[0];
|
|
|
|
status = ads_startup(c, true, &ads);
|
|
if ( !ADS_ERR_OK(status) ) {
|
|
DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
|
|
TALLOC_FREE(ctx);
|
|
return -1;
|
|
}
|
|
|
|
ntstatus = net_update_dns_ext(c, ctx, ads, hostname, NULL, 0, true);
|
|
if (!NT_STATUS_IS_OK(ntstatus)) {
|
|
d_fprintf( stderr, _("DNS update failed!\n") );
|
|
ads_destroy( &ads );
|
|
TALLOC_FREE( ctx );
|
|
return -1;
|
|
}
|
|
|
|
d_fprintf( stderr, _("Successfully un-registered hostname from DNS\n"));
|
|
|
|
ads_destroy(&ads);
|
|
TALLOC_FREE( ctx );
|
|
|
|
return 0;
|
|
#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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
LDAPMessage *res = NULL;
|
|
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads printer search\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("List printers in the AD"));
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
rc = ads_find_printers(ads, &res);
|
|
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("ads_find_printer: %s\n"), ads_errstr(rc));
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) == 0) {
|
|
d_fprintf(stderr, _("No results found\n"));
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
ads_dump(ads, res);
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
|
|
static int net_ads_printer_info(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *servername, *printername;
|
|
LDAPMessage *res = NULL;
|
|
|
|
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"));
|
|
return 0;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
if (argc > 0) {
|
|
printername = argv[0];
|
|
} else {
|
|
printername = "*";
|
|
}
|
|
|
|
if (argc > 1) {
|
|
servername = argv[1];
|
|
} else {
|
|
servername = lp_netbios_name();
|
|
}
|
|
|
|
rc = ads_find_printer_on_server(ads, &res, printername, servername);
|
|
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("Server '%s' not found: %s\n"),
|
|
servername, ads_errstr(rc));
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) == 0) {
|
|
d_fprintf(stderr, _("Printer '%s' not found\n"), printername);
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
ads_dump(ads, res);
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int net_ads_printer_publish(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *servername, *printername;
|
|
struct cli_state *cli = NULL;
|
|
struct rpc_pipe_client *pipe_hnd = NULL;
|
|
struct sockaddr_storage server_ss;
|
|
NTSTATUS nt_status;
|
|
TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
|
|
ADS_MODLIST mods = ads_init_mods(mem_ctx);
|
|
char *prt_dn, *srv_dn, **srv_cn;
|
|
char *srv_cn_escaped = NULL, *printername_escaped = NULL;
|
|
LDAPMessage *res = NULL;
|
|
bool ok;
|
|
|
|
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_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
/* 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);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
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!"));
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
if (asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn) == -1) {
|
|
SAFE_FREE(srv_cn_escaped);
|
|
SAFE_FREE(printername_escaped);
|
|
d_fprintf(stderr, _("Internal error, out of memory!"));
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
SAFE_FREE(prt_dn);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
|
|
printername))) {
|
|
SAFE_FREE(prt_dn);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
|
|
SAFE_FREE(prt_dn);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
return -1;
|
|
}
|
|
|
|
d_printf("published printer\n");
|
|
SAFE_FREE(prt_dn);
|
|
ads_destroy(&ads);
|
|
talloc_destroy(mem_ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int net_ads_printer_remove(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *servername;
|
|
char *prt_dn;
|
|
LDAPMessage *res = NULL;
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
if (argc > 1) {
|
|
servername = argv[1];
|
|
} else {
|
|
servername = lp_netbios_name();
|
|
}
|
|
|
|
rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
|
|
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("ads_find_printer_on_server: %s\n"), ads_errstr(rc));
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) == 0) {
|
|
d_fprintf(stderr, _("Printer '%s' not found\n"), argv[1]);
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
prt_dn = ads_get_dn(ads, talloc_tos(), res);
|
|
ads_msgfree(ads, res);
|
|
rc = ads_del_dn(ads, prt_dn);
|
|
TALLOC_FREE(prt_dn);
|
|
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("ads_del_dn: %s\n"), ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
ads_destroy(&ads);
|
|
return 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
const char *auth_principal = cli_credentials_get_username(c->creds);
|
|
const char *auth_password = cli_credentials_get_password(c->creds);
|
|
const char *realm = NULL;
|
|
const char *new_password = NULL;
|
|
char *chr, *prompt;
|
|
const char *user;
|
|
char pwd[256] = {0};
|
|
ADS_STATUS ret;
|
|
|
|
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"));
|
|
return 0;
|
|
}
|
|
|
|
if (auth_principal == NULL || auth_password == NULL) {
|
|
d_fprintf(stderr, _("You must supply an administrator "
|
|
"username/password\n"));
|
|
return -1;
|
|
}
|
|
|
|
if (argc < 1) {
|
|
d_fprintf(stderr, _("ERROR: You must say which username to "
|
|
"change password for\n"));
|
|
return -1;
|
|
}
|
|
|
|
user = argv[0];
|
|
if (!strchr_m(user, '@')) {
|
|
if (asprintf(&chr, "%s@%s", argv[0], lp_realm()) == -1) {
|
|
return -1;
|
|
}
|
|
user = chr;
|
|
}
|
|
|
|
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(realm, c->opt_workgroup, c->opt_host, ADS_SASL_PLAIN);
|
|
if (ads == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (argv[1]) {
|
|
new_password = (const char *)argv[1];
|
|
} else {
|
|
int rc;
|
|
|
|
if (asprintf(&prompt, _("Enter new password for %s:"), user) == -1) {
|
|
return -1;
|
|
}
|
|
rc = samba_getpass(prompt, pwd, sizeof(pwd), false, true);
|
|
if (rc < 0) {
|
|
return -1;
|
|
}
|
|
new_password = pwd;
|
|
free(prompt);
|
|
}
|
|
|
|
ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
|
|
auth_password, user, new_password, ads->auth.time_offset);
|
|
memset(pwd, '\0', sizeof(pwd));
|
|
if (!ADS_ERR_OK(ret)) {
|
|
d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(ret));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
d_printf(_("Password change for %s completed.\n"), user);
|
|
ads_destroy(&ads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
char *host_principal;
|
|
fstring my_name;
|
|
ADS_STATUS ret;
|
|
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads changetrustpw\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Change the machine account's trust password"));
|
|
return 0;
|
|
}
|
|
|
|
if (!secrets_init()) {
|
|
DEBUG(1,("Failed to initialise secrets database\n"));
|
|
return -1;
|
|
}
|
|
|
|
net_use_krb_machine_account(c);
|
|
|
|
use_in_memory_ccache();
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
fstrcpy(my_name, lp_netbios_name());
|
|
if (!strlower_m(my_name)) {
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
if (asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm) == -1) {
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
d_printf(_("Changing password for principal: %s\n"), host_principal);
|
|
|
|
ret = ads_change_trust_account_password(ads, host_principal);
|
|
|
|
if (!ADS_ERR_OK(ret)) {
|
|
d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(ret));
|
|
ads_destroy(&ads);
|
|
SAFE_FREE(host_principal);
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
}
|
|
}
|
|
|
|
ads_destroy(&ads);
|
|
SAFE_FREE(host_principal);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *ldap_exp;
|
|
const char **attrs;
|
|
LDAPMessage *res = NULL;
|
|
|
|
if (argc < 1 || c->display_usage) {
|
|
return net_ads_search_usage(c, argc, argv);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
ldap_exp = argv[0];
|
|
attrs = (argv + 1);
|
|
|
|
rc = ads_do_search_retry(ads, ads->config.bind_path,
|
|
LDAP_SCOPE_SUBTREE,
|
|
ldap_exp, attrs, &res);
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res));
|
|
|
|
/* dump the results */
|
|
ads_dump(ads, res);
|
|
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *dn;
|
|
const char **attrs;
|
|
LDAPMessage *res = NULL;
|
|
|
|
if (argc < 1 || c->display_usage) {
|
|
return net_ads_dn_usage(c, argc, argv);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
dn = argv[0];
|
|
attrs = (argv + 1);
|
|
|
|
rc = ads_do_search_all(ads, dn,
|
|
LDAP_SCOPE_BASE,
|
|
"(objectclass=*)", attrs, &res);
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
|
|
|
|
/* dump the results */
|
|
ads_dump(ads, res);
|
|
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
ADS_STATUS rc;
|
|
const char *sid_string;
|
|
const char **attrs;
|
|
LDAPMessage *res = NULL;
|
|
struct dom_sid sid;
|
|
|
|
if (argc < 1 || c->display_usage) {
|
|
return net_ads_sid_usage(c, argc, argv);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
|
|
return -1;
|
|
}
|
|
|
|
sid_string = argv[0];
|
|
attrs = (argv + 1);
|
|
|
|
if (!string_to_sid(&sid, sid_string)) {
|
|
d_fprintf(stderr, _("could not convert sid\n"));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
rc = ads_search_retry_sid(ads, &res, &sid, attrs);
|
|
if (!ADS_ERR_OK(rc)) {
|
|
d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(rc));
|
|
ads_destroy(&ads);
|
|
return -1;
|
|
}
|
|
|
|
d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res));
|
|
|
|
/* dump the results */
|
|
ads_dump(ads, res);
|
|
|
|
ads_msgfree(ads, res);
|
|
ads_destroy(&ads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int net_ads_keytab_flush(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
int ret;
|
|
ADS_STRUCT *ads;
|
|
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads keytab flush\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Delete the whole keytab"));
|
|
return 0;
|
|
}
|
|
|
|
if (!c->opt_user_specified && c->opt_password == NULL) {
|
|
net_use_krb_machine_account(c);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
return -1;
|
|
}
|
|
ret = ads_keytab_flush(ads);
|
|
ads_destroy(&ads);
|
|
return ret;
|
|
}
|
|
|
|
static int net_ads_keytab_add(struct net_context *c,
|
|
int argc,
|
|
const char **argv,
|
|
bool update_ads)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
ADS_STRUCT *ads;
|
|
|
|
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"));
|
|
return 0;
|
|
}
|
|
|
|
d_printf(_("Processing principals to add...\n"));
|
|
|
|
if (!c->opt_user_specified && c->opt_password == NULL) {
|
|
net_use_krb_machine_account(c);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
return -1;
|
|
}
|
|
for (i = 0; i < argc; i++) {
|
|
ret |= ads_keytab_add_entry(ads, argv[i], update_ads);
|
|
}
|
|
ads_destroy(&ads);
|
|
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)
|
|
{
|
|
ADS_STRUCT *ads;
|
|
int ret;
|
|
|
|
if (c->display_usage) {
|
|
d_printf( "%s\n"
|
|
"net ads keytab create\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Create new default keytab"));
|
|
return 0;
|
|
}
|
|
|
|
if (!c->opt_user_specified && c->opt_password == NULL) {
|
|
net_use_krb_machine_account(c);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
return -1;
|
|
}
|
|
ret = ads_keytab_create_default(ads);
|
|
ads_destroy(&ads);
|
|
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 0;
|
|
}
|
|
|
|
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 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
TALLOC_CTX *mem_ctx = NULL;
|
|
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 0;
|
|
}
|
|
|
|
mem_ctx = talloc_init("net_ads_kerberos_kinit");
|
|
if (!mem_ctx) {
|
|
goto out;
|
|
}
|
|
|
|
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));
|
|
}
|
|
out:
|
|
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)
|
|
{
|
|
int ret = 0;
|
|
bool ok = false;
|
|
ADS_STRUCT *ads = NULL;
|
|
if (c->display_usage) {
|
|
d_printf("%s\n%s",
|
|
_("Usage:"),
|
|
_("net ads setspn list <machinename>\n"));
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
if (argc) {
|
|
ok = ads_setspn_list(ads, argv[0]);
|
|
} else {
|
|
ok = ads_setspn_list(ads, lp_netbios_name());
|
|
}
|
|
if (!ok) {
|
|
ret = -1;
|
|
}
|
|
done:
|
|
if (ads) {
|
|
ads_destroy(&ads);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int net_ads_setspn_add(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
int ret = 0;
|
|
bool ok = false;
|
|
ADS_STRUCT *ads = NULL;
|
|
if (c->display_usage || argc < 1) {
|
|
d_printf("%s\n%s",
|
|
_("Usage:"),
|
|
_("net ads setspn add <machinename> SPN\n"));
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
if (argc > 1) {
|
|
ok = ads_setspn_add(ads, argv[0], argv[1]);
|
|
} else {
|
|
ok = ads_setspn_add(ads, lp_netbios_name(), argv[0]);
|
|
}
|
|
if (!ok) {
|
|
ret = -1;
|
|
}
|
|
done:
|
|
if (ads) {
|
|
ads_destroy(&ads);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int net_ads_setspn_delete(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
int ret = 0;
|
|
bool ok = false;
|
|
ADS_STRUCT *ads = NULL;
|
|
if (c->display_usage || argc < 1) {
|
|
d_printf("%s\n%s",
|
|
_("Usage:"),
|
|
_("net ads setspn delete <machinename> SPN\n"));
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
if (argc > 1) {
|
|
ok = ads_setspn_delete(ads, argv[0], argv[1]);
|
|
} else {
|
|
ok = ads_setspn_delete(ads, lp_netbios_name(), argv[0]);
|
|
}
|
|
if (!ok) {
|
|
ret = -1;
|
|
}
|
|
done:
|
|
if (ads) {
|
|
ads_destroy(&ads);
|
|
}
|
|
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);
|
|
}
|
|
|
|
static int net_ads_enctypes_list(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
int ret = -1;
|
|
ADS_STATUS status;
|
|
ADS_STRUCT *ads = NULL;
|
|
LDAPMessage *res = NULL;
|
|
const char *str = NULL;
|
|
|
|
if (c->display_usage || (argc < 1)) {
|
|
d_printf( "%s\n"
|
|
"net ads enctypes list\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("List supported enctypes"));
|
|
return 0;
|
|
}
|
|
|
|
status = ads_startup(c, false, &ads);
|
|
if (!ADS_ERR_OK(status)) {
|
|
printf("startup failed\n");
|
|
return ret;
|
|
}
|
|
|
|
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);
|
|
ads_destroy(&ads);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int net_ads_enctypes_set(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
int ret = -1;
|
|
ADS_STATUS status;
|
|
ADS_STRUCT *ads;
|
|
LDAPMessage *res = NULL;
|
|
const char *etype_list_str;
|
|
const char *dn;
|
|
ADS_MODLIST mods;
|
|
uint32_t etype_list;
|
|
const char *str;
|
|
|
|
if (c->display_usage || argc < 1) {
|
|
d_printf( "%s\n"
|
|
"net ads enctypes set <sAMAccountName> [enctypes]\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Set supported enctypes"));
|
|
return 0;
|
|
}
|
|
|
|
status = ads_startup(c, false, &ads);
|
|
if (!ADS_ERR_OK(status)) {
|
|
printf("startup failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL);
|
|
if (ret) {
|
|
goto done;
|
|
}
|
|
|
|
dn = ads_get_dn(ads, c, res);
|
|
if (dn == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
etype_list = ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
|
|
#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
|
|
etype_list |= ENC_HMAC_SHA1_96_AES128;
|
|
#endif
|
|
#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
|
|
etype_list |= ENC_HMAC_SHA1_96_AES256;
|
|
#endif
|
|
|
|
if (argv[1] != NULL) {
|
|
sscanf(argv[1], "%i", &etype_list);
|
|
}
|
|
|
|
etype_list_str = talloc_asprintf(c, "%d", etype_list);
|
|
if (!etype_list_str) {
|
|
goto done;
|
|
}
|
|
|
|
mods = ads_init_mods(c);
|
|
if (!mods) {
|
|
goto done;
|
|
}
|
|
|
|
status = ads_mod_str(c, &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);
|
|
|
|
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);
|
|
ads_destroy(&ads);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int net_ads_enctypes_delete(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
int ret = -1;
|
|
ADS_STATUS status;
|
|
ADS_STRUCT *ads;
|
|
LDAPMessage *res = NULL;
|
|
const char *dn;
|
|
ADS_MODLIST mods;
|
|
|
|
if (c->display_usage || argc < 1) {
|
|
d_printf( "%s\n"
|
|
"net ads enctypes delete <sAMAccountName>\n"
|
|
" %s\n",
|
|
_("Usage:"),
|
|
_("Delete supported enctypes"));
|
|
return 0;
|
|
}
|
|
|
|
status = ads_startup(c, false, &ads);
|
|
if (!ADS_ERR_OK(status)) {
|
|
printf("startup failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL);
|
|
if (ret) {
|
|
goto done;
|
|
}
|
|
|
|
dn = ads_get_dn(ads, c, res);
|
|
if (dn == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
mods = ads_init_mods(c);
|
|
if (!mods) {
|
|
goto done;
|
|
}
|
|
|
|
status = ads_mod_str(c, &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);
|
|
ads_destroy(&ads);
|
|
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 */
|