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
*/
#include "includes.h"
-#include "../utils/net.h"
+#include "utils/net.h"
+/* Macro for checking RPC error codes to make things more readable */
+
+#if 0
+#define CHECK_RPC_ERR(rpc, msg) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
+ goto done; \
+ }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, debug_args); \
+ goto done; \
+ }
+
+#endif
#ifdef HAVE_ADS
int net_ads_usage(int argc, const char **argv)
"\n\tperform a raw LDAP search and dump the results\n"
"\nnet ads dn"\
"\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
+"\nnet ads sid"\
+"\n\tperform a raw LDAP search and dump attributes of a particular SID\n"
"\nnet ads keytab"\
"\n\tcreates and updates the kerberos system keytab file\n"
);
}
+/*
+ do a cldap netlogon query
+*/
+static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
+{
+ struct cldap_netlogon_reply reply;
+
+ if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
+ d_fprintf(stderr, "CLDAP query failed!\n");
+ return -1;
+ }
+
+ d_printf("Information for Domain Controller: %s\n\n",
+ inet_ntoa(ads->ldap_ip));
+
+ d_printf("Response Type: ");
+ switch (reply.type) {
+ case SAMLOGON_AD_UNK_R:
+ d_printf("SAMLOGON\n");
+ break;
+ case SAMLOGON_AD_R:
+ d_printf("SAMLOGON_USER\n");
+ break;
+ default:
+ d_printf("0x%x\n", reply.type);
+ break;
+ }
+ d_printf("GUID: %s\n",
+ smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
+ 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",
+ (reply.flags & ADS_PDC) ? "yes" : "no",
+ (reply.flags & ADS_GC) ? "yes" : "no",
+ (reply.flags & ADS_LDAP) ? "yes" : "no",
+ (reply.flags & ADS_DS) ? "yes" : "no",
+ (reply.flags & ADS_KDC) ? "yes" : "no",
+ (reply.flags & ADS_TIMESERV) ? "yes" : "no",
+ (reply.flags & ADS_CLOSEST) ? "yes" : "no",
+ (reply.flags & ADS_WRITABLE) ? "yes" : "no",
+ (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
+ (reply.flags & ADS_NDNC) ? "yes" : "no");
+
+ printf("Forest:\t\t\t%s\n", reply.forest);
+ printf("Domain:\t\t\t%s\n", reply.domain);
+ printf("Domain Controller:\t%s\n", reply.hostname);
+
+ printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
+ printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
+
+ if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
+ if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
+
+ printf("Site Name:\t\t%s\n", reply.site_name);
+ printf("Site Name (2):\t\t%s\n", reply.site_name_2);
+
+ d_printf("NT Version: %d\n", reply.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(int argc, const char **argv)
{
ADS_STRUCT *ads;
+ ADS_STATUS status;
+ const char *realm = NULL;
- ads = ads_init(NULL, opt_target_workgroup, opt_host);
+ if ( strequal(lp_workgroup(), opt_target_workgroup ) )
+ realm = lp_realm();
+
+ ads = ads_init(realm, opt_target_workgroup, opt_host);
if (ads) {
ads->auth.flags |= ADS_AUTH_NO_BIND;
}
- ads_connect(ads);
-
- if (!ads || !ads->config.realm) {
- d_printf("Didn't find the cldap server!\n");
+ status = ads_connect(ads);
+ if (!ADS_ERR_OK(status) || !ads) {
+ d_fprintf(stderr, "Didn't find the cldap server!\n");
return -1;
}
+
+ if (!ads->config.realm) {
+ ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
+ ads->ldap_port = 389;
+ }
- return ads_cldap_netlogon(ads);
+ return net_ads_cldap_netlogon(ads);
}
{
ADS_STRUCT *ads;
- ads = ads_init(NULL, opt_target_workgroup, opt_host);
-
- if (ads) {
+ if ( (ads = ads_init(lp_realm(), opt_target_workgroup, opt_host)) != NULL ) {
ads->auth.flags |= ADS_AUTH_NO_BIND;
}
ads_connect(ads);
if (!ads || !ads->config.realm) {
- d_printf("Didn't find the ldap server!\n");
+ d_fprintf(stderr, "Didn't find the ldap server!\n");
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");
+ }
+
d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
d_printf("Realm: %s\n", ads->config.realm);
d_printf("LDAP port: %d\n", ads->ldap_port);
d_printf("Server time: %s\n", http_timestring(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("KDC server: %s\n", ads->auth.kdc_server );
+ d_printf("Server time offset: %d\n", ads->auth.time_offset );
return 0;
}
* extract the realm and convert to upper case.
* This is only used to establish the connection.
*/
- if ((cp = strchr(ads->auth.user_name, '@'))!=0) {
+ if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
*cp++ = '\0';
ads->auth.realm = smb_xstrdup(cp);
strupper_m(ads->auth.realm);
second_time = True;
goto retry;
} else {
- DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
+ DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
+ ads_destroy(&ads);
return NULL;
}
}
int net_ads_check(void)
{
ADS_STRUCT *ads;
+ ADS_STATUS status;
- ads = ads_startup();
- if (!ads)
+ if ( (ads = ads_init( lp_realm(), lp_workgroup(), NULL )) == 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;
}
static int net_ads_workgroup(int argc, const char **argv)
{
ADS_STRUCT *ads;
- TALLOC_CTX *ctx;
- const char *workgroup;
+ ADS_STATUS status;
+ const char *realm = NULL;
+ struct cldap_netlogon_reply reply;
- if (!(ads = ads_startup())) return -1;
+ if ( strequal(lp_workgroup(), opt_target_workgroup ) )
+ realm = lp_realm();
- if (!(ctx = talloc_init("net_ads_workgroup"))) {
- return -1;
+ ads = ads_init(realm, opt_target_workgroup, opt_host);
+ if (ads) {
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
}
- if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
- d_printf("Failed to find workgroup for realm '%s'\n",
- ads->config.realm);
- talloc_destroy(ctx);
+ status = ads_connect(ads);
+ if (!ADS_ERR_OK(status) || !ads) {
+ d_fprintf(stderr, "Didn't find the cldap server!\n");
+ return -1;
+ }
+
+ if (!ads->config.realm) {
+ ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
+ ads->ldap_port = 389;
+ }
+
+ if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
+ d_fprintf(stderr, "CLDAP query failed!\n");
return -1;
}
- d_printf("Workgroup: %s\n", workgroup);
-
- talloc_destroy(ctx);
+ d_printf("Workgroup: %s\n", reply.netbios_domain);
+ ads_destroy(&ads);
+
return 0;
}
char **disp_fields = (char **) data_area;
if (!field) { /* must be end of record */
- 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]);
+ 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]);
if (!values) /* must be new field, indicate string field */
return True;
if (StrCaseCmp(field, "sAMAccountName") == 0) {
- disp_fields[0] = strdup((char *) values[0]);
+ disp_fields[0] = SMB_STRDUP((char *) values[0]);
}
if (StrCaseCmp(field, "description") == 0)
- disp_fields[1] = strdup((char *) values[0]);
+ disp_fields[1] = SMB_STRDUP((char *) values[0]);
return True;
}
if (argc < 1) return net_ads_user_usage(argc, argv);
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
status = ads_find_user_acct(ads, &res, argv[0]);
if (!ADS_ERR_OK(status)) {
- d_printf("ads_user_add: %s\n", ads_errstr(status));
+ d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
goto done;
}
if (ads_count_replies(ads, res)) {
- d_printf("ads_user_add: User %s already exists\n", argv[0]);
+ d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
goto done;
}
+ if (opt_container == NULL) {
+ opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
+ }
+
status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
if (!ADS_ERR_OK(status)) {
- d_printf("Could not add user %s: %s\n", argv[0],
+ d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
ads_errstr(status));
goto done;
}
}
/* password didn't set, delete account */
- d_printf("Could not add user %s. Error setting password %s\n",
+ 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]);
const char *attrs[] = {"memberOf", NULL};
char *searchstring=NULL;
char **grouplist;
- char *escaped_user = escape_ldap_string_alloc(argv[0]);
+ char *escaped_user;
- if (argc < 1) return net_ads_user_usage(argc, argv);
-
- if (!(ads = ads_startup())) return -1;
+ if (argc < 1) {
+ return net_ads_user_usage(argc, argv);
+ }
+
+ escaped_user = escape_ldap_string_alloc(argv[0]);
if (!escaped_user) {
- d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
+ d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
+ return -1;
+ }
+
+ if (!(ads = ads_startup())) {
+ SAFE_FREE(escaped_user);
return -1;
}
safe_free(searchstring);
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_search: %s\n", ads_errstr(rc));
+ d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ SAFE_FREE(escaped_user);
return -1;
}
- grouplist = ldap_get_values(ads->ld, res, "memberOf");
+ grouplist = ldap_get_values((LDAP *)ads->ld,
+ (LDAPMessage *)res, "memberOf");
if (grouplist) {
int i;
}
ads_msgfree(ads, res);
-
ads_destroy(&ads);
+ SAFE_FREE(escaped_user);
return 0;
}
void *res;
char *userdn;
- if (argc < 1) return net_ads_user_usage(argc, argv);
+ if (argc < 1) {
+ return net_ads_user_usage(argc, argv);
+ }
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
rc = ads_find_user_acct(ads, &res, argv[0]);
if (!ADS_ERR_OK(rc)) {
DEBUG(0, ("User %s does not exist\n", argv[0]));
+ ads_destroy(&ads);
return -1;
}
userdn = ads_get_dn(ads, res);
ads_memfree(ads, userdn);
if (!ADS_ERR_OK(rc)) {
d_printf("User %s deleted\n", argv[0]);
+ ads_destroy(&ads);
return 0;
}
- d_printf("Error deleting user %s: %s\n", argv[0],
+ d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
ads_errstr(rc));
+ ads_destroy(&ads);
return -1;
}
char *disp_fields[2] = {NULL, NULL};
if (argc == 0) {
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
if (opt_long_list_entries)
d_printf("\nUser name Comment"\
rc = ads_do_search_all_fn(ads, ads->config.bind_path,
LDAP_SCOPE_SUBTREE,
- "(objectclass=user)",
+ "(objectCategory=user)",
opt_long_list_entries ? longattrs :
shortattrs, usergrp_display,
disp_fields);
ads_destroy(&ads);
- return 0;
+ return ADS_ERR_OK(rc) ? 0 : -1;
}
return net_run_function(argc, argv, func, net_ads_user_usage);
void *res=NULL;
int rc = -1;
- if (argc < 1) return net_ads_group_usage(argc, argv);
+ if (argc < 1) {
+ return net_ads_group_usage(argc, argv);
+ }
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
status = ads_find_user_acct(ads, &res, argv[0]);
if (!ADS_ERR_OK(status)) {
- d_printf("ads_group_add: %s\n", ads_errstr(status));
+ d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
goto done;
}
if (ads_count_replies(ads, res)) {
- d_printf("ads_group_add: Group %s already exists\n", argv[0]);
+ d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
ads_msgfree(ads, res);
goto done;
}
+ if (opt_container == NULL) {
+ opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
+ }
+
status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
if (ADS_ERR_OK(status)) {
d_printf("Group %s added\n", argv[0]);
rc = 0;
} else {
- d_printf("Could not add group %s: %s\n", argv[0],
+ d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
ads_errstr(status));
}
void *res;
char *groupdn;
- if (argc < 1) return net_ads_group_usage(argc, argv);
+ if (argc < 1) {
+ return net_ads_group_usage(argc, argv);
+ }
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
rc = ads_find_user_acct(ads, &res, argv[0]);
if (!ADS_ERR_OK(rc)) {
DEBUG(0, ("Group %s does not exist\n", argv[0]));
+ ads_destroy(&ads);
return -1;
}
groupdn = ads_get_dn(ads, res);
ads_memfree(ads, groupdn);
if (!ADS_ERR_OK(rc)) {
d_printf("Group %s deleted\n", argv[0]);
+ ads_destroy(&ads);
return 0;
}
- d_printf("Error deleting group %s: %s\n", argv[0],
+ d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
ads_errstr(rc));
+ ads_destroy(&ads);
return -1;
}
char *disp_fields[2] = {NULL, NULL};
if (argc == 0) {
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
if (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,
- "(objectclass=group)",
+ "(objectCategory=group)",
opt_long_list_entries ? longattrs :
shortattrs, usergrp_display,
disp_fields);
ads_destroy(&ads);
- return 0;
+ return ADS_ERR_OK(rc) ? 0 : -1;
}
return net_run_function(argc, argv, func, net_ads_group_usage);
}
ADS_STATUS rc;
void *res;
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
rc = ads_find_machine_acct(ads, &res, global_myname());
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_find_machine_acct: %s\n", ads_errstr(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_printf("No machine account for '%s' found\n", global_myname());
+ d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
+ 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(int argc, const char **argv)
{
ADS_STRUCT *ads = NULL;
- ADS_STATUS rc;
+ int ret = -1;
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *ctx;
+ DOM_SID *dom_sid = NULL;
if (!secrets_init()) {
DEBUG(1,("Failed to initialise secrets database\n"));
return -1;
}
- if (!opt_password) {
- net_use_machine_password();
+ if (!(ctx = talloc_init("net_ads_leave"))) {
+ DEBUG(0, ("Could not initialise talloc context\n"));
+ return -1;
}
+ /* The finds a DC and takes care of getting the
+ user creds if necessary */
+
if (!(ads = ads_startup())) {
return -1;
}
- rc = ads_leave_realm(ads, global_myname());
- if (!ADS_ERR_OK(rc)) {
- d_printf("Failed to delete host '%s' from the '%s' realm.\n",
- global_myname(), ads->config.realm);
- return -1;
+ /* make RPC calls here */
+
+ if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
+ ads->config.ldap_server_name)) )
+ {
+ goto done;
}
+
+ saf_store( cli->server_domain, cli->desthost );
- d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
+ if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
+ goto done;
+ }
- return 0;
+ if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
+ d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
+ global_myname(), ads->config.realm);
+ goto done;
+ }
+
+ d_printf("Disabled account for '%s' in realm '%s'\n",
+ global_myname(), ads->config.realm);
+
+ ret = 0;
+
+done:
+ if ( cli )
+ cli_shutdown(cli);
+
+ ads_destroy(&ads);
+ TALLOC_FREE( ctx );
+
+ return ret;
}
static int net_ads_join_ok(void)
return 0;
}
-/*
- join a domain using ADS
- */
-int net_ads_join(int argc, const char **argv)
+/*******************************************************************
+ Simple configu checks before beginning the join
+ ********************************************************************/
+
+static int check_ads_config( void )
{
- ADS_STRUCT *ads;
- ADS_STATUS rc;
- char *password;
- char *machine_account = NULL;
- char *tmp_password;
- const char *org_unit = "Computers";
- char *dn;
- void *res;
- DOM_SID dom_sid;
- char *ou_str;
- uint32 sec_channel_type = SEC_CHAN_WKSTA;
- uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT;
- const char *short_domain_name = NULL;
- TALLOC_CTX *ctx = NULL;
+ if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+ d_printf("Host is not configured as a member server.\n");
+ return -1;
+ }
- if (argc > 0) org_unit = argv[0];
+ if (strlen(global_myname()) > 15) {
+ d_printf("Our netbios name can be at most 15 chars long, "
+ "\"%s\" is %u chars long\n",
+ global_myname(), (unsigned int)strlen(global_myname()));
+ return -1;
+ }
+
+ if ( lp_security() == SEC_ADS && !*lp_realm()) {
+ d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
+ "join to succeed.\n");
+ return -1;
+ }
if (!secrets_init()) {
DEBUG(1,("Failed to initialise secrets database\n"));
return -1;
}
+
+ return 0;
+}
- tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
- password = strdup(tmp_password);
+/*******************************************************************
+ Do the domain join
+ ********************************************************************/
- if (!(ads = ads_startup())) return -1;
+static int net_join_domain( TALLOC_CTX *ctx, const char *servername,
+ struct in_addr *ip, DOM_SID **dom_sid, const char *password )
+{
+ int ret = -1;
+ struct cli_state *cli = NULL;
- if (!*lp_realm()) {
- d_printf("realm must be set in in smb.conf for ADS join to succeed.\n");
- return -1;
+ if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
+ goto done;
+
+ saf_store( cli->server_domain, cli->desthost );
+
+ if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
+ goto done;
+
+ if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid,
+ password, ND_TYPE_AD )) )
+ {
+ goto done;
}
+
+ ret = 0;
- if (strcmp(ads->config.realm, lp_realm()) != 0) {
- d_printf("realm of remote server (%s) and realm in smb.conf (%s) DO NOT match. Aborting join\n", ads->config.realm, lp_realm());
- return -1;
+done:
+ if ( cli )
+ cli_shutdown(cli);
+
+ return ret;
+}
+
+/*******************************************************************
+ Set a machines dNSHostName and servicePrincipalName attributes
+ ********************************************************************/
+
+static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
+{
+ ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
+ char *host_upn, *new_dn;
+ ADS_MODLIST mods;
+ const char *servicePrincipalName[3] = {NULL, NULL, NULL};
+ char *psp;
+ fstring my_fqdn;
+ LDAPMessage *res = NULL;
+ char *dn_string = NULL;
+ const char *machine_name = global_myname();
+ int count;
+
+ if ( !machine_name ) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ /* Find our DN */
+
+ status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
+ if (!ADS_ERR_OK(status))
+ return status;
+
+ if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
+ DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
+ DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
+ goto done;
}
+
+ new_dn = talloc_strdup(ctx, dn_string);
+ ads_memfree(ads_s, dn_string);
+ if (!new_dn) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ /* Windows only creates HOST/shortname & HOST/fqdn. We create
+ the UPN as well so that 'kinit -k' will work. You can only
+ request a TGT for entries with a UPN in AD. */
+
+ if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
+ goto done;
+ strupper_m(psp);
+ servicePrincipalName[0] = psp;
- ou_str = ads_ou_string(org_unit);
+ name_to_fqdn(my_fqdn, machine_name);
+ strlower_m(my_fqdn);
+ if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
+ goto done;
+ servicePrincipalName[1] = psp;
+
+ if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
+ goto done;
+
+ /* now do the mods */
+
+ if (!(mods = ads_init_mods(ctx))) {
+ goto done;
+ }
+
+ /* fields of primary importance */
+
+ ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
+ ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
+
+ status = ads_gen_mod(ads_s, new_dn, mods);
+
+done:
+ ads_msgfree(ads_s, res);
+
+ return status;
+}
+
+
+/*******************************************************************
+ join a domain using ADS (LDAP mods)
+ ********************************************************************/
+
+static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
+{
+ ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
+ char *dn, *ou_str;
+ LDAPMessage *res = NULL;
+
+ ou_str = ads_ou_string(ads, ou);
asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
free(ou_str);
rc = ads_search_dn(ads, &res, dn, NULL);
ads_msgfree(ads, res);
- if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
- d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n",
- org_unit, dn);
- return -1;
+ if (ADS_ERR_OK(rc)) {
+ /* Attempt to create the machine account and bail if this fails.
+ Assume that the admin wants exactly what they requested */
+
+ rc = ads_create_machine_acct( ads, global_myname(), dn );
+ if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
+ rc = ADS_SUCCESS;
+ }
}
- free(dn);
- if (!ADS_ERR_OK(rc)) {
- d_printf("ads_join_realm: %s\n", ads_errstr(rc));
- return -1;
- }
+ SAFE_FREE( dn );
- rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
- if (!ADS_ERR_OK(rc)) {
- d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+ return rc;
+}
+
+/*******************************************************************
+ join a domain using ADS (LDAP mods)
+ ********************************************************************/
+
+int net_ads_join(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ char *machine_account = NULL;
+ const char *short_domain_name = NULL;
+ char *tmp_password, *password;
+ struct cldap_netlogon_reply cldap_reply;
+ TALLOC_CTX *ctx;
+ DOM_SID *domain_sid = NULL;
+
+ if ( check_ads_config() != 0 ) {
+ d_fprintf(stderr, "Invalid configuration. Exiting....\n");
return -1;
}
- rc = ads_domain_sid(ads, &dom_sid);
- if (!ADS_ERR_OK(rc)) {
- d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
- return -1;
+ if ( (ads = ads_startup()) == NULL ) {
+ return -1;
}
- if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
- d_printf("asprintf failed\n");
+ if (strcmp(ads->config.realm, lp_realm()) != 0) {
+ d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
+ "(%s) DO NOT match. Aborting join\n", ads->config.realm,
+ lp_realm());
+ ads_destroy(&ads);
return -1;
}
- rc = ads_set_machine_password(ads, machine_account, password);
- if (!ADS_ERR_OK(rc)) {
- d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
+ if (!(ctx = talloc_init("net_ads_join"))) {
+ DEBUG(0, ("Could not initialise talloc context\n"));
return -1;
}
+
+ /* If we were given an OU, try to create the machine in the OU account
+ first and then do the normal RPC join */
+
+ if ( argc > 0 ) {
+ status = net_precreate_machine_acct( ads, argv[0] );
+ if ( !ADS_ERR_OK(status) ) {
+ d_fprintf( stderr, "Failed to pre-create the machine object "
+ "in OU %s.\n", argv[0]);
+ ads_destroy( &ads );
+ return -1;
+ }
+ }
+
+ /* Do the domain join here */
+
+ tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ password = talloc_strdup(ctx, tmp_password);
- /* make sure we get the right workgroup */
-
- if ( !(ctx = talloc_init("net ads join")) ) {
- d_printf("talloc_init() failed!\n");
+ if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
+ d_fprintf(stderr, "Failed to join domain!\n");
return -1;
}
- rc = ads_workgroup_name(ads, ctx, &short_domain_name);
- if ( ADS_ERR_OK(rc) ) {
+ /* Check the short name of the domain */
+
+ ZERO_STRUCT( cldap_reply );
+
+ if ( ads_cldap_netlogon( ads->config.ldap_server_name,
+ ads->server.realm, &cldap_reply ) )
+ {
+ short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
if ( !strequal(lp_workgroup(), short_domain_name) ) {
d_printf("The workgroup in smb.conf does not match the short\n");
d_printf("domain name obtained from the server.\n");
}
d_printf("Using short domain name -- %s\n", short_domain_name);
-
- /* HACK ALRET! Store the sid and password under bother the lp_workgroup()
+
+ /* HACK ALERT! Store the sid and password under both the lp_workgroup()
value from smb.conf and the string returned from the server. The former is
neede to bootstrap winbindd's first connection to the DC to get the real
short domain name --jerry */
-
- if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
- DEBUG(1,("Failed to save domain sid\n"));
+
+ if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
+ || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
+ {
+ ads_destroy(&ads);
return -1;
}
- if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
- DEBUG(1,("Failed to save machine password\n"));
+ /* Verify that everything is ok */
+
+ if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
+ d_fprintf(stderr, "Failed to verify membership in domain!\n");
+ return -1;
+ }
+
+ /* create the dNSHostName & servicePrincipalName values */
+
+ status = net_set_machine_spn( ctx, ads );
+ if ( !ADS_ERR_OK(status) ) {
+ d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
+ d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
+ d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
+
+ /* don't fail */
+ }
+
+#if defined(HAVE_KRB5)
+ if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
+ d_fprintf(stderr, "asprintf failed\n");
+ ads_destroy(&ads);
return -1;
}
- if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
- DEBUG(1,("Failed to save domain sid\n"));
+ if (!kerberos_derive_salting_principal(machine_account)) {
+ DEBUG(1,("Failed to determine salting principal\n"));
+ ads_destroy(&ads);
return -1;
}
- if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
- DEBUG(1,("Failed to save machine password\n"));
+ if (!kerberos_derive_cifs_salting_principals()) {
+ DEBUG(1,("Failed to determine salting principals\n"));
+ ads_destroy(&ads);
return -1;
}
if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
DEBUG(1,("Error creating host keytab!\n"));
}
+#endif
d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
- SAFE_FREE(password);
SAFE_FREE(machine_account);
- if ( ctx ) {
- talloc_destroy(ctx);
- }
+ TALLOC_FREE( ctx );
+ ads_destroy(&ads);
+
return 0;
}
+/*******************************************************************
+ ********************************************************************/
+
int net_ads_printer_usage(int argc, const char **argv)
{
d_printf(
"\nnet ads printer search <printer>"
-"\n\tsearch for a printer in the directory"
+"\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"
return -1;
}
+/*******************************************************************
+ ********************************************************************/
+
static int net_ads_printer_search(int argc, const char **argv)
{
ADS_STRUCT *ads;
ADS_STATUS rc;
void *res = NULL;
- if (!(ads = ads_startup()))
+ if (!(ads = ads_startup())) {
return -1;
+ }
rc = ads_find_printers(ads, &res);
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_find_printer: %s\n", ads_errstr(rc));
+ d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
ads_msgfree(ads, res);
- return -1;
+ ads_destroy(&ads);
+ return -1;
}
if (ads_count_replies(ads, res) == 0) {
- d_printf("No results found\n");
+ 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;
}
const char *servername, *printername;
void *res = NULL;
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
- if (argc > 0)
+ if (argc > 0) {
printername = argv[0];
- else
+ } else {
printername = "*";
+ }
- if (argc > 1)
+ if (argc > 1) {
servername = argv[1];
- else
+ } else {
servername = global_myname();
+ }
rc = ads_find_printer_on_server(ads, &res, printername, servername);
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_find_printer_on_server: %s\n", ads_errstr(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_printf("Printer '%s' not found\n", printername);
+ 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;
}
-void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+void do_drv_upgrade_printer(int msg_type, struct process_id src,
+ void *buf, size_t len)
{
return;
}
ADS_STATUS rc;
const char *servername, *printername;
struct cli_state *cli;
+ struct rpc_pipe_client *pipe_hnd;
struct in_addr server_ip;
NTSTATUS nt_status;
TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
char *prt_dn, *srv_dn, **srv_cn;
void *res = NULL;
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
- if (argc < 1)
+ if (argc < 1) {
return net_ads_printer_usage(argc, argv);
+ }
printername = argv[0];
- if (argc == 2)
+ if (argc == 2) {
servername = argv[1];
- else
+ } else {
servername = global_myname();
+ }
/* Get printer data from SPOOLSS */
Undefined, NULL);
if (NT_STATUS_IS_ERR(nt_status)) {
- d_printf("Unable to open a connnection to %s to obtain data "
+ d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
"for %s\n", servername, printername);
+ ads_destroy(&ads);
return -1;
}
ads_find_machine_acct(ads, &res, servername);
if (ads_count_replies(ads, res) == 0) {
- d_printf("Could not find machine account for server %s\n",
+ d_fprintf(stderr, "Could not find machine account for server %s\n",
servername);
+ ads_destroy(&ads);
return -1;
}
- srv_dn = ldap_get_dn(ads->ld, res);
+ srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
srv_cn = ldap_explode_dn(srv_dn, 1);
asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
- cli_nt_session_open(cli, PI_SPOOLSS);
- get_remote_printer_publishing_data(cli, mem_ctx, &mods, printername);
+ pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
+ if (!pipe_hnd) {
+ d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
+ servername);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
+ printername);
rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
+ d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
return -1;
}
d_printf("published printer\n");
+ ads_destroy(&ads);
return 0;
}
char *prt_dn;
void *res = NULL;
- if (!(ads = ads_startup())) return -1;
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
- if (argc < 1)
+ if (argc < 1) {
return net_ads_printer_usage(argc, argv);
+ }
- if (argc > 1)
+ if (argc > 1) {
servername = argv[1];
- else
+ } else {
servername = global_myname();
+ }
rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_find_printer_on_server: %s\n", ads_errstr(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_printf("Printer '%s' not found\n", argv[1]);
+ d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
ads_msgfree(ads, res);
+ ads_destroy(&ads);
return -1;
}
ads_memfree(ads, prt_dn);
if (!ADS_ERR_OK(rc)) {
- d_printf("ads_del_dn: %s\n", ads_errstr(rc));
+ d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
return -1;
}
+ ads_destroy(&ads);
return 0;
}
ADS_STATUS ret;
if (opt_user_name == NULL || opt_password == NULL) {
- d_printf("You must supply an administrator username/password\n");
+ d_fprintf(stderr, "You must supply an administrator username/password\n");
return -1;
}
if (argc < 1) {
- d_printf("ERROR: You must say which username to change password for\n");
+ d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
return -1;
}
}
use_in_memory_ccache();
- c = strchr(auth_principal, '@');
+ c = strchr_m(auth_principal, '@');
if (c) {
realm = ++c;
} else {
/* use the realm so we can eventually change passwords for users
in realms other than default */
- if (!(ads = ads_init(realm, NULL, NULL))) {
+ if (!(ads = ads_init(realm, opt_workgroup, NULL))) {
return -1;
}
ads_connect(ads);
if (!ads || !ads->config.realm) {
- d_printf("Didn't find the kerberos server!\n");
+ d_fprintf(stderr, "Didn't find the kerberos server!\n");
return -1;
}
ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
auth_password, user, new_password, ads->auth.time_offset);
if (!ADS_ERR_OK(ret)) {
- d_printf("Password change failed :-( ...\n");
+ d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
ads_destroy(&ads);
return -1;
}
{
ADS_STRUCT *ads;
char *host_principal;
- fstring my_fqdn;
+ fstring my_name;
ADS_STATUS ret;
if (!secrets_init()) {
return -1;
}
- name_to_fqdn(my_fqdn, global_myname());
- strlower_m(my_fqdn);
- asprintf(&host_principal, "%s@%s", my_fqdn, ads->config.realm);
- d_printf("Changing password for principal: HOST/%s\n", host_principal);
+ fstrcpy(my_name, global_myname());
+ strlower_m(my_name);
+ asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
+ 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_printf("Password change failed :-( ...\n");
+ 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 HOST/%s succeeded.\n", host_principal);
+ d_printf("Password change for principal %s succeeded.\n", host_principal);
if (lp_use_kerberos_keytab()) {
d_printf("Attempting to update system keytab with new password.\n");
LDAP_SCOPE_SUBTREE,
ldap_exp, attrs, &res);
if (!ADS_ERR_OK(rc)) {
- d_printf("search failed: %s\n", ads_errstr(rc));
+ d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
return -1;
}
LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, &res);
if (!ADS_ERR_OK(rc)) {
- d_printf("search failed: %s\n", ads_errstr(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(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(argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_sid(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *sid_string;
+ const char **attrs;
+ void *res = NULL;
+ DOM_SID sid;
+
+ if (argc < 1) {
+ return net_ads_sid_usage(argc, argv);
+ }
+
+ if (!(ads = ads_startup())) {
+ 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;
}
return 0;
}
+
static int net_ads_keytab_usage(int argc, const char **argv)
{
d_printf(
#if 0
{"INFO", net_ads_info},
{"JOIN", net_ads_join},
+ {"JOIN2", net_ads_join2},
{"LEAVE", net_ads_leave},
{"STATUS", net_ads_status},
{"PASSWORD", net_ads_password},
{"PRINTER", net_ads_printer},
{"SEARCH", net_ads_search},
{"DN", net_ads_dn},
+ {"SID", net_ads_sid},
{"WORKGROUP", net_ads_workgroup},
{"LOOKUP", net_ads_lookup},
{"KEYTAB", net_ads_keytab},
#else
-static int net_ads_noads(int argc, const char **argv)
+static int net_ads_noads(void)
{
- d_printf("ADS support not compiled in\n");
+ d_fprintf(stderr, "ADS support not compiled in\n");
return -1;
}