| 1 | /*
|
|---|
| 2 | Samba Unix/Linux SMB client library
|
|---|
| 3 | net ads commands
|
|---|
| 4 | Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
|
|---|
| 5 | Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
|
|---|
| 6 | Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
|
|---|
| 7 | Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
|
|---|
| 8 |
|
|---|
| 9 | This program is free software; you can redistribute it and/or modify
|
|---|
| 10 | it under the terms of the GNU General Public License as published by
|
|---|
| 11 | the Free Software Foundation; either version 2 of the License, or
|
|---|
| 12 | (at your option) any later version.
|
|---|
| 13 |
|
|---|
| 14 | This program is distributed in the hope that it will be useful,
|
|---|
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 17 | GNU General Public License for more details.
|
|---|
| 18 |
|
|---|
| 19 | You should have received a copy of the GNU General Public License
|
|---|
| 20 | along with this program; if not, write to the Free Software
|
|---|
| 21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|---|
| 22 | */
|
|---|
| 23 |
|
|---|
| 24 | #include "includes.h"
|
|---|
| 25 | #include "utils/net.h"
|
|---|
| 26 |
|
|---|
| 27 | #ifdef HAVE_ADS
|
|---|
| 28 |
|
|---|
| 29 | int net_ads_usage(int argc, const char **argv)
|
|---|
| 30 | {
|
|---|
| 31 | d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
|
|---|
| 32 | d_printf(" Join the local machine to a ADS realm\n");
|
|---|
| 33 | d_printf("leave\n");
|
|---|
| 34 | d_printf(" Remove the local machine from a ADS realm\n");
|
|---|
| 35 | d_printf("testjoin\n");
|
|---|
| 36 | d_printf(" Validates the machine account in the domain\n");
|
|---|
| 37 | d_printf("user\n");
|
|---|
| 38 | d_printf(" List, add, or delete users in the realm\n");
|
|---|
| 39 | d_printf("group\n");
|
|---|
| 40 | d_printf(" List, add, or delete groups in the realm\n");
|
|---|
| 41 | d_printf("info\n");
|
|---|
| 42 | d_printf(" Displays details regarding a specific AD server\n");
|
|---|
| 43 | d_printf("status\n");
|
|---|
| 44 | d_printf(" Display details regarding the machine's account in AD\n");
|
|---|
| 45 | d_printf("lookup\n");
|
|---|
| 46 | d_printf(" Performs CLDAP query of AD domain controllers\n");
|
|---|
| 47 | d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
|
|---|
| 48 | d_printf(" Change a user's password using an admin account\n");
|
|---|
| 49 | d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
|
|---|
| 50 | d_printf("changetrustpw\n");
|
|---|
| 51 | d_printf(" Change the trust account password of this machine in the AD tree\n");
|
|---|
| 52 | d_printf("printer [info | publish | remove] <printername> <servername>\n");
|
|---|
| 53 | d_printf(" Lookup, add, or remove directory entry for a printer\n");
|
|---|
| 54 | d_printf("{search,dn,sid}\n");
|
|---|
| 55 | d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
|
|---|
| 56 | d_printf("keytab\n");
|
|---|
| 57 | d_printf(" Manage a local keytab file based on the machine account in AD\n");
|
|---|
| 58 | d_printf("dns\n");
|
|---|
| 59 | d_printf(" Issue a dynamic DNS update request the server's hostname\n");
|
|---|
| 60 | d_printf(" (using the machine credentials)\n");
|
|---|
| 61 |
|
|---|
| 62 | return -1;
|
|---|
| 63 | }
|
|---|
| 64 |
|
|---|
| 65 | /* when we do not have sufficient input parameters to contact a remote domain
|
|---|
| 66 | * we always fall back to our own realm - Guenther*/
|
|---|
| 67 |
|
|---|
| 68 | static const char *assume_own_realm(void)
|
|---|
| 69 | {
|
|---|
| 70 | if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
|
|---|
| 71 | return lp_realm();
|
|---|
| 72 | }
|
|---|
| 73 |
|
|---|
| 74 | return NULL;
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| 77 | /*
|
|---|
| 78 | do a cldap netlogon query
|
|---|
| 79 | */
|
|---|
| 80 | static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
|
|---|
| 81 | {
|
|---|
| 82 | struct cldap_netlogon_reply reply;
|
|---|
| 83 |
|
|---|
| 84 | if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
|
|---|
| 85 | d_fprintf(stderr, "CLDAP query failed!\n");
|
|---|
| 86 | return -1;
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 | d_printf("Information for Domain Controller: %s\n\n",
|
|---|
| 90 | inet_ntoa(ads->ldap_ip));
|
|---|
| 91 |
|
|---|
| 92 | d_printf("Response Type: ");
|
|---|
| 93 | switch (reply.type) {
|
|---|
| 94 | case SAMLOGON_AD_UNK_R:
|
|---|
| 95 | d_printf("SAMLOGON\n");
|
|---|
| 96 | break;
|
|---|
| 97 | case SAMLOGON_AD_R:
|
|---|
| 98 | d_printf("SAMLOGON_USER\n");
|
|---|
| 99 | break;
|
|---|
| 100 | default:
|
|---|
| 101 | d_printf("0x%x\n", reply.type);
|
|---|
| 102 | break;
|
|---|
| 103 | }
|
|---|
| 104 | d_printf("GUID: %s\n",
|
|---|
| 105 | smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
|
|---|
| 106 | d_printf("Flags:\n"
|
|---|
| 107 | "\tIs a PDC: %s\n"
|
|---|
| 108 | "\tIs a GC of the forest: %s\n"
|
|---|
| 109 | "\tIs an LDAP server: %s\n"
|
|---|
| 110 | "\tSupports DS: %s\n"
|
|---|
| 111 | "\tIs running a KDC: %s\n"
|
|---|
| 112 | "\tIs running time services: %s\n"
|
|---|
| 113 | "\tIs the closest DC: %s\n"
|
|---|
| 114 | "\tIs writable: %s\n"
|
|---|
| 115 | "\tHas a hardware clock: %s\n"
|
|---|
| 116 | "\tIs a non-domain NC serviced by LDAP server: %s\n",
|
|---|
| 117 | (reply.flags & ADS_PDC) ? "yes" : "no",
|
|---|
| 118 | (reply.flags & ADS_GC) ? "yes" : "no",
|
|---|
| 119 | (reply.flags & ADS_LDAP) ? "yes" : "no",
|
|---|
| 120 | (reply.flags & ADS_DS) ? "yes" : "no",
|
|---|
| 121 | (reply.flags & ADS_KDC) ? "yes" : "no",
|
|---|
| 122 | (reply.flags & ADS_TIMESERV) ? "yes" : "no",
|
|---|
| 123 | (reply.flags & ADS_CLOSEST) ? "yes" : "no",
|
|---|
| 124 | (reply.flags & ADS_WRITABLE) ? "yes" : "no",
|
|---|
| 125 | (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
|
|---|
| 126 | (reply.flags & ADS_NDNC) ? "yes" : "no");
|
|---|
| 127 |
|
|---|
| 128 | printf("Forest:\t\t\t%s\n", reply.forest);
|
|---|
| 129 | printf("Domain:\t\t\t%s\n", reply.domain);
|
|---|
| 130 | printf("Domain Controller:\t%s\n", reply.hostname);
|
|---|
| 131 |
|
|---|
| 132 | printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
|
|---|
| 133 | printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
|
|---|
| 134 |
|
|---|
| 135 | if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
|
|---|
| 136 | if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
|
|---|
| 137 |
|
|---|
| 138 | printf("Server Site Name :\t\t%s\n", reply.server_site_name);
|
|---|
| 139 | printf("Client Site Name :\t\t%s\n", reply.client_site_name);
|
|---|
| 140 |
|
|---|
| 141 | d_printf("NT Version: %d\n", reply.version);
|
|---|
| 142 | d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
|
|---|
| 143 | d_printf("LM20 Token: %.2x\n", reply.lm20_token);
|
|---|
| 144 |
|
|---|
| 145 | return 0;
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 |
|
|---|
| 149 | /*
|
|---|
| 150 | this implements the CLDAP based netlogon lookup requests
|
|---|
| 151 | for finding the domain controller of a ADS domain
|
|---|
| 152 | */
|
|---|
| 153 | static int net_ads_lookup(int argc, const char **argv)
|
|---|
| 154 | {
|
|---|
| 155 | ADS_STRUCT *ads;
|
|---|
| 156 |
|
|---|
| 157 | if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
|
|---|
| 158 | d_fprintf(stderr, "Didn't find the cldap server!\n");
|
|---|
| 159 | return -1;
|
|---|
| 160 | }
|
|---|
| 161 |
|
|---|
| 162 | if (!ads->config.realm) {
|
|---|
| 163 | ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
|
|---|
| 164 | ads->ldap_port = 389;
|
|---|
| 165 | }
|
|---|
| 166 |
|
|---|
| 167 | return net_ads_cldap_netlogon(ads);
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 |
|
|---|
| 171 |
|
|---|
| 172 | static int net_ads_info(int argc, const char **argv)
|
|---|
| 173 | {
|
|---|
| 174 | ADS_STRUCT *ads;
|
|---|
| 175 |
|
|---|
| 176 | if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
|
|---|
| 177 | d_fprintf(stderr, "Didn't find the ldap server!\n");
|
|---|
| 178 | return -1;
|
|---|
| 179 | }
|
|---|
| 180 |
|
|---|
| 181 | if (!ads || !ads->config.realm) {
|
|---|
| 182 | d_fprintf(stderr, "Didn't find the ldap server!\n");
|
|---|
| 183 | return -1;
|
|---|
| 184 | }
|
|---|
| 185 |
|
|---|
| 186 | /* Try to set the server's current time since we didn't do a full
|
|---|
| 187 | TCP LDAP session initially */
|
|---|
| 188 |
|
|---|
| 189 | if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
|
|---|
| 190 | d_fprintf( stderr, "Failed to get server's current time!\n");
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| 193 | d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
|
|---|
| 194 | d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
|
|---|
| 195 | d_printf("Realm: %s\n", ads->config.realm);
|
|---|
| 196 | d_printf("Bind Path: %s\n", ads->config.bind_path);
|
|---|
| 197 | d_printf("LDAP port: %d\n", ads->ldap_port);
|
|---|
| 198 | d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
|
|---|
| 199 |
|
|---|
| 200 | d_printf("KDC server: %s\n", ads->auth.kdc_server );
|
|---|
| 201 | d_printf("Server time offset: %d\n", ads->auth.time_offset );
|
|---|
| 202 |
|
|---|
| 203 | return 0;
|
|---|
| 204 | }
|
|---|
| 205 |
|
|---|
| 206 | static void use_in_memory_ccache(void) {
|
|---|
| 207 | /* Use in-memory credentials cache so we do not interfere with
|
|---|
| 208 | * existing credentials */
|
|---|
| 209 | setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
|
|---|
| 210 | }
|
|---|
| 211 |
|
|---|
| 212 | static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
|
|---|
| 213 | {
|
|---|
| 214 | ADS_STRUCT *ads = NULL;
|
|---|
| 215 | ADS_STATUS status;
|
|---|
| 216 | BOOL need_password = False;
|
|---|
| 217 | BOOL second_time = False;
|
|---|
| 218 | char *cp;
|
|---|
| 219 | const char *realm = NULL;
|
|---|
| 220 | BOOL tried_closest_dc = False;
|
|---|
| 221 |
|
|---|
| 222 | /* lp_realm() should be handled by a command line param,
|
|---|
| 223 | However, the join requires that realm be set in smb.conf
|
|---|
| 224 | and compares our realm with the remote server's so this is
|
|---|
| 225 | ok until someone needs more flexibility */
|
|---|
| 226 |
|
|---|
| 227 | *ads_ret = NULL;
|
|---|
| 228 |
|
|---|
| 229 | retry_connect:
|
|---|
| 230 | if (only_own_domain) {
|
|---|
| 231 | realm = lp_realm();
|
|---|
| 232 | } else {
|
|---|
| 233 | realm = assume_own_realm();
|
|---|
| 234 | }
|
|---|
| 235 |
|
|---|
| 236 | ads = ads_init(realm, opt_target_workgroup, opt_host);
|
|---|
| 237 |
|
|---|
| 238 | if (!opt_user_name) {
|
|---|
| 239 | opt_user_name = "administrator";
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | if (opt_user_specified) {
|
|---|
| 243 | need_password = True;
|
|---|
| 244 | }
|
|---|
| 245 |
|
|---|
| 246 | retry:
|
|---|
| 247 | if (!opt_password && need_password && !opt_machine_pass) {
|
|---|
| 248 | char *prompt = NULL;
|
|---|
| 249 | asprintf(&prompt,"%s's password: ", opt_user_name);
|
|---|
| 250 | if (!prompt) {
|
|---|
| 251 | ads_destroy(&ads);
|
|---|
| 252 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 253 | }
|
|---|
| 254 | opt_password = getpass(prompt);
|
|---|
| 255 | free(prompt);
|
|---|
| 256 | }
|
|---|
| 257 |
|
|---|
| 258 | if (opt_password) {
|
|---|
| 259 | use_in_memory_ccache();
|
|---|
| 260 | SAFE_FREE(ads->auth.password);
|
|---|
| 261 | ads->auth.password = smb_xstrdup(opt_password);
|
|---|
| 262 | }
|
|---|
| 263 |
|
|---|
| 264 | ads->auth.flags |= auth_flags;
|
|---|
| 265 | SAFE_FREE(ads->auth.user_name);
|
|---|
| 266 | ads->auth.user_name = smb_xstrdup(opt_user_name);
|
|---|
| 267 |
|
|---|
| 268 | /*
|
|---|
| 269 | * If the username is of the form "name@realm",
|
|---|
| 270 | * extract the realm and convert to upper case.
|
|---|
| 271 | * This is only used to establish the connection.
|
|---|
| 272 | */
|
|---|
| 273 | if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
|
|---|
| 274 | *cp++ = '\0';
|
|---|
| 275 | SAFE_FREE(ads->auth.realm);
|
|---|
| 276 | ads->auth.realm = smb_xstrdup(cp);
|
|---|
| 277 | strupper_m(ads->auth.realm);
|
|---|
| 278 | }
|
|---|
| 279 |
|
|---|
| 280 | status = ads_connect(ads);
|
|---|
| 281 |
|
|---|
| 282 | if (!ADS_ERR_OK(status)) {
|
|---|
| 283 |
|
|---|
| 284 | if (NT_STATUS_EQUAL(ads_ntstatus(status),
|
|---|
| 285 | NT_STATUS_NO_LOGON_SERVERS)) {
|
|---|
| 286 | DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
|
|---|
| 287 | ads_destroy(&ads);
|
|---|
| 288 | return status;
|
|---|
| 289 | }
|
|---|
| 290 |
|
|---|
| 291 | if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
|
|---|
| 292 | need_password = True;
|
|---|
| 293 | second_time = True;
|
|---|
| 294 | goto retry;
|
|---|
| 295 | } else {
|
|---|
| 296 | ads_destroy(&ads);
|
|---|
| 297 | return status;
|
|---|
| 298 | }
|
|---|
| 299 | }
|
|---|
| 300 |
|
|---|
| 301 | /* when contacting our own domain, make sure we use the closest DC.
|
|---|
| 302 | * This is done by reconnecting to ADS because only the first call to
|
|---|
| 303 | * ads_connect will give us our own sitename */
|
|---|
| 304 |
|
|---|
| 305 | if ((only_own_domain || !opt_host) && !tried_closest_dc) {
|
|---|
| 306 |
|
|---|
| 307 | tried_closest_dc = True; /* avoid loop */
|
|---|
| 308 |
|
|---|
| 309 | if (!ads->config.tried_closest_dc) {
|
|---|
| 310 |
|
|---|
| 311 | namecache_delete(ads->server.realm, 0x1C);
|
|---|
| 312 | namecache_delete(ads->server.workgroup, 0x1C);
|
|---|
| 313 |
|
|---|
| 314 | ads_destroy(&ads);
|
|---|
| 315 | ads = NULL;
|
|---|
| 316 |
|
|---|
| 317 | goto retry_connect;
|
|---|
| 318 | }
|
|---|
| 319 | }
|
|---|
| 320 |
|
|---|
| 321 | *ads_ret = ads;
|
|---|
| 322 | return status;
|
|---|
| 323 | }
|
|---|
| 324 |
|
|---|
| 325 | ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
|
|---|
| 326 | {
|
|---|
| 327 | return ads_startup_int(only_own_domain, 0, ads);
|
|---|
| 328 | }
|
|---|
| 329 |
|
|---|
| 330 | ADS_STATUS ads_startup_nobind(BOOL only_own_domain, ADS_STRUCT **ads)
|
|---|
| 331 | {
|
|---|
| 332 | return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
|
|---|
| 333 | }
|
|---|
| 334 |
|
|---|
| 335 | /*
|
|---|
| 336 | Check to see if connection can be made via ads.
|
|---|
| 337 | ads_startup() stores the password in opt_password if it needs to so
|
|---|
| 338 | that rpc or rap can use it without re-prompting.
|
|---|
| 339 | */
|
|---|
| 340 | static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
|
|---|
| 341 | {
|
|---|
| 342 | ADS_STRUCT *ads;
|
|---|
| 343 | ADS_STATUS status;
|
|---|
| 344 |
|
|---|
| 345 | if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
|
|---|
| 346 | return -1;
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | ads->auth.flags |= ADS_AUTH_NO_BIND;
|
|---|
| 350 |
|
|---|
| 351 | status = ads_connect(ads);
|
|---|
| 352 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 353 | return -1;
|
|---|
| 354 | }
|
|---|
| 355 |
|
|---|
| 356 | ads_destroy(&ads);
|
|---|
| 357 | return 0;
|
|---|
| 358 | }
|
|---|
| 359 |
|
|---|
| 360 | int net_ads_check_our_domain(void)
|
|---|
| 361 | {
|
|---|
| 362 | return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
|
|---|
| 363 | }
|
|---|
| 364 |
|
|---|
| 365 | int net_ads_check(void)
|
|---|
| 366 | {
|
|---|
| 367 | return net_ads_check_int(NULL, opt_workgroup, opt_host);
|
|---|
| 368 | }
|
|---|
| 369 | /*
|
|---|
| 370 | determine the netbios workgroup name for a domain
|
|---|
| 371 | */
|
|---|
| 372 | static int net_ads_workgroup(int argc, const char **argv)
|
|---|
| 373 | {
|
|---|
| 374 | ADS_STRUCT *ads;
|
|---|
| 375 | struct cldap_netlogon_reply reply;
|
|---|
| 376 |
|
|---|
| 377 | if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
|
|---|
| 378 | d_fprintf(stderr, "Didn't find the cldap server!\n");
|
|---|
| 379 | return -1;
|
|---|
| 380 | }
|
|---|
| 381 |
|
|---|
| 382 | if (!ads->config.realm) {
|
|---|
| 383 | ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
|
|---|
| 384 | ads->ldap_port = 389;
|
|---|
| 385 | }
|
|---|
| 386 |
|
|---|
| 387 | if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
|
|---|
| 388 | d_fprintf(stderr, "CLDAP query failed!\n");
|
|---|
| 389 | return -1;
|
|---|
| 390 | }
|
|---|
| 391 |
|
|---|
| 392 | d_printf("Workgroup: %s\n", reply.netbios_domain);
|
|---|
| 393 |
|
|---|
| 394 | ads_destroy(&ads);
|
|---|
| 395 |
|
|---|
| 396 | return 0;
|
|---|
| 397 | }
|
|---|
| 398 |
|
|---|
| 399 |
|
|---|
| 400 |
|
|---|
| 401 | static BOOL usergrp_display(char *field, void **values, void *data_area)
|
|---|
| 402 | {
|
|---|
| 403 | char **disp_fields = (char **) data_area;
|
|---|
| 404 |
|
|---|
| 405 | if (!field) { /* must be end of record */
|
|---|
| 406 | if (disp_fields[0]) {
|
|---|
| 407 | if (!strchr_m(disp_fields[0], '$')) {
|
|---|
| 408 | if (disp_fields[1])
|
|---|
| 409 | d_printf("%-21.21s %s\n",
|
|---|
| 410 | disp_fields[0], disp_fields[1]);
|
|---|
| 411 | else
|
|---|
| 412 | d_printf("%s\n", disp_fields[0]);
|
|---|
| 413 | }
|
|---|
| 414 | }
|
|---|
| 415 | SAFE_FREE(disp_fields[0]);
|
|---|
| 416 | SAFE_FREE(disp_fields[1]);
|
|---|
| 417 | return True;
|
|---|
| 418 | }
|
|---|
| 419 | if (!values) /* must be new field, indicate string field */
|
|---|
| 420 | return True;
|
|---|
| 421 | if (StrCaseCmp(field, "sAMAccountName") == 0) {
|
|---|
| 422 | disp_fields[0] = SMB_STRDUP((char *) values[0]);
|
|---|
| 423 | }
|
|---|
| 424 | if (StrCaseCmp(field, "description") == 0)
|
|---|
| 425 | disp_fields[1] = SMB_STRDUP((char *) values[0]);
|
|---|
| 426 | return True;
|
|---|
| 427 | }
|
|---|
| 428 |
|
|---|
| 429 | static int net_ads_user_usage(int argc, const char **argv)
|
|---|
| 430 | {
|
|---|
| 431 | return net_help_user(argc, argv);
|
|---|
| 432 | }
|
|---|
| 433 |
|
|---|
| 434 | static int ads_user_add(int argc, const char **argv)
|
|---|
| 435 | {
|
|---|
| 436 | ADS_STRUCT *ads;
|
|---|
| 437 | ADS_STATUS status;
|
|---|
| 438 | char *upn, *userdn;
|
|---|
| 439 | LDAPMessage *res=NULL;
|
|---|
| 440 | int rc = -1;
|
|---|
| 441 | char *ou_str = NULL;
|
|---|
| 442 |
|
|---|
| 443 | if (argc < 1) return net_ads_user_usage(argc, argv);
|
|---|
| 444 |
|
|---|
| 445 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 446 | return -1;
|
|---|
| 447 | }
|
|---|
| 448 |
|
|---|
| 449 | status = ads_find_user_acct(ads, &res, argv[0]);
|
|---|
| 450 |
|
|---|
| 451 | if (!ADS_ERR_OK(status)) {
|
|---|
| 452 | d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
|
|---|
| 453 | goto done;
|
|---|
| 454 | }
|
|---|
| 455 |
|
|---|
| 456 | if (ads_count_replies(ads, res)) {
|
|---|
| 457 | d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
|
|---|
| 458 | goto done;
|
|---|
| 459 | }
|
|---|
| 460 |
|
|---|
| 461 | if (opt_container) {
|
|---|
| 462 | ou_str = SMB_STRDUP(opt_container);
|
|---|
| 463 | } else {
|
|---|
| 464 | ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
|
|---|
| 465 | }
|
|---|
| 466 |
|
|---|
| 467 | status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
|
|---|
| 468 |
|
|---|
| 469 | if (!ADS_ERR_OK(status)) {
|
|---|
| 470 | d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
|
|---|
| 471 | ads_errstr(status));
|
|---|
| 472 | goto done;
|
|---|
| 473 | }
|
|---|
| 474 |
|
|---|
| 475 | /* if no password is to be set, we're done */
|
|---|
| 476 | if (argc == 1) {
|
|---|
| 477 | d_printf("User %s added\n", argv[0]);
|
|---|
| 478 | rc = 0;
|
|---|
| 479 | goto done;
|
|---|
| 480 | }
|
|---|
| 481 |
|
|---|
| 482 | /* try setting the password */
|
|---|
| 483 | asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
|
|---|
| 484 | status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
|
|---|
| 485 | ads->auth.time_offset);
|
|---|
| 486 | safe_free(upn);
|
|---|
| 487 | if (ADS_ERR_OK(status)) {
|
|---|
| 488 | d_printf("User %s added\n", argv[0]);
|
|---|
| 489 | rc = 0;
|
|---|
| 490 | goto done;
|
|---|
| 491 | }
|
|---|
| 492 |
|
|---|
| 493 | /* password didn't set, delete account */
|
|---|
| 494 | d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
|
|---|
| 495 | argv[0], ads_errstr(status));
|
|---|
| 496 | ads_msgfree(ads, res);
|
|---|
| 497 | status=ads_find_user_acct(ads, &res, argv[0]);
|
|---|
| 498 | if (ADS_ERR_OK(status)) {
|
|---|
| 499 | userdn = ads_get_dn(ads, res);
|
|---|
| 500 | ads_del_dn(ads, userdn);
|
|---|
| 501 | ads_memfree(ads, userdn);
|
|---|
| 502 | }
|
|---|
| 503 |
|
|---|
| 504 | done:
|
|---|
| 505 | if (res)
|
|---|
| 506 | ads_msgfree(ads, res);
|
|---|
| 507 | ads_destroy(&ads);
|
|---|
| 508 | SAFE_FREE(ou_str);
|
|---|
| 509 | return rc;
|
|---|
| 510 | }
|
|---|
| 511 |
|
|---|
| 512 | static int ads_user_info(int argc, const char **argv)
|
|---|
| 513 | {
|
|---|
| 514 | ADS_STRUCT *ads;
|
|---|
| 515 | ADS_STATUS rc;
|
|---|
| 516 | LDAPMessage *res;
|
|---|
| 517 | const char *attrs[] = {"memberOf", NULL};
|
|---|
| 518 | char *searchstring=NULL;
|
|---|
| 519 | char **grouplist;
|
|---|
| 520 | char *escaped_user;
|
|---|
| 521 |
|
|---|
| 522 | if (argc < 1) {
|
|---|
| 523 | return net_ads_user_usage(argc, argv);
|
|---|
| 524 | }
|
|---|
| 525 |
|
|---|
| 526 | escaped_user = escape_ldap_string_alloc(argv[0]);
|
|---|
| 527 |
|
|---|
| 528 | if (!escaped_user) {
|
|---|
| 529 | d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
|
|---|
| 530 | return -1;
|
|---|
| 531 | }
|
|---|
| 532 |
|
|---|
| 533 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 534 | SAFE_FREE(escaped_user);
|
|---|
| 535 | return -1;
|
|---|
| 536 | }
|
|---|
| 537 |
|
|---|
| 538 | asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
|
|---|
| 539 | rc = ads_search(ads, &res, searchstring, attrs);
|
|---|
| 540 | safe_free(searchstring);
|
|---|
| 541 |
|
|---|
| 542 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 543 | d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
|
|---|
| 544 | ads_destroy(&ads);
|
|---|
| 545 | SAFE_FREE(escaped_user);
|
|---|
| 546 | return -1;
|
|---|
| 547 | }
|
|---|
| 548 |
|
|---|
| 549 | grouplist = ldap_get_values((LDAP *)ads->ld,
|
|---|
| 550 | (LDAPMessage *)res, "memberOf");
|
|---|
| 551 |
|
|---|
| 552 | if (grouplist) {
|
|---|
| 553 | int i;
|
|---|
| 554 | char **groupname;
|
|---|
| 555 | for (i=0;grouplist[i];i++) {
|
|---|
| 556 | groupname = ldap_explode_dn(grouplist[i], 1);
|
|---|
| 557 | d_printf("%s\n", groupname[0]);
|
|---|
| 558 | ldap_value_free(groupname);
|
|---|
| 559 | }
|
|---|
| 560 | ldap_value_free(grouplist);
|
|---|
| 561 | }
|
|---|
| 562 |
|
|---|
| 563 | ads_msgfree(ads, res);
|
|---|
| 564 | ads_destroy(&ads);
|
|---|
| 565 | SAFE_FREE(escaped_user);
|
|---|
| 566 | return 0;
|
|---|
| 567 | }
|
|---|
| 568 |
|
|---|
| 569 | static int ads_user_delete(int argc, const char **argv)
|
|---|
| 570 | {
|
|---|
| 571 | ADS_STRUCT *ads;
|
|---|
| 572 | ADS_STATUS rc;
|
|---|
| 573 | LDAPMessage *res = NULL;
|
|---|
| 574 | char *userdn;
|
|---|
| 575 |
|
|---|
| 576 | if (argc < 1) {
|
|---|
| 577 | return net_ads_user_usage(argc, argv);
|
|---|
| 578 | }
|
|---|
| 579 |
|
|---|
| 580 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 581 | return -1;
|
|---|
| 582 | }
|
|---|
| 583 |
|
|---|
| 584 | rc = ads_find_user_acct(ads, &res, argv[0]);
|
|---|
| 585 | if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
|
|---|
| 586 | d_printf("User %s does not exist.\n", argv[0]);
|
|---|
| 587 | ads_msgfree(ads, res);
|
|---|
| 588 | ads_destroy(&ads);
|
|---|
| 589 | return -1;
|
|---|
| 590 | }
|
|---|
| 591 | userdn = ads_get_dn(ads, res);
|
|---|
| 592 | ads_msgfree(ads, res);
|
|---|
| 593 | rc = ads_del_dn(ads, userdn);
|
|---|
| 594 | ads_memfree(ads, userdn);
|
|---|
| 595 | if (ADS_ERR_OK(rc)) {
|
|---|
| 596 | d_printf("User %s deleted\n", argv[0]);
|
|---|
| 597 | ads_destroy(&ads);
|
|---|
| 598 | return 0;
|
|---|
| 599 | }
|
|---|
| 600 | d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
|
|---|
| 601 | ads_errstr(rc));
|
|---|
| 602 | ads_destroy(&ads);
|
|---|
| 603 | return -1;
|
|---|
| 604 | }
|
|---|
| 605 |
|
|---|
| 606 | int net_ads_user(int argc, const char **argv)
|
|---|
| 607 | {
|
|---|
| 608 | struct functable func[] = {
|
|---|
| 609 | {"ADD", ads_user_add},
|
|---|
| 610 | {"INFO", ads_user_info},
|
|---|
| 611 | {"DELETE", ads_user_delete},
|
|---|
| 612 | {NULL, NULL}
|
|---|
| 613 | };
|
|---|
| 614 | ADS_STRUCT *ads;
|
|---|
| 615 | ADS_STATUS rc;
|
|---|
| 616 | const char *shortattrs[] = {"sAMAccountName", NULL};
|
|---|
| 617 | const char *longattrs[] = {"sAMAccountName", "description", NULL};
|
|---|
| 618 | char *disp_fields[2] = {NULL, NULL};
|
|---|
| 619 |
|
|---|
| 620 | if (argc == 0) {
|
|---|
| 621 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 622 | return -1;
|
|---|
| 623 | }
|
|---|
| 624 |
|
|---|
| 625 | if (opt_long_list_entries)
|
|---|
| 626 | d_printf("\nUser name Comment"\
|
|---|
| 627 | "\n-----------------------------\n");
|
|---|
| 628 |
|
|---|
| 629 | rc = ads_do_search_all_fn(ads, ads->config.bind_path,
|
|---|
| 630 | LDAP_SCOPE_SUBTREE,
|
|---|
| 631 | "(objectCategory=user)",
|
|---|
| 632 | opt_long_list_entries ? longattrs :
|
|---|
| 633 | shortattrs, usergrp_display,
|
|---|
| 634 | disp_fields);
|
|---|
| 635 | ads_destroy(&ads);
|
|---|
| 636 | return ADS_ERR_OK(rc) ? 0 : -1;
|
|---|
| 637 | }
|
|---|
| 638 |
|
|---|
| 639 | return net_run_function(argc, argv, func, net_ads_user_usage);
|
|---|
| 640 | }
|
|---|
| 641 |
|
|---|
| 642 | static int net_ads_group_usage(int argc, const char **argv)
|
|---|
| 643 | {
|
|---|
| 644 | return net_help_group(argc, argv);
|
|---|
| 645 | }
|
|---|
| 646 |
|
|---|
| 647 | static int ads_group_add(int argc, const char **argv)
|
|---|
| 648 | {
|
|---|
| 649 | ADS_STRUCT *ads;
|
|---|
| 650 | ADS_STATUS status;
|
|---|
| 651 | LDAPMessage *res=NULL;
|
|---|
| 652 | int rc = -1;
|
|---|
| 653 | char *ou_str = NULL;
|
|---|
| 654 |
|
|---|
| 655 | if (argc < 1) {
|
|---|
| 656 | return net_ads_group_usage(argc, argv);
|
|---|
| 657 | }
|
|---|
| 658 |
|
|---|
| 659 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 660 | return -1;
|
|---|
| 661 | }
|
|---|
| 662 |
|
|---|
| 663 | status = ads_find_user_acct(ads, &res, argv[0]);
|
|---|
| 664 |
|
|---|
| 665 | if (!ADS_ERR_OK(status)) {
|
|---|
| 666 | d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
|
|---|
| 667 | goto done;
|
|---|
| 668 | }
|
|---|
| 669 |
|
|---|
| 670 | if (ads_count_replies(ads, res)) {
|
|---|
| 671 | d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
|
|---|
| 672 | goto done;
|
|---|
| 673 | }
|
|---|
| 674 |
|
|---|
| 675 | if (opt_container) {
|
|---|
| 676 | ou_str = SMB_STRDUP(opt_container);
|
|---|
| 677 | } else {
|
|---|
| 678 | ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
|
|---|
| 679 | }
|
|---|
| 680 |
|
|---|
| 681 | status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
|
|---|
| 682 |
|
|---|
| 683 | if (ADS_ERR_OK(status)) {
|
|---|
| 684 | d_printf("Group %s added\n", argv[0]);
|
|---|
| 685 | rc = 0;
|
|---|
| 686 | } else {
|
|---|
| 687 | d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
|
|---|
| 688 | ads_errstr(status));
|
|---|
| 689 | }
|
|---|
| 690 |
|
|---|
| 691 | done:
|
|---|
| 692 | if (res)
|
|---|
| 693 | ads_msgfree(ads, res);
|
|---|
| 694 | ads_destroy(&ads);
|
|---|
| 695 | SAFE_FREE(ou_str);
|
|---|
| 696 | return rc;
|
|---|
| 697 | }
|
|---|
| 698 |
|
|---|
| 699 | static int ads_group_delete(int argc, const char **argv)
|
|---|
| 700 | {
|
|---|
| 701 | ADS_STRUCT *ads;
|
|---|
| 702 | ADS_STATUS rc;
|
|---|
| 703 | LDAPMessage *res = NULL;
|
|---|
| 704 | char *groupdn;
|
|---|
| 705 |
|
|---|
| 706 | if (argc < 1) {
|
|---|
| 707 | return net_ads_group_usage(argc, argv);
|
|---|
| 708 | }
|
|---|
| 709 |
|
|---|
| 710 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 711 | return -1;
|
|---|
| 712 | }
|
|---|
| 713 |
|
|---|
| 714 | rc = ads_find_user_acct(ads, &res, argv[0]);
|
|---|
| 715 | if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
|
|---|
| 716 | d_printf("Group %s does not exist.\n", argv[0]);
|
|---|
| 717 | ads_msgfree(ads, res);
|
|---|
| 718 | ads_destroy(&ads);
|
|---|
| 719 | return -1;
|
|---|
| 720 | }
|
|---|
| 721 | groupdn = ads_get_dn(ads, res);
|
|---|
| 722 | ads_msgfree(ads, res);
|
|---|
| 723 | rc = ads_del_dn(ads, groupdn);
|
|---|
| 724 | ads_memfree(ads, groupdn);
|
|---|
| 725 | if (ADS_ERR_OK(rc)) {
|
|---|
| 726 | d_printf("Group %s deleted\n", argv[0]);
|
|---|
| 727 | ads_destroy(&ads);
|
|---|
| 728 | return 0;
|
|---|
| 729 | }
|
|---|
| 730 | d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
|
|---|
| 731 | ads_errstr(rc));
|
|---|
| 732 | ads_destroy(&ads);
|
|---|
| 733 | return -1;
|
|---|
| 734 | }
|
|---|
| 735 |
|
|---|
| 736 | int net_ads_group(int argc, const char **argv)
|
|---|
| 737 | {
|
|---|
| 738 | struct functable func[] = {
|
|---|
| 739 | {"ADD", ads_group_add},
|
|---|
| 740 | {"DELETE", ads_group_delete},
|
|---|
| 741 | {NULL, NULL}
|
|---|
| 742 | };
|
|---|
| 743 | ADS_STRUCT *ads;
|
|---|
| 744 | ADS_STATUS rc;
|
|---|
| 745 | const char *shortattrs[] = {"sAMAccountName", NULL};
|
|---|
| 746 | const char *longattrs[] = {"sAMAccountName", "description", NULL};
|
|---|
| 747 | char *disp_fields[2] = {NULL, NULL};
|
|---|
| 748 |
|
|---|
| 749 | if (argc == 0) {
|
|---|
| 750 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 751 | return -1;
|
|---|
| 752 | }
|
|---|
| 753 |
|
|---|
| 754 | if (opt_long_list_entries)
|
|---|
| 755 | d_printf("\nGroup name Comment"\
|
|---|
| 756 | "\n-----------------------------\n");
|
|---|
| 757 | rc = ads_do_search_all_fn(ads, ads->config.bind_path,
|
|---|
| 758 | LDAP_SCOPE_SUBTREE,
|
|---|
| 759 | "(objectCategory=group)",
|
|---|
| 760 | opt_long_list_entries ? longattrs :
|
|---|
| 761 | shortattrs, usergrp_display,
|
|---|
| 762 | disp_fields);
|
|---|
| 763 |
|
|---|
| 764 | ads_destroy(&ads);
|
|---|
| 765 | return ADS_ERR_OK(rc) ? 0 : -1;
|
|---|
| 766 | }
|
|---|
| 767 | return net_run_function(argc, argv, func, net_ads_group_usage);
|
|---|
| 768 | }
|
|---|
| 769 |
|
|---|
| 770 | static int net_ads_status(int argc, const char **argv)
|
|---|
| 771 | {
|
|---|
| 772 | ADS_STRUCT *ads;
|
|---|
| 773 | ADS_STATUS rc;
|
|---|
| 774 | LDAPMessage *res;
|
|---|
| 775 |
|
|---|
| 776 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 777 | return -1;
|
|---|
| 778 | }
|
|---|
| 779 |
|
|---|
| 780 | rc = ads_find_machine_acct(ads, &res, global_myname());
|
|---|
| 781 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 782 | d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
|
|---|
| 783 | ads_destroy(&ads);
|
|---|
| 784 | return -1;
|
|---|
| 785 | }
|
|---|
| 786 |
|
|---|
| 787 | if (ads_count_replies(ads, res) == 0) {
|
|---|
| 788 | d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
|
|---|
| 789 | ads_destroy(&ads);
|
|---|
| 790 | return -1;
|
|---|
| 791 | }
|
|---|
| 792 |
|
|---|
| 793 | ads_dump(ads, res);
|
|---|
| 794 | ads_destroy(&ads);
|
|---|
| 795 | return 0;
|
|---|
| 796 | }
|
|---|
| 797 |
|
|---|
| 798 | /*******************************************************************
|
|---|
| 799 | Leave an AD domain. Windows XP disables the machine account.
|
|---|
| 800 | We'll try the same. The old code would do an LDAP delete.
|
|---|
| 801 | That only worked using the machine creds because added the machine
|
|---|
| 802 | with full control to the computer object's ACL.
|
|---|
| 803 | *******************************************************************/
|
|---|
| 804 |
|
|---|
| 805 | static int net_ads_leave(int argc, const char **argv)
|
|---|
| 806 | {
|
|---|
| 807 | ADS_STRUCT *ads = NULL;
|
|---|
| 808 | ADS_STATUS adsret;
|
|---|
| 809 | NTSTATUS status;
|
|---|
| 810 | int ret = -1;
|
|---|
| 811 | struct cli_state *cli = NULL;
|
|---|
| 812 | TALLOC_CTX *ctx;
|
|---|
| 813 | DOM_SID *dom_sid = NULL;
|
|---|
| 814 | char *short_domain_name = NULL;
|
|---|
| 815 |
|
|---|
| 816 | if (!secrets_init()) {
|
|---|
| 817 | DEBUG(1,("Failed to initialise secrets database\n"));
|
|---|
| 818 | return -1;
|
|---|
| 819 | }
|
|---|
| 820 |
|
|---|
| 821 | if (!(ctx = talloc_init("net_ads_leave"))) {
|
|---|
| 822 | d_fprintf(stderr, "Could not initialise talloc context.\n");
|
|---|
| 823 | return -1;
|
|---|
| 824 | }
|
|---|
| 825 |
|
|---|
| 826 | /* The finds a DC and takes care of getting the
|
|---|
| 827 | user creds if necessary */
|
|---|
| 828 |
|
|---|
| 829 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 830 | return -1;
|
|---|
| 831 | }
|
|---|
| 832 |
|
|---|
| 833 | /* make RPC calls here */
|
|---|
| 834 |
|
|---|
| 835 | if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
|
|---|
| 836 | ads->config.ldap_server_name)) )
|
|---|
| 837 | {
|
|---|
| 838 | goto done;
|
|---|
| 839 | }
|
|---|
| 840 |
|
|---|
| 841 | if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
|
|---|
| 842 | goto done;
|
|---|
| 843 | }
|
|---|
| 844 |
|
|---|
| 845 | saf_delete( short_domain_name );
|
|---|
| 846 |
|
|---|
| 847 | status = netdom_leave_domain(ctx, cli, dom_sid);
|
|---|
| 848 |
|
|---|
| 849 | /* Try and delete it via LDAP - the old way we used to. */
|
|---|
| 850 |
|
|---|
| 851 | adsret = ads_leave_realm(ads, global_myname());
|
|---|
| 852 | if (ADS_ERR_OK(adsret)) {
|
|---|
| 853 | d_printf("Deleted account for '%s' in realm '%s'\n",
|
|---|
| 854 | global_myname(), ads->config.realm);
|
|---|
| 855 | ret = 0;
|
|---|
| 856 | } else {
|
|---|
| 857 | /* We couldn't delete it - see if the disable succeeded. */
|
|---|
| 858 | if (NT_STATUS_IS_OK(status)) {
|
|---|
| 859 | d_printf("Disabled account for '%s' in realm '%s'\n",
|
|---|
| 860 | global_myname(), ads->config.realm);
|
|---|
| 861 | ret = 0;
|
|---|
| 862 | } else {
|
|---|
| 863 | d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
|
|---|
| 864 | global_myname(), ads->config.realm);
|
|---|
| 865 | }
|
|---|
| 866 | }
|
|---|
| 867 |
|
|---|
| 868 | done:
|
|---|
| 869 |
|
|---|
| 870 | if ( cli )
|
|---|
| 871 | cli_shutdown(cli);
|
|---|
| 872 |
|
|---|
| 873 | ads_destroy(&ads);
|
|---|
| 874 | TALLOC_FREE( ctx );
|
|---|
| 875 |
|
|---|
| 876 | return ret;
|
|---|
| 877 | }
|
|---|
| 878 |
|
|---|
| 879 | static NTSTATUS net_ads_join_ok(void)
|
|---|
| 880 | {
|
|---|
| 881 | ADS_STRUCT *ads = NULL;
|
|---|
| 882 | ADS_STATUS status;
|
|---|
| 883 |
|
|---|
| 884 | if (!secrets_init()) {
|
|---|
| 885 | DEBUG(1,("Failed to initialise secrets database\n"));
|
|---|
| 886 | return NT_STATUS_ACCESS_DENIED;
|
|---|
| 887 | }
|
|---|
| 888 |
|
|---|
| 889 | net_use_machine_password();
|
|---|
| 890 |
|
|---|
| 891 | status = ads_startup(True, &ads);
|
|---|
| 892 | if (!ADS_ERR_OK(status)) {
|
|---|
| 893 | return ads_ntstatus(status);
|
|---|
| 894 | }
|
|---|
| 895 |
|
|---|
| 896 | ads_destroy(&ads);
|
|---|
| 897 | return NT_STATUS_OK;
|
|---|
| 898 | }
|
|---|
| 899 |
|
|---|
| 900 | /*
|
|---|
| 901 | check that an existing join is OK
|
|---|
| 902 | */
|
|---|
| 903 | int net_ads_testjoin(int argc, const char **argv)
|
|---|
| 904 | {
|
|---|
| 905 | NTSTATUS status;
|
|---|
| 906 | use_in_memory_ccache();
|
|---|
| 907 |
|
|---|
| 908 | /* Display success or failure */
|
|---|
| 909 | status = net_ads_join_ok();
|
|---|
| 910 | if (!NT_STATUS_IS_OK(status)) {
|
|---|
| 911 | fprintf(stderr,"Join to domain is not valid: %s\n",
|
|---|
| 912 | get_friendly_nt_error_msg(status));
|
|---|
| 913 | return -1;
|
|---|
| 914 | }
|
|---|
| 915 |
|
|---|
| 916 | printf("Join is OK\n");
|
|---|
| 917 | return 0;
|
|---|
| 918 | }
|
|---|
| 919 |
|
|---|
| 920 | /*******************************************************************
|
|---|
| 921 | Simple configu checks before beginning the join
|
|---|
| 922 | ********************************************************************/
|
|---|
| 923 |
|
|---|
| 924 | static NTSTATUS check_ads_config( void )
|
|---|
| 925 | {
|
|---|
| 926 | if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
|
|---|
| 927 | d_printf("Host is not configured as a member server.\n");
|
|---|
| 928 | return NT_STATUS_INVALID_DOMAIN_ROLE;
|
|---|
| 929 | }
|
|---|
| 930 |
|
|---|
| 931 | if (strlen(global_myname()) > 15) {
|
|---|
| 932 | d_printf("Our netbios name can be at most 15 chars long, "
|
|---|
| 933 | "\"%s\" is %u chars long\n", global_myname(),
|
|---|
| 934 | (unsigned int)strlen(global_myname()));
|
|---|
| 935 | return NT_STATUS_NAME_TOO_LONG;
|
|---|
| 936 | }
|
|---|
| 937 |
|
|---|
| 938 | if ( lp_security() == SEC_ADS && !*lp_realm()) {
|
|---|
| 939 | d_fprintf(stderr, "realm must be set in in %s for ADS "
|
|---|
| 940 | "join to succeed.\n", dyn_CONFIGFILE);
|
|---|
| 941 | return NT_STATUS_INVALID_PARAMETER;
|
|---|
| 942 | }
|
|---|
| 943 |
|
|---|
| 944 | if (!secrets_init()) {
|
|---|
| 945 | DEBUG(1,("Failed to initialise secrets database\n"));
|
|---|
| 946 | /* This is a good bet for failure of secrets_init ... */
|
|---|
| 947 | return NT_STATUS_ACCESS_DENIED;
|
|---|
| 948 | }
|
|---|
| 949 |
|
|---|
| 950 | return NT_STATUS_OK;
|
|---|
| 951 | }
|
|---|
| 952 |
|
|---|
| 953 | /*******************************************************************
|
|---|
| 954 | Do the domain join
|
|---|
| 955 | ********************************************************************/
|
|---|
| 956 |
|
|---|
| 957 | static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
|
|---|
| 958 | struct in_addr *ip, char **domain,
|
|---|
| 959 | DOM_SID **dom_sid,
|
|---|
| 960 | const char *password)
|
|---|
| 961 | {
|
|---|
| 962 | NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
|
|---|
| 963 | struct cli_state *cli = NULL;
|
|---|
| 964 |
|
|---|
| 965 | ret = connect_to_ipc_krb5(&cli, ip, servername);
|
|---|
| 966 | if ( !NT_STATUS_IS_OK(ret) ) {
|
|---|
| 967 | goto done;
|
|---|
| 968 | }
|
|---|
| 969 |
|
|---|
| 970 | ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
|
|---|
| 971 | if ( !NT_STATUS_IS_OK(ret) ) {
|
|---|
| 972 | goto done;
|
|---|
| 973 | }
|
|---|
| 974 |
|
|---|
| 975 | /* cli->server_domain is not filled in when using krb5
|
|---|
| 976 | session setups */
|
|---|
| 977 |
|
|---|
| 978 | saf_store( *domain, cli->desthost );
|
|---|
| 979 |
|
|---|
| 980 | ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
|
|---|
| 981 |
|
|---|
| 982 | done:
|
|---|
| 983 | if ( cli )
|
|---|
| 984 | cli_shutdown(cli);
|
|---|
| 985 |
|
|---|
| 986 | return ret;
|
|---|
| 987 | }
|
|---|
| 988 |
|
|---|
| 989 | /*******************************************************************
|
|---|
| 990 | Set a machines dNSHostName and servicePrincipalName attributes
|
|---|
| 991 | ********************************************************************/
|
|---|
| 992 |
|
|---|
| 993 | static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
|
|---|
| 994 | {
|
|---|
| 995 | ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
|
|---|
| 996 | char *new_dn;
|
|---|
| 997 | ADS_MODLIST mods;
|
|---|
| 998 | const char *servicePrincipalName[3] = {NULL, NULL, NULL};
|
|---|
| 999 | char *psp;
|
|---|
| 1000 | fstring my_fqdn;
|
|---|
| 1001 | LDAPMessage *res = NULL;
|
|---|
| 1002 | char *dn_string = NULL;
|
|---|
| 1003 | const char *machine_name = global_myname();
|
|---|
| 1004 | int count;
|
|---|
| 1005 |
|
|---|
| 1006 | if ( !machine_name ) {
|
|---|
| 1007 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1008 | }
|
|---|
| 1009 |
|
|---|
| 1010 | /* Find our DN */
|
|---|
| 1011 |
|
|---|
| 1012 | status = ads_find_machine_acct(ads_s, &res, machine_name);
|
|---|
| 1013 | if (!ADS_ERR_OK(status))
|
|---|
| 1014 | return status;
|
|---|
| 1015 |
|
|---|
| 1016 | if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
|
|---|
| 1017 | DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
|
|---|
| 1018 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1019 | }
|
|---|
| 1020 |
|
|---|
| 1021 | if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
|
|---|
| 1022 | DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
|
|---|
| 1023 | goto done;
|
|---|
| 1024 | }
|
|---|
| 1025 |
|
|---|
| 1026 | new_dn = talloc_strdup(ctx, dn_string);
|
|---|
| 1027 | ads_memfree(ads_s, dn_string);
|
|---|
| 1028 | if (!new_dn) {
|
|---|
| 1029 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1030 | }
|
|---|
| 1031 |
|
|---|
| 1032 | /* Windows only creates HOST/shortname & HOST/fqdn. */
|
|---|
| 1033 |
|
|---|
| 1034 | if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
|
|---|
| 1035 | goto done;
|
|---|
| 1036 | strupper_m(psp);
|
|---|
| 1037 | servicePrincipalName[0] = psp;
|
|---|
| 1038 |
|
|---|
| 1039 | name_to_fqdn(my_fqdn, machine_name);
|
|---|
| 1040 | strlower_m(my_fqdn);
|
|---|
| 1041 | if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
|
|---|
| 1042 | goto done;
|
|---|
| 1043 | servicePrincipalName[1] = psp;
|
|---|
| 1044 |
|
|---|
| 1045 | if (!(mods = ads_init_mods(ctx))) {
|
|---|
| 1046 | goto done;
|
|---|
| 1047 | }
|
|---|
| 1048 |
|
|---|
| 1049 | /* fields of primary importance */
|
|---|
| 1050 |
|
|---|
| 1051 | ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
|
|---|
| 1052 | ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
|
|---|
| 1053 |
|
|---|
| 1054 | status = ads_gen_mod(ads_s, new_dn, mods);
|
|---|
| 1055 |
|
|---|
| 1056 | done:
|
|---|
| 1057 | ads_msgfree(ads_s, res);
|
|---|
| 1058 |
|
|---|
| 1059 | return status;
|
|---|
| 1060 | }
|
|---|
| 1061 |
|
|---|
| 1062 | /*******************************************************************
|
|---|
| 1063 | Set a machines dNSHostName and servicePrincipalName attributes
|
|---|
| 1064 | ********************************************************************/
|
|---|
| 1065 |
|
|---|
| 1066 | static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
|
|---|
| 1067 | {
|
|---|
| 1068 | ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
|
|---|
| 1069 | char *new_dn;
|
|---|
| 1070 | ADS_MODLIST mods;
|
|---|
| 1071 | LDAPMessage *res = NULL;
|
|---|
| 1072 | char *dn_string = NULL;
|
|---|
| 1073 | const char *machine_name = global_myname();
|
|---|
| 1074 | int count;
|
|---|
| 1075 |
|
|---|
| 1076 | if ( !machine_name ) {
|
|---|
| 1077 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1078 | }
|
|---|
| 1079 |
|
|---|
| 1080 | /* Find our DN */
|
|---|
| 1081 |
|
|---|
| 1082 | status = ads_find_machine_acct(ads_s, &res, machine_name);
|
|---|
| 1083 | if (!ADS_ERR_OK(status))
|
|---|
| 1084 | return status;
|
|---|
| 1085 |
|
|---|
| 1086 | if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
|
|---|
| 1087 | DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
|
|---|
| 1088 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1089 | }
|
|---|
| 1090 |
|
|---|
| 1091 | if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
|
|---|
| 1092 | DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
|
|---|
| 1093 | goto done;
|
|---|
| 1094 | }
|
|---|
| 1095 |
|
|---|
| 1096 | new_dn = talloc_strdup(ctx, dn_string);
|
|---|
| 1097 | ads_memfree(ads_s, dn_string);
|
|---|
| 1098 | if (!new_dn) {
|
|---|
| 1099 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1100 | }
|
|---|
| 1101 |
|
|---|
| 1102 | /* now do the mods */
|
|---|
| 1103 |
|
|---|
| 1104 | if (!(mods = ads_init_mods(ctx))) {
|
|---|
| 1105 | goto done;
|
|---|
| 1106 | }
|
|---|
| 1107 |
|
|---|
| 1108 | /* fields of primary importance */
|
|---|
| 1109 |
|
|---|
| 1110 | ads_mod_str(ctx, &mods, "userPrincipalName", upn);
|
|---|
| 1111 |
|
|---|
| 1112 | status = ads_gen_mod(ads_s, new_dn, mods);
|
|---|
| 1113 |
|
|---|
| 1114 | done:
|
|---|
| 1115 | ads_msgfree(ads_s, res);
|
|---|
| 1116 |
|
|---|
| 1117 | return status;
|
|---|
| 1118 | }
|
|---|
| 1119 |
|
|---|
| 1120 | /*******************************************************************
|
|---|
| 1121 | Set a machines dNSHostName and servicePrincipalName attributes
|
|---|
| 1122 | ********************************************************************/
|
|---|
| 1123 |
|
|---|
| 1124 | static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
|
|---|
| 1125 | const char *os_name, const char *os_version )
|
|---|
| 1126 | {
|
|---|
| 1127 | ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
|
|---|
| 1128 | char *new_dn;
|
|---|
| 1129 | ADS_MODLIST mods;
|
|---|
| 1130 | LDAPMessage *res = NULL;
|
|---|
| 1131 | char *dn_string = NULL;
|
|---|
| 1132 | const char *machine_name = global_myname();
|
|---|
| 1133 | int count;
|
|---|
| 1134 | char *os_sp = NULL;
|
|---|
| 1135 |
|
|---|
| 1136 | if ( !os_name || !os_version ) {
|
|---|
| 1137 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1138 | }
|
|---|
| 1139 |
|
|---|
| 1140 | /* Find our DN */
|
|---|
| 1141 |
|
|---|
| 1142 | status = ads_find_machine_acct(ads_s, &res, machine_name);
|
|---|
| 1143 | if (!ADS_ERR_OK(status))
|
|---|
| 1144 | return status;
|
|---|
| 1145 |
|
|---|
| 1146 | if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
|
|---|
| 1147 | DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
|
|---|
| 1148 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1149 | }
|
|---|
| 1150 |
|
|---|
| 1151 | if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
|
|---|
| 1152 | DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
|
|---|
| 1153 | goto done;
|
|---|
| 1154 | }
|
|---|
| 1155 |
|
|---|
| 1156 | new_dn = talloc_strdup(ctx, dn_string);
|
|---|
| 1157 | ads_memfree(ads_s, dn_string);
|
|---|
| 1158 | if (!new_dn) {
|
|---|
| 1159 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1160 | }
|
|---|
| 1161 |
|
|---|
| 1162 | /* now do the mods */
|
|---|
| 1163 |
|
|---|
| 1164 | if (!(mods = ads_init_mods(ctx))) {
|
|---|
| 1165 | goto done;
|
|---|
| 1166 | }
|
|---|
| 1167 |
|
|---|
| 1168 | os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
|
|---|
| 1169 |
|
|---|
| 1170 | /* fields of primary importance */
|
|---|
| 1171 |
|
|---|
| 1172 | ads_mod_str(ctx, &mods, "operatingSystem", os_name);
|
|---|
| 1173 | ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
|
|---|
| 1174 | if ( os_sp )
|
|---|
| 1175 | ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
|
|---|
| 1176 |
|
|---|
| 1177 | status = ads_gen_mod(ads_s, new_dn, mods);
|
|---|
| 1178 |
|
|---|
| 1179 | done:
|
|---|
| 1180 | ads_msgfree(ads_s, res);
|
|---|
| 1181 | TALLOC_FREE( os_sp );
|
|---|
| 1182 |
|
|---|
| 1183 | return status;
|
|---|
| 1184 | }
|
|---|
| 1185 |
|
|---|
| 1186 | /*******************************************************************
|
|---|
| 1187 | join a domain using ADS (LDAP mods)
|
|---|
| 1188 | ********************************************************************/
|
|---|
| 1189 |
|
|---|
| 1190 | static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
|
|---|
| 1191 | {
|
|---|
| 1192 | ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
|
|---|
| 1193 | char *dn, *ou_str;
|
|---|
| 1194 | LDAPMessage *res = NULL;
|
|---|
| 1195 |
|
|---|
| 1196 | ou_str = ads_ou_string(ads, ou);
|
|---|
| 1197 | if ((asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path)) == -1) {
|
|---|
| 1198 | SAFE_FREE(ou_str);
|
|---|
| 1199 | return ADS_ERROR(LDAP_NO_MEMORY);
|
|---|
| 1200 | }
|
|---|
| 1201 |
|
|---|
| 1202 | rc = ads_search_dn(ads, &res, dn, NULL);
|
|---|
| 1203 | ads_msgfree(ads, res);
|
|---|
| 1204 |
|
|---|
| 1205 | if (ADS_ERR_OK(rc)) {
|
|---|
| 1206 | /* Attempt to create the machine account and bail if this fails.
|
|---|
| 1207 | Assume that the admin wants exactly what they requested */
|
|---|
| 1208 |
|
|---|
| 1209 | rc = ads_create_machine_acct( ads, global_myname(), dn );
|
|---|
| 1210 | if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
|
|---|
| 1211 | rc = ADS_SUCCESS;
|
|---|
| 1212 | }
|
|---|
| 1213 | }
|
|---|
| 1214 |
|
|---|
| 1215 | SAFE_FREE( ou_str );
|
|---|
| 1216 | SAFE_FREE( dn );
|
|---|
| 1217 |
|
|---|
| 1218 | return rc;
|
|---|
| 1219 | }
|
|---|
| 1220 |
|
|---|
| 1221 | /************************************************************************
|
|---|
| 1222 | ************************************************************************/
|
|---|
| 1223 |
|
|---|
| 1224 | static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
|
|---|
| 1225 | {
|
|---|
| 1226 | uint32 domain_func;
|
|---|
| 1227 | ADS_STATUS status;
|
|---|
| 1228 | fstring salt;
|
|---|
| 1229 | char *std_salt;
|
|---|
| 1230 | LDAPMessage *res = NULL;
|
|---|
| 1231 | const char *machine_name = global_myname();
|
|---|
| 1232 |
|
|---|
| 1233 | status = ads_domain_func_level( ads, &domain_func );
|
|---|
| 1234 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 1235 | DEBUG(2,("Failed to determine domain functional level!\n"));
|
|---|
| 1236 | return False;
|
|---|
| 1237 | }
|
|---|
| 1238 |
|
|---|
| 1239 | /* go ahead and setup the default salt */
|
|---|
| 1240 |
|
|---|
| 1241 | if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
|
|---|
| 1242 | d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
|
|---|
| 1243 | return False;
|
|---|
| 1244 | }
|
|---|
| 1245 |
|
|---|
| 1246 | fstrcpy( salt, std_salt );
|
|---|
| 1247 | SAFE_FREE( std_salt );
|
|---|
| 1248 |
|
|---|
| 1249 | /* if it's a Windows functional domain, we have to look for the UPN */
|
|---|
| 1250 |
|
|---|
| 1251 | if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
|
|---|
| 1252 | char *upn;
|
|---|
| 1253 | int count;
|
|---|
| 1254 |
|
|---|
| 1255 | status = ads_find_machine_acct(ads, &res, machine_name);
|
|---|
| 1256 | if (!ADS_ERR_OK(status)) {
|
|---|
| 1257 | return False;
|
|---|
| 1258 | }
|
|---|
| 1259 |
|
|---|
| 1260 | if ( (count = ads_count_replies(ads, res)) != 1 ) {
|
|---|
| 1261 | DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
|
|---|
| 1262 | return False;
|
|---|
| 1263 | }
|
|---|
| 1264 |
|
|---|
| 1265 | upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
|
|---|
| 1266 | if ( upn ) {
|
|---|
| 1267 | fstrcpy( salt, upn );
|
|---|
| 1268 | }
|
|---|
| 1269 |
|
|---|
| 1270 | ads_msgfree(ads, res);
|
|---|
| 1271 | }
|
|---|
| 1272 |
|
|---|
| 1273 | return kerberos_secrets_store_des_salt( salt );
|
|---|
| 1274 | }
|
|---|
| 1275 |
|
|---|
| 1276 | /*******************************************************************
|
|---|
| 1277 | Send a DNS update request
|
|---|
| 1278 | *******************************************************************/
|
|---|
| 1279 |
|
|---|
| 1280 | #if defined(WITH_DNS_UPDATES)
|
|---|
| 1281 | #include "dns.h"
|
|---|
| 1282 | DNS_ERROR DoDNSUpdate(char *pszServerName,
|
|---|
| 1283 | const char *pszDomainName,
|
|---|
| 1284 | const char *pszHostName,
|
|---|
| 1285 | const struct in_addr *iplist, int num_addrs );
|
|---|
| 1286 |
|
|---|
| 1287 |
|
|---|
| 1288 | static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
|
|---|
| 1289 | const char *machine_name,
|
|---|
| 1290 | const struct in_addr *addrs,
|
|---|
| 1291 | int num_addrs)
|
|---|
| 1292 | {
|
|---|
| 1293 | struct dns_rr_ns *nameservers = NULL;
|
|---|
| 1294 | int ns_count = 0;
|
|---|
| 1295 | NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
|
|---|
| 1296 | DNS_ERROR dns_err;
|
|---|
| 1297 | fstring dns_server;
|
|---|
| 1298 | const char *dnsdomain = NULL;
|
|---|
| 1299 | char *root_domain = NULL;
|
|---|
| 1300 |
|
|---|
| 1301 | if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
|
|---|
| 1302 | d_printf("No DNS domain configured for %s. "
|
|---|
| 1303 | "Unable to perform DNS Update.\n", machine_name);
|
|---|
| 1304 | status = NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1305 | goto done;
|
|---|
| 1306 | }
|
|---|
| 1307 | dnsdomain++;
|
|---|
| 1308 |
|
|---|
| 1309 | status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
|
|---|
| 1310 | if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
|
|---|
| 1311 | /* Child domains often do not have NS records. Look
|
|---|
| 1312 | for the NS record for the forest root domain
|
|---|
| 1313 | (rootDomainNamingContext in therootDSE) */
|
|---|
| 1314 |
|
|---|
| 1315 | const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
|
|---|
| 1316 | LDAPMessage *msg = NULL;
|
|---|
| 1317 | char *root_dn;
|
|---|
| 1318 | ADS_STATUS ads_status;
|
|---|
| 1319 |
|
|---|
| 1320 | if ( !ads->ld ) {
|
|---|
| 1321 | ads_status = ads_connect( ads );
|
|---|
| 1322 | if ( !ADS_ERR_OK(ads_status) ) {
|
|---|
| 1323 | DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
|
|---|
| 1324 | goto done;
|
|---|
| 1325 | }
|
|---|
| 1326 | }
|
|---|
| 1327 |
|
|---|
| 1328 | ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
|
|---|
| 1329 | "(objectclass=*)", rootname_attrs, &msg);
|
|---|
| 1330 | if (!ADS_ERR_OK(ads_status)) {
|
|---|
| 1331 | goto done;
|
|---|
| 1332 | }
|
|---|
| 1333 |
|
|---|
| 1334 | root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
|
|---|
| 1335 | if ( !root_dn ) {
|
|---|
| 1336 | ads_msgfree( ads, msg );
|
|---|
| 1337 | goto done;
|
|---|
| 1338 | }
|
|---|
| 1339 |
|
|---|
| 1340 | root_domain = ads_build_domain( root_dn );
|
|---|
| 1341 |
|
|---|
| 1342 | /* cleanup */
|
|---|
| 1343 | ads_msgfree( ads, msg );
|
|---|
| 1344 |
|
|---|
| 1345 | /* try again for NS servers */
|
|---|
| 1346 |
|
|---|
| 1347 | status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
|
|---|
| 1348 |
|
|---|
| 1349 | if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
|
|---|
| 1350 | DEBUG(3,("net_ads_join: Failed to find name server for the %s "
|
|---|
| 1351 | "realm\n", ads->config.realm));
|
|---|
| 1352 | goto done;
|
|---|
| 1353 | }
|
|---|
| 1354 |
|
|---|
| 1355 | dnsdomain = root_domain;
|
|---|
| 1356 |
|
|---|
| 1357 | }
|
|---|
| 1358 |
|
|---|
| 1359 | /* Now perform the dns update - we'll try non-secure and if we fail,
|
|---|
| 1360 | we'll follow it up with a secure update */
|
|---|
| 1361 |
|
|---|
| 1362 | fstrcpy( dns_server, nameservers[0].hostname );
|
|---|
| 1363 |
|
|---|
| 1364 | dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
|
|---|
| 1365 | if (!ERR_DNS_IS_OK(dns_err)) {
|
|---|
| 1366 | status = NT_STATUS_UNSUCCESSFUL;
|
|---|
| 1367 | }
|
|---|
| 1368 |
|
|---|
| 1369 | done:
|
|---|
| 1370 |
|
|---|
| 1371 | SAFE_FREE( root_domain );
|
|---|
| 1372 |
|
|---|
| 1373 | return status;
|
|---|
| 1374 | }
|
|---|
| 1375 |
|
|---|
| 1376 | static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
|
|---|
| 1377 | {
|
|---|
| 1378 | int num_addrs;
|
|---|
| 1379 | struct in_addr *iplist = NULL;
|
|---|
| 1380 | fstring machine_name;
|
|---|
| 1381 | NTSTATUS status;
|
|---|
| 1382 |
|
|---|
| 1383 | name_to_fqdn( machine_name, global_myname() );
|
|---|
| 1384 | strlower_m( machine_name );
|
|---|
| 1385 |
|
|---|
| 1386 | /* Get our ip address (not the 127.0.0.x address but a real ip
|
|---|
| 1387 | * address) */
|
|---|
| 1388 |
|
|---|
| 1389 | num_addrs = get_my_ip_address( &iplist );
|
|---|
| 1390 | if ( num_addrs <= 0 ) {
|
|---|
| 1391 | DEBUG(4,("net_ads_join: Failed to find my non-loopback IP "
|
|---|
| 1392 | "addresses!\n"));
|
|---|
| 1393 | return NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1394 | }
|
|---|
| 1395 |
|
|---|
| 1396 | status = net_update_dns_internal(mem_ctx, ads, machine_name,
|
|---|
| 1397 | iplist, num_addrs);
|
|---|
| 1398 | SAFE_FREE( iplist );
|
|---|
| 1399 | return status;
|
|---|
| 1400 | }
|
|---|
| 1401 | #endif
|
|---|
| 1402 |
|
|---|
| 1403 |
|
|---|
| 1404 | /*******************************************************************
|
|---|
| 1405 | utility function to parse an integer parameter from
|
|---|
| 1406 | "parameter = value"
|
|---|
| 1407 | **********************************************************/
|
|---|
| 1408 | static char* get_string_param( const char* param )
|
|---|
| 1409 | {
|
|---|
| 1410 | char *p;
|
|---|
| 1411 |
|
|---|
| 1412 | if ( (p = strchr( param, '=' )) == NULL )
|
|---|
| 1413 | return NULL;
|
|---|
| 1414 |
|
|---|
| 1415 | return (p+1);
|
|---|
| 1416 | }
|
|---|
| 1417 |
|
|---|
| 1418 | /*******************************************************************
|
|---|
| 1419 | ********************************************************************/
|
|---|
| 1420 |
|
|---|
| 1421 | static int net_ads_join_usage(int argc, const char **argv)
|
|---|
| 1422 | {
|
|---|
| 1423 | d_printf("net ads join [options]\n");
|
|---|
| 1424 | d_printf("Valid options:\n");
|
|---|
| 1425 | d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
|
|---|
| 1426 | d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
|
|---|
| 1427 | d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
|
|---|
| 1428 | d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
|
|---|
| 1429 | d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
|
|---|
| 1430 | d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
|
|---|
| 1431 | d_printf(" need to be doubled or even quadrupled. It is not used as a separator");
|
|---|
| 1432 |
|
|---|
| 1433 | return -1;
|
|---|
| 1434 | }
|
|---|
| 1435 |
|
|---|
| 1436 | /*******************************************************************
|
|---|
| 1437 | ********************************************************************/
|
|---|
| 1438 |
|
|---|
| 1439 | int net_ads_join(int argc, const char **argv)
|
|---|
| 1440 | {
|
|---|
| 1441 | ADS_STRUCT *ads = NULL;
|
|---|
| 1442 | ADS_STATUS status;
|
|---|
| 1443 | NTSTATUS nt_status;
|
|---|
| 1444 | char *machine_account = NULL;
|
|---|
| 1445 | char *short_domain_name = NULL;
|
|---|
| 1446 | char *tmp_password, *password;
|
|---|
| 1447 | TALLOC_CTX *ctx = NULL;
|
|---|
| 1448 | DOM_SID *domain_sid = NULL;
|
|---|
| 1449 | BOOL createupn = False;
|
|---|
| 1450 | const char *machineupn = NULL;
|
|---|
| 1451 | const char *create_in_ou = NULL;
|
|---|
| 1452 | int i;
|
|---|
| 1453 | fstring dc_name;
|
|---|
| 1454 | struct in_addr dcip;
|
|---|
| 1455 | const char *os_name = NULL;
|
|---|
| 1456 | const char *os_version = NULL;
|
|---|
| 1457 |
|
|---|
| 1458 | nt_status = check_ads_config();
|
|---|
| 1459 | if (!NT_STATUS_IS_OK(nt_status)) {
|
|---|
| 1460 | d_fprintf(stderr, "Invalid configuration. Exiting....\n");
|
|---|
| 1461 | goto fail;
|
|---|
| 1462 | }
|
|---|
| 1463 |
|
|---|
| 1464 | /* find a DC to initialize the server affinity cache */
|
|---|
| 1465 |
|
|---|
| 1466 | get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
|
|---|
| 1467 |
|
|---|
| 1468 | status = ads_startup(True, &ads);
|
|---|
| 1469 | if (!ADS_ERR_OK(status)) {
|
|---|
| 1470 | DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
|
|---|
| 1471 | nt_status = ads_ntstatus(status);
|
|---|
| 1472 | goto fail;
|
|---|
| 1473 | }
|
|---|
| 1474 |
|
|---|
| 1475 | if (strcmp(ads->config.realm, lp_realm()) != 0) {
|
|---|
| 1476 | d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
|
|---|
| 1477 | "(%s) DO NOT match. Aborting join\n", ads->config.realm,
|
|---|
| 1478 | dyn_CONFIGFILE, lp_realm());
|
|---|
| 1479 | nt_status = NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1480 | goto fail;
|
|---|
| 1481 | }
|
|---|
| 1482 |
|
|---|
| 1483 | if (!(ctx = talloc_init("net_ads_join"))) {
|
|---|
| 1484 | d_fprintf(stderr, "Could not initialise talloc context.\n");
|
|---|
| 1485 | nt_status = NT_STATUS_NO_MEMORY;
|
|---|
| 1486 | goto fail;
|
|---|
| 1487 | }
|
|---|
| 1488 |
|
|---|
| 1489 | /* process additional command line args */
|
|---|
| 1490 |
|
|---|
| 1491 | for ( i=0; i<argc; i++ ) {
|
|---|
| 1492 | if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
|
|---|
| 1493 | createupn = True;
|
|---|
| 1494 | machineupn = get_string_param(argv[i]);
|
|---|
| 1495 | }
|
|---|
| 1496 | else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
|
|---|
| 1497 | if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
|
|---|
| 1498 | d_fprintf(stderr, "Please supply a valid OU path.\n");
|
|---|
| 1499 | nt_status = NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1500 | goto fail;
|
|---|
| 1501 | }
|
|---|
| 1502 | }
|
|---|
| 1503 | else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
|
|---|
| 1504 | if ( (os_name = get_string_param(argv[i])) == NULL ) {
|
|---|
| 1505 | d_fprintf(stderr, "Please supply a operating system name.\n");
|
|---|
| 1506 | nt_status = NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1507 | goto fail;
|
|---|
| 1508 | }
|
|---|
| 1509 | }
|
|---|
| 1510 | else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
|
|---|
| 1511 | if ( (os_version = get_string_param(argv[i])) == NULL ) {
|
|---|
| 1512 | d_fprintf(stderr, "Please supply a valid operating system version.\n");
|
|---|
| 1513 | nt_status = NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1514 | goto fail;
|
|---|
| 1515 | }
|
|---|
| 1516 | }
|
|---|
| 1517 | else {
|
|---|
| 1518 | d_fprintf(stderr, "Bad option: %s\n", argv[i]);
|
|---|
| 1519 | nt_status = NT_STATUS_INVALID_PARAMETER;
|
|---|
| 1520 | goto fail;
|
|---|
| 1521 | }
|
|---|
| 1522 | }
|
|---|
| 1523 |
|
|---|
| 1524 | /* If we were given an OU, try to create the machine in
|
|---|
| 1525 | the OU account first and then do the normal RPC join */
|
|---|
| 1526 |
|
|---|
| 1527 | if ( create_in_ou ) {
|
|---|
| 1528 | status = net_precreate_machine_acct( ads, create_in_ou );
|
|---|
| 1529 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 1530 | d_fprintf( stderr, "Failed to pre-create the machine object "
|
|---|
| 1531 | "in OU %s.\n", argv[0]);
|
|---|
| 1532 | DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
|
|---|
| 1533 | ads_errstr(status)));
|
|---|
| 1534 | nt_status = ads_ntstatus(status);
|
|---|
| 1535 | goto fail;
|
|---|
| 1536 | }
|
|---|
| 1537 | }
|
|---|
| 1538 |
|
|---|
| 1539 | /* Do the domain join here */
|
|---|
| 1540 |
|
|---|
| 1541 | tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
|
|---|
| 1542 | password = talloc_strdup(ctx, tmp_password);
|
|---|
| 1543 |
|
|---|
| 1544 | nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
|
|---|
| 1545 | &ads->ldap_ip, &short_domain_name, &domain_sid, password);
|
|---|
| 1546 | if ( !NT_STATUS_IS_OK(nt_status) ) {
|
|---|
| 1547 | DEBUG(1, ("call of net_join_domain failed: %s\n",
|
|---|
| 1548 | get_friendly_nt_error_msg(nt_status)));
|
|---|
| 1549 | goto fail;
|
|---|
| 1550 | }
|
|---|
| 1551 |
|
|---|
| 1552 | /* Check the short name of the domain */
|
|---|
| 1553 |
|
|---|
| 1554 | if ( !strequal(lp_workgroup(), short_domain_name) ) {
|
|---|
| 1555 | d_printf("The workgroup in %s does not match the short\n", dyn_CONFIGFILE);
|
|---|
| 1556 | d_printf("domain name obtained from the server.\n");
|
|---|
| 1557 | d_printf("Using the name [%s] from the server.\n", short_domain_name);
|
|---|
| 1558 | d_printf("You should set \"workgroup = %s\" in %s.\n",
|
|---|
| 1559 | short_domain_name, dyn_CONFIGFILE);
|
|---|
| 1560 | }
|
|---|
| 1561 |
|
|---|
| 1562 | d_printf("Using short domain name -- %s\n", short_domain_name);
|
|---|
| 1563 |
|
|---|
| 1564 | /* HACK ALERT! Store the sid and password under both the lp_workgroup()
|
|---|
| 1565 | value from smb.conf and the string returned from the server. The former is
|
|---|
| 1566 | neede to bootstrap winbindd's first connection to the DC to get the real
|
|---|
| 1567 | short domain name --jerry */
|
|---|
| 1568 |
|
|---|
| 1569 | if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
|
|---|
| 1570 | || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
|
|---|
| 1571 | {
|
|---|
| 1572 | /* issue an internal error here for now.
|
|---|
| 1573 | * everything else would mean changing tdb routines. */
|
|---|
| 1574 | nt_status = NT_STATUS_INTERNAL_ERROR;
|
|---|
| 1575 | goto fail;
|
|---|
| 1576 | }
|
|---|
| 1577 |
|
|---|
| 1578 | /* Verify that everything is ok */
|
|---|
| 1579 |
|
|---|
| 1580 | if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
|
|---|
| 1581 | d_fprintf(stderr, "Failed to verify membership in domain!\n");
|
|---|
| 1582 | goto fail;
|
|---|
| 1583 | }
|
|---|
| 1584 |
|
|---|
| 1585 | /* create the dNSHostName & servicePrincipalName values */
|
|---|
| 1586 |
|
|---|
| 1587 | status = net_set_machine_spn( ctx, ads );
|
|---|
| 1588 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 1589 |
|
|---|
| 1590 | d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
|
|---|
| 1591 | d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
|
|---|
| 1592 | d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
|
|---|
| 1593 |
|
|---|
| 1594 | /* Disable the machine account in AD. Better to fail than to leave
|
|---|
| 1595 | a confused admin. */
|
|---|
| 1596 |
|
|---|
| 1597 | if ( net_ads_leave( 0, NULL ) != 0 ) {
|
|---|
| 1598 | d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
|
|---|
| 1599 | }
|
|---|
| 1600 |
|
|---|
| 1601 | /* clear out the machine password */
|
|---|
| 1602 |
|
|---|
| 1603 | netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
|
|---|
| 1604 | netdom_store_machine_account( short_domain_name, domain_sid, "" );
|
|---|
| 1605 |
|
|---|
| 1606 | nt_status = ads_ntstatus(status);
|
|---|
| 1607 | goto fail;
|
|---|
| 1608 | }
|
|---|
| 1609 |
|
|---|
| 1610 | if ( !net_derive_salting_principal( ctx, ads ) ) {
|
|---|
| 1611 | DEBUG(1,("Failed to determine salting principal\n"));
|
|---|
| 1612 | goto fail;
|
|---|
| 1613 | }
|
|---|
| 1614 |
|
|---|
| 1615 | if ( createupn ) {
|
|---|
| 1616 | pstring upn;
|
|---|
| 1617 |
|
|---|
| 1618 | /* default to using the short UPN name */
|
|---|
| 1619 | if ( !machineupn ) {
|
|---|
| 1620 | snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
|
|---|
| 1621 | ads->config.realm );
|
|---|
| 1622 | machineupn = upn;
|
|---|
| 1623 | }
|
|---|
| 1624 |
|
|---|
| 1625 | status = net_set_machine_upn( ctx, ads, machineupn );
|
|---|
| 1626 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 1627 | d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
|
|---|
| 1628 | }
|
|---|
| 1629 | }
|
|---|
| 1630 |
|
|---|
| 1631 | /* Try to set the operatingSystem attributes if asked */
|
|---|
| 1632 |
|
|---|
| 1633 | if ( os_name && os_version ) {
|
|---|
| 1634 | status = net_set_os_attributes( ctx, ads, os_name, os_version );
|
|---|
| 1635 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 1636 | d_fprintf(stderr, "Failed to set operatingSystem attributes. "
|
|---|
| 1637 | "Are you a Domain Admin?\n");
|
|---|
| 1638 | }
|
|---|
| 1639 | }
|
|---|
| 1640 |
|
|---|
| 1641 | /* Now build the keytab, using the same ADS connection */
|
|---|
| 1642 |
|
|---|
| 1643 | if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
|
|---|
| 1644 | DEBUG(1,("Error creating host keytab!\n"));
|
|---|
| 1645 | }
|
|---|
| 1646 |
|
|---|
| 1647 | #if defined(WITH_DNS_UPDATES)
|
|---|
| 1648 | /* We enter this block with user creds */
|
|---|
| 1649 | ads_kdestroy( NULL );
|
|---|
| 1650 | ads_destroy(&ads);
|
|---|
| 1651 | ads = NULL;
|
|---|
| 1652 |
|
|---|
| 1653 | if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
|
|---|
| 1654 | /* kinit with the machine password */
|
|---|
| 1655 |
|
|---|
| 1656 | use_in_memory_ccache();
|
|---|
| 1657 | asprintf( &ads->auth.user_name, "%s$", global_myname() );
|
|---|
| 1658 | ads->auth.password = secrets_fetch_machine_password(
|
|---|
| 1659 | lp_workgroup(), NULL, NULL );
|
|---|
| 1660 | ads->auth.realm = SMB_STRDUP( lp_realm() );
|
|---|
| 1661 | ads_kinit_password( ads );
|
|---|
| 1662 | }
|
|---|
| 1663 |
|
|---|
| 1664 | if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
|
|---|
| 1665 | d_fprintf( stderr, "DNS update failed!\n" );
|
|---|
| 1666 | }
|
|---|
| 1667 |
|
|---|
| 1668 | /* exit from this block using machine creds */
|
|---|
| 1669 | #endif
|
|---|
| 1670 |
|
|---|
| 1671 | d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
|
|---|
| 1672 |
|
|---|
| 1673 | SAFE_FREE(machine_account);
|
|---|
| 1674 | TALLOC_FREE( ctx );
|
|---|
| 1675 | ads_destroy(&ads);
|
|---|
| 1676 |
|
|---|
| 1677 | return 0;
|
|---|
| 1678 |
|
|---|
| 1679 | fail:
|
|---|
| 1680 | /* issue an overall failure message at the end. */
|
|---|
| 1681 | d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
|
|---|
| 1682 |
|
|---|
| 1683 | SAFE_FREE(machine_account);
|
|---|
| 1684 | TALLOC_FREE( ctx );
|
|---|
| 1685 | ads_destroy(&ads);
|
|---|
| 1686 |
|
|---|
| 1687 | return -1;
|
|---|
| 1688 |
|
|---|
| 1689 | }
|
|---|
| 1690 |
|
|---|
| 1691 | /*******************************************************************
|
|---|
| 1692 | ********************************************************************/
|
|---|
| 1693 |
|
|---|
| 1694 | static int net_ads_dns_usage(int argc, const char **argv)
|
|---|
| 1695 | {
|
|---|
| 1696 | #if defined(WITH_DNS_UPDATES)
|
|---|
| 1697 | d_printf("net ads dns <command>\n");
|
|---|
| 1698 | d_printf("Valid commands:\n");
|
|---|
| 1699 | d_printf(" register Issue a dynamic DNS update request for our hostname\n");
|
|---|
| 1700 |
|
|---|
| 1701 | return 0;
|
|---|
| 1702 | #else
|
|---|
| 1703 | d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
|
|---|
| 1704 | return -1;
|
|---|
| 1705 | #endif
|
|---|
| 1706 | }
|
|---|
| 1707 |
|
|---|
| 1708 | /*******************************************************************
|
|---|
| 1709 | ********************************************************************/
|
|---|
| 1710 |
|
|---|
| 1711 | static int net_ads_dns_register(int argc, const char **argv)
|
|---|
| 1712 | {
|
|---|
| 1713 | #if defined(WITH_DNS_UPDATES)
|
|---|
| 1714 | ADS_STRUCT *ads;
|
|---|
| 1715 | ADS_STATUS status;
|
|---|
| 1716 | TALLOC_CTX *ctx;
|
|---|
| 1717 |
|
|---|
| 1718 | #ifdef DEVELOPER
|
|---|
| 1719 | talloc_enable_leak_report();
|
|---|
| 1720 | #endif
|
|---|
| 1721 |
|
|---|
| 1722 | if (argc > 0) {
|
|---|
| 1723 | d_fprintf(stderr, "net ads dns register\n");
|
|---|
| 1724 | return -1;
|
|---|
| 1725 | }
|
|---|
| 1726 |
|
|---|
| 1727 | if (!(ctx = talloc_init("net_ads_dns"))) {
|
|---|
| 1728 | d_fprintf(stderr, "Could not initialise talloc context\n");
|
|---|
| 1729 | return -1;
|
|---|
| 1730 | }
|
|---|
| 1731 |
|
|---|
| 1732 | status = ads_startup(True, &ads);
|
|---|
| 1733 | if ( !ADS_ERR_OK(status) ) {
|
|---|
| 1734 | DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
|
|---|
| 1735 | TALLOC_FREE(ctx);
|
|---|
| 1736 | return -1;
|
|---|
| 1737 | }
|
|---|
| 1738 |
|
|---|
| 1739 | if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
|
|---|
| 1740 | d_fprintf( stderr, "DNS update failed!\n" );
|
|---|
| 1741 | ads_destroy( &ads );
|
|---|
| 1742 | TALLOC_FREE( ctx );
|
|---|
| 1743 | return -1;
|
|---|
| 1744 | }
|
|---|
| 1745 |
|
|---|
| 1746 | d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
|
|---|
| 1747 |
|
|---|
| 1748 | ads_destroy(&ads);
|
|---|
| 1749 | TALLOC_FREE( ctx );
|
|---|
| 1750 |
|
|---|
| 1751 | return 0;
|
|---|
| 1752 | #else
|
|---|
| 1753 | d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
|
|---|
| 1754 | return -1;
|
|---|
| 1755 | #endif
|
|---|
| 1756 | }
|
|---|
| 1757 |
|
|---|
| 1758 | #if defined(WITH_DNS_UPDATES)
|
|---|
| 1759 | DNS_ERROR do_gethostbyname(const char *server, const char *host);
|
|---|
| 1760 | #endif
|
|---|
| 1761 |
|
|---|
| 1762 | static int net_ads_dns_gethostbyname(int argc, const char **argv)
|
|---|
| 1763 | {
|
|---|
| 1764 | #if defined(WITH_DNS_UPDATES)
|
|---|
| 1765 | DNS_ERROR err;
|
|---|
| 1766 |
|
|---|
| 1767 | #ifdef DEVELOPER
|
|---|
| 1768 | talloc_enable_leak_report();
|
|---|
| 1769 | #endif
|
|---|
| 1770 |
|
|---|
| 1771 | if (argc != 2) {
|
|---|
| 1772 | d_fprintf(stderr, "net ads dns gethostbyname <server> "
|
|---|
| 1773 | "<name>\n");
|
|---|
| 1774 | return -1;
|
|---|
| 1775 | }
|
|---|
| 1776 |
|
|---|
| 1777 | err = do_gethostbyname(argv[0], argv[1]);
|
|---|
| 1778 |
|
|---|
| 1779 | d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
|
|---|
| 1780 | #endif
|
|---|
| 1781 | return 0;
|
|---|
| 1782 | }
|
|---|
| 1783 |
|
|---|
| 1784 | static int net_ads_dns(int argc, const char *argv[])
|
|---|
| 1785 | {
|
|---|
| 1786 | struct functable func[] = {
|
|---|
| 1787 | {"REGISTER", net_ads_dns_register},
|
|---|
| 1788 | {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
|
|---|
| 1789 | {NULL, NULL}
|
|---|
| 1790 | };
|
|---|
| 1791 |
|
|---|
| 1792 | return net_run_function(argc, argv, func, net_ads_dns_usage);
|
|---|
| 1793 | }
|
|---|
| 1794 |
|
|---|
| 1795 | /*******************************************************************
|
|---|
| 1796 | ********************************************************************/
|
|---|
| 1797 |
|
|---|
| 1798 | int net_ads_printer_usage(int argc, const char **argv)
|
|---|
| 1799 | {
|
|---|
| 1800 | d_printf(
|
|---|
| 1801 | "\nnet ads printer search <printer>"
|
|---|
| 1802 | "\n\tsearch for a printer in the directory\n"
|
|---|
| 1803 | "\nnet ads printer info <printer> <server>"
|
|---|
| 1804 | "\n\tlookup info in directory for printer on server"
|
|---|
| 1805 | "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
|
|---|
| 1806 | "\nnet ads printer publish <printername>"
|
|---|
| 1807 | "\n\tpublish printer in directory"
|
|---|
| 1808 | "\n\t(note: printer name is required)\n"
|
|---|
| 1809 | "\nnet ads printer remove <printername>"
|
|---|
| 1810 | "\n\tremove printer from directory"
|
|---|
| 1811 | "\n\t(note: printer name is required)\n");
|
|---|
| 1812 | return -1;
|
|---|
| 1813 | }
|
|---|
| 1814 |
|
|---|
| 1815 | /*******************************************************************
|
|---|
| 1816 | ********************************************************************/
|
|---|
| 1817 |
|
|---|
| 1818 | static int net_ads_printer_search(int argc, const char **argv)
|
|---|
| 1819 | {
|
|---|
| 1820 | ADS_STRUCT *ads;
|
|---|
| 1821 | ADS_STATUS rc;
|
|---|
| 1822 | LDAPMessage *res = NULL;
|
|---|
| 1823 |
|
|---|
| 1824 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 1825 | return -1;
|
|---|
| 1826 | }
|
|---|
| 1827 |
|
|---|
| 1828 | rc = ads_find_printers(ads, &res);
|
|---|
| 1829 |
|
|---|
| 1830 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 1831 | d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
|
|---|
| 1832 | ads_msgfree(ads, res);
|
|---|
| 1833 | ads_destroy(&ads);
|
|---|
| 1834 | return -1;
|
|---|
| 1835 | }
|
|---|
| 1836 |
|
|---|
| 1837 | if (ads_count_replies(ads, res) == 0) {
|
|---|
| 1838 | d_fprintf(stderr, "No results found\n");
|
|---|
| 1839 | ads_msgfree(ads, res);
|
|---|
| 1840 | ads_destroy(&ads);
|
|---|
| 1841 | return -1;
|
|---|
| 1842 | }
|
|---|
| 1843 |
|
|---|
| 1844 | ads_dump(ads, res);
|
|---|
| 1845 | ads_msgfree(ads, res);
|
|---|
| 1846 | ads_destroy(&ads);
|
|---|
| 1847 | return 0;
|
|---|
| 1848 | }
|
|---|
| 1849 |
|
|---|
| 1850 | static int net_ads_printer_info(int argc, const char **argv)
|
|---|
| 1851 | {
|
|---|
| 1852 | ADS_STRUCT *ads;
|
|---|
| 1853 | ADS_STATUS rc;
|
|---|
| 1854 | const char *servername, *printername;
|
|---|
| 1855 | LDAPMessage *res = NULL;
|
|---|
| 1856 |
|
|---|
| 1857 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 1858 | return -1;
|
|---|
| 1859 | }
|
|---|
| 1860 |
|
|---|
| 1861 | if (argc > 0) {
|
|---|
| 1862 | printername = argv[0];
|
|---|
| 1863 | } else {
|
|---|
| 1864 | printername = "*";
|
|---|
| 1865 | }
|
|---|
| 1866 |
|
|---|
| 1867 | if (argc > 1) {
|
|---|
| 1868 | servername = argv[1];
|
|---|
| 1869 | } else {
|
|---|
| 1870 | servername = global_myname();
|
|---|
| 1871 | }
|
|---|
| 1872 |
|
|---|
| 1873 | rc = ads_find_printer_on_server(ads, &res, printername, servername);
|
|---|
| 1874 |
|
|---|
| 1875 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 1876 | d_fprintf(stderr, "Server '%s' not found: %s\n",
|
|---|
| 1877 | servername, ads_errstr(rc));
|
|---|
| 1878 | ads_msgfree(ads, res);
|
|---|
| 1879 | ads_destroy(&ads);
|
|---|
| 1880 | return -1;
|
|---|
| 1881 | }
|
|---|
| 1882 |
|
|---|
| 1883 | if (ads_count_replies(ads, res) == 0) {
|
|---|
| 1884 | d_fprintf(stderr, "Printer '%s' not found\n", printername);
|
|---|
| 1885 | ads_msgfree(ads, res);
|
|---|
| 1886 | ads_destroy(&ads);
|
|---|
| 1887 | return -1;
|
|---|
| 1888 | }
|
|---|
| 1889 |
|
|---|
| 1890 | ads_dump(ads, res);
|
|---|
| 1891 | ads_msgfree(ads, res);
|
|---|
| 1892 | ads_destroy(&ads);
|
|---|
| 1893 |
|
|---|
| 1894 | return 0;
|
|---|
| 1895 | }
|
|---|
| 1896 |
|
|---|
| 1897 | void do_drv_upgrade_printer(int msg_type, struct process_id src,
|
|---|
| 1898 | void *buf, size_t len, void *private_data)
|
|---|
| 1899 | {
|
|---|
| 1900 | return;
|
|---|
| 1901 | }
|
|---|
| 1902 |
|
|---|
| 1903 | static int net_ads_printer_publish(int argc, const char **argv)
|
|---|
| 1904 | {
|
|---|
| 1905 | ADS_STRUCT *ads;
|
|---|
| 1906 | ADS_STATUS rc;
|
|---|
| 1907 | const char *servername, *printername;
|
|---|
| 1908 | struct cli_state *cli;
|
|---|
| 1909 | struct rpc_pipe_client *pipe_hnd;
|
|---|
| 1910 | struct in_addr server_ip;
|
|---|
| 1911 | NTSTATUS nt_status;
|
|---|
| 1912 | TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
|
|---|
| 1913 | ADS_MODLIST mods = ads_init_mods(mem_ctx);
|
|---|
| 1914 | char *prt_dn, *srv_dn, **srv_cn;
|
|---|
| 1915 | char *srv_cn_escaped = NULL, *printername_escaped = NULL;
|
|---|
| 1916 | LDAPMessage *res = NULL;
|
|---|
| 1917 |
|
|---|
| 1918 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 1919 | talloc_destroy(mem_ctx);
|
|---|
| 1920 | return -1;
|
|---|
| 1921 | }
|
|---|
| 1922 |
|
|---|
| 1923 | if (argc < 1) {
|
|---|
| 1924 | talloc_destroy(mem_ctx);
|
|---|
| 1925 | return net_ads_printer_usage(argc, argv);
|
|---|
| 1926 | }
|
|---|
| 1927 |
|
|---|
| 1928 | printername = argv[0];
|
|---|
| 1929 |
|
|---|
| 1930 | if (argc == 2) {
|
|---|
| 1931 | servername = argv[1];
|
|---|
| 1932 | } else {
|
|---|
| 1933 | servername = global_myname();
|
|---|
| 1934 | }
|
|---|
| 1935 |
|
|---|
| 1936 | /* Get printer data from SPOOLSS */
|
|---|
| 1937 |
|
|---|
| 1938 | resolve_name(servername, &server_ip, 0x20);
|
|---|
| 1939 |
|
|---|
| 1940 | nt_status = cli_full_connection(&cli, global_myname(), servername,
|
|---|
| 1941 | &server_ip, 0,
|
|---|
| 1942 | "IPC$", "IPC",
|
|---|
| 1943 | opt_user_name, opt_workgroup,
|
|---|
| 1944 | opt_password ? opt_password : "",
|
|---|
| 1945 | CLI_FULL_CONNECTION_USE_KERBEROS,
|
|---|
| 1946 | Undefined, NULL);
|
|---|
| 1947 |
|
|---|
| 1948 | if (NT_STATUS_IS_ERR(nt_status)) {
|
|---|
| 1949 | d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
|
|---|
| 1950 | "for %s\n", servername, printername);
|
|---|
| 1951 | ads_destroy(&ads);
|
|---|
| 1952 | talloc_destroy(mem_ctx);
|
|---|
| 1953 | return -1;
|
|---|
| 1954 | }
|
|---|
| 1955 |
|
|---|
| 1956 | /* Publish on AD server */
|
|---|
| 1957 |
|
|---|
| 1958 | ads_find_machine_acct(ads, &res, servername);
|
|---|
| 1959 |
|
|---|
| 1960 | if (ads_count_replies(ads, res) == 0) {
|
|---|
| 1961 | d_fprintf(stderr, "Could not find machine account for server %s\n",
|
|---|
| 1962 | servername);
|
|---|
| 1963 | ads_destroy(&ads);
|
|---|
| 1964 | talloc_destroy(mem_ctx);
|
|---|
| 1965 | return -1;
|
|---|
| 1966 | }
|
|---|
| 1967 |
|
|---|
| 1968 | srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
|
|---|
| 1969 | srv_cn = ldap_explode_dn(srv_dn, 1);
|
|---|
| 1970 |
|
|---|
| 1971 | srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
|
|---|
| 1972 | printername_escaped = escape_rdn_val_string_alloc(printername);
|
|---|
| 1973 | if (!srv_cn_escaped || !printername_escaped) {
|
|---|
| 1974 | SAFE_FREE(srv_cn_escaped);
|
|---|
| 1975 | SAFE_FREE(printername_escaped);
|
|---|
| 1976 | d_fprintf(stderr, "Internal error, out of memory!");
|
|---|
| 1977 | ads_destroy(&ads);
|
|---|
| 1978 | talloc_destroy(mem_ctx);
|
|---|
| 1979 | return -1;
|
|---|
| 1980 | }
|
|---|
| 1981 |
|
|---|
| 1982 | asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
|
|---|
| 1983 |
|
|---|
| 1984 | SAFE_FREE(srv_cn_escaped);
|
|---|
| 1985 | SAFE_FREE(printername_escaped);
|
|---|
| 1986 |
|
|---|
| 1987 | pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
|
|---|
| 1988 | if (!pipe_hnd) {
|
|---|
| 1989 | d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
|
|---|
| 1990 | servername);
|
|---|
| 1991 | SAFE_FREE(prt_dn);
|
|---|
| 1992 | ads_destroy(&ads);
|
|---|
| 1993 | talloc_destroy(mem_ctx);
|
|---|
| 1994 | return -1;
|
|---|
| 1995 | }
|
|---|
| 1996 |
|
|---|
| 1997 | if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
|
|---|
| 1998 | printername))) {
|
|---|
| 1999 | SAFE_FREE(prt_dn);
|
|---|
| 2000 | ads_destroy(&ads);
|
|---|
| 2001 | talloc_destroy(mem_ctx);
|
|---|
| 2002 | return -1;
|
|---|
| 2003 | }
|
|---|
| 2004 |
|
|---|
| 2005 | rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
|
|---|
| 2006 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 2007 | d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
|
|---|
| 2008 | SAFE_FREE(prt_dn);
|
|---|
| 2009 | ads_destroy(&ads);
|
|---|
| 2010 | talloc_destroy(mem_ctx);
|
|---|
| 2011 | return -1;
|
|---|
| 2012 | }
|
|---|
| 2013 |
|
|---|
| 2014 | d_printf("published printer\n");
|
|---|
| 2015 | SAFE_FREE(prt_dn);
|
|---|
| 2016 | ads_destroy(&ads);
|
|---|
| 2017 | talloc_destroy(mem_ctx);
|
|---|
| 2018 |
|
|---|
| 2019 | return 0;
|
|---|
| 2020 | }
|
|---|
| 2021 |
|
|---|
| 2022 | static int net_ads_printer_remove(int argc, const char **argv)
|
|---|
| 2023 | {
|
|---|
| 2024 | ADS_STRUCT *ads;
|
|---|
| 2025 | ADS_STATUS rc;
|
|---|
| 2026 | const char *servername;
|
|---|
| 2027 | char *prt_dn;
|
|---|
| 2028 | LDAPMessage *res = NULL;
|
|---|
| 2029 |
|
|---|
| 2030 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 2031 | return -1;
|
|---|
| 2032 | }
|
|---|
| 2033 |
|
|---|
| 2034 | if (argc < 1) {
|
|---|
| 2035 | return net_ads_printer_usage(argc, argv);
|
|---|
| 2036 | }
|
|---|
| 2037 |
|
|---|
| 2038 | if (argc > 1) {
|
|---|
| 2039 | servername = argv[1];
|
|---|
| 2040 | } else {
|
|---|
| 2041 | servername = global_myname();
|
|---|
| 2042 | }
|
|---|
| 2043 |
|
|---|
| 2044 | rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
|
|---|
| 2045 |
|
|---|
| 2046 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 2047 | d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
|
|---|
| 2048 | ads_msgfree(ads, res);
|
|---|
| 2049 | ads_destroy(&ads);
|
|---|
| 2050 | return -1;
|
|---|
| 2051 | }
|
|---|
| 2052 |
|
|---|
| 2053 | if (ads_count_replies(ads, res) == 0) {
|
|---|
| 2054 | d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
|
|---|
| 2055 | ads_msgfree(ads, res);
|
|---|
| 2056 | ads_destroy(&ads);
|
|---|
| 2057 | return -1;
|
|---|
| 2058 | }
|
|---|
| 2059 |
|
|---|
| 2060 | prt_dn = ads_get_dn(ads, res);
|
|---|
| 2061 | ads_msgfree(ads, res);
|
|---|
| 2062 | rc = ads_del_dn(ads, prt_dn);
|
|---|
| 2063 | ads_memfree(ads, prt_dn);
|
|---|
| 2064 |
|
|---|
| 2065 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 2066 | d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
|
|---|
| 2067 | ads_destroy(&ads);
|
|---|
| 2068 | return -1;
|
|---|
| 2069 | }
|
|---|
| 2070 |
|
|---|
| 2071 | ads_destroy(&ads);
|
|---|
| 2072 | return 0;
|
|---|
| 2073 | }
|
|---|
| 2074 |
|
|---|
| 2075 | static int net_ads_printer(int argc, const char **argv)
|
|---|
| 2076 | {
|
|---|
| 2077 | struct functable func[] = {
|
|---|
| 2078 | {"SEARCH", net_ads_printer_search},
|
|---|
| 2079 | {"INFO", net_ads_printer_info},
|
|---|
| 2080 | {"PUBLISH", net_ads_printer_publish},
|
|---|
| 2081 | {"REMOVE", net_ads_printer_remove},
|
|---|
| 2082 | {NULL, NULL}
|
|---|
| 2083 | };
|
|---|
| 2084 |
|
|---|
| 2085 | return net_run_function(argc, argv, func, net_ads_printer_usage);
|
|---|
| 2086 | }
|
|---|
| 2087 |
|
|---|
| 2088 |
|
|---|
| 2089 | static int net_ads_password(int argc, const char **argv)
|
|---|
| 2090 | {
|
|---|
| 2091 | ADS_STRUCT *ads;
|
|---|
| 2092 | const char *auth_principal = opt_user_name;
|
|---|
| 2093 | const char *auth_password = opt_password;
|
|---|
| 2094 | char *realm = NULL;
|
|---|
| 2095 | char *new_password = NULL;
|
|---|
| 2096 | char *c, *prompt;
|
|---|
| 2097 | const char *user;
|
|---|
| 2098 | ADS_STATUS ret;
|
|---|
| 2099 |
|
|---|
| 2100 | if (opt_user_name == NULL || opt_password == NULL) {
|
|---|
| 2101 | d_fprintf(stderr, "You must supply an administrator username/password\n");
|
|---|
| 2102 | return -1;
|
|---|
| 2103 | }
|
|---|
| 2104 |
|
|---|
| 2105 | if (argc < 1) {
|
|---|
| 2106 | d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
|
|---|
| 2107 | return -1;
|
|---|
| 2108 | }
|
|---|
| 2109 |
|
|---|
| 2110 | user = argv[0];
|
|---|
| 2111 | if (!strchr_m(user, '@')) {
|
|---|
| 2112 | asprintf(&c, "%s@%s", argv[0], lp_realm());
|
|---|
| 2113 | user = c;
|
|---|
| 2114 | }
|
|---|
| 2115 |
|
|---|
| 2116 | use_in_memory_ccache();
|
|---|
| 2117 | c = strchr_m(auth_principal, '@');
|
|---|
| 2118 | if (c) {
|
|---|
| 2119 | realm = ++c;
|
|---|
| 2120 | } else {
|
|---|
| 2121 | realm = lp_realm();
|
|---|
| 2122 | }
|
|---|
| 2123 |
|
|---|
| 2124 | /* use the realm so we can eventually change passwords for users
|
|---|
| 2125 | in realms other than default */
|
|---|
| 2126 | if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
|
|---|
| 2127 | return -1;
|
|---|
| 2128 | }
|
|---|
| 2129 |
|
|---|
| 2130 | /* we don't actually need a full connect, but it's the easy way to
|
|---|
| 2131 | fill in the KDC's addresss */
|
|---|
| 2132 | ads_connect(ads);
|
|---|
| 2133 |
|
|---|
| 2134 | if (!ads || !ads->config.realm) {
|
|---|
| 2135 | d_fprintf(stderr, "Didn't find the kerberos server!\n");
|
|---|
| 2136 | return -1;
|
|---|
| 2137 | }
|
|---|
| 2138 |
|
|---|
| 2139 | if (argv[1]) {
|
|---|
| 2140 | new_password = (char *)argv[1];
|
|---|
| 2141 | } else {
|
|---|
| 2142 | asprintf(&prompt, "Enter new password for %s:", user);
|
|---|
| 2143 | new_password = getpass(prompt);
|
|---|
| 2144 | free(prompt);
|
|---|
| 2145 | }
|
|---|
| 2146 |
|
|---|
| 2147 | ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
|
|---|
| 2148 | auth_password, user, new_password, ads->auth.time_offset);
|
|---|
| 2149 | if (!ADS_ERR_OK(ret)) {
|
|---|
| 2150 | d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
|
|---|
| 2151 | ads_destroy(&ads);
|
|---|
| 2152 | return -1;
|
|---|
| 2153 | }
|
|---|
| 2154 |
|
|---|
| 2155 | d_printf("Password change for %s completed.\n", user);
|
|---|
| 2156 | ads_destroy(&ads);
|
|---|
| 2157 |
|
|---|
| 2158 | return 0;
|
|---|
| 2159 | }
|
|---|
| 2160 |
|
|---|
| 2161 | int net_ads_changetrustpw(int argc, const char **argv)
|
|---|
| 2162 | {
|
|---|
| 2163 | ADS_STRUCT *ads;
|
|---|
| 2164 | char *host_principal;
|
|---|
| 2165 | fstring my_name;
|
|---|
| 2166 | ADS_STATUS ret;
|
|---|
| 2167 |
|
|---|
| 2168 | if (!secrets_init()) {
|
|---|
| 2169 | DEBUG(1,("Failed to initialise secrets database\n"));
|
|---|
| 2170 | return -1;
|
|---|
| 2171 | }
|
|---|
| 2172 |
|
|---|
| 2173 | net_use_machine_password();
|
|---|
| 2174 |
|
|---|
| 2175 | use_in_memory_ccache();
|
|---|
| 2176 |
|
|---|
| 2177 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 2178 | return -1;
|
|---|
| 2179 | }
|
|---|
| 2180 |
|
|---|
| 2181 | fstrcpy(my_name, global_myname());
|
|---|
| 2182 | strlower_m(my_name);
|
|---|
| 2183 | asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
|
|---|
| 2184 | d_printf("Changing password for principal: %s\n", host_principal);
|
|---|
| 2185 |
|
|---|
| 2186 | ret = ads_change_trust_account_password(ads, host_principal);
|
|---|
| 2187 |
|
|---|
| 2188 | if (!ADS_ERR_OK(ret)) {
|
|---|
| 2189 | d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
|
|---|
| 2190 | ads_destroy(&ads);
|
|---|
| 2191 | SAFE_FREE(host_principal);
|
|---|
| 2192 | return -1;
|
|---|
| 2193 | }
|
|---|
| 2194 |
|
|---|
| 2195 | d_printf("Password change for principal %s succeeded.\n", host_principal);
|
|---|
| 2196 |
|
|---|
| 2197 | if (lp_use_kerberos_keytab()) {
|
|---|
| 2198 | d_printf("Attempting to update system keytab with new password.\n");
|
|---|
| 2199 | if (ads_keytab_create_default(ads)) {
|
|---|
| 2200 | d_printf("Failed to update system keytab.\n");
|
|---|
| 2201 | }
|
|---|
| 2202 | }
|
|---|
| 2203 |
|
|---|
| 2204 | ads_destroy(&ads);
|
|---|
| 2205 | SAFE_FREE(host_principal);
|
|---|
| 2206 |
|
|---|
| 2207 | return 0;
|
|---|
| 2208 | }
|
|---|
| 2209 |
|
|---|
| 2210 | /*
|
|---|
| 2211 | help for net ads search
|
|---|
| 2212 | */
|
|---|
| 2213 | static int net_ads_search_usage(int argc, const char **argv)
|
|---|
| 2214 | {
|
|---|
| 2215 | d_printf(
|
|---|
| 2216 | "\nnet ads search <expression> <attributes...>\n"\
|
|---|
| 2217 | "\nperform a raw LDAP search on a ADS server and dump the results\n"\
|
|---|
| 2218 | "The expression is a standard LDAP search expression, and the\n"\
|
|---|
| 2219 | "attributes are a list of LDAP fields to show in the results\n\n"\
|
|---|
| 2220 | "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
|
|---|
| 2221 | );
|
|---|
| 2222 | net_common_flags_usage(argc, argv);
|
|---|
| 2223 | return -1;
|
|---|
| 2224 | }
|
|---|
| 2225 |
|
|---|
| 2226 |
|
|---|
| 2227 | /*
|
|---|
| 2228 | general ADS search function. Useful in diagnosing problems in ADS
|
|---|
| 2229 | */
|
|---|
| 2230 | static int net_ads_search(int argc, const char **argv)
|
|---|
| 2231 | {
|
|---|
| 2232 | ADS_STRUCT *ads;
|
|---|
| 2233 | ADS_STATUS rc;
|
|---|
| 2234 | const char *ldap_exp;
|
|---|
| 2235 | const char **attrs;
|
|---|
| 2236 | LDAPMessage *res = NULL;
|
|---|
| 2237 |
|
|---|
| 2238 | if (argc < 1) {
|
|---|
| 2239 | return net_ads_search_usage(argc, argv);
|
|---|
| 2240 | }
|
|---|
| 2241 |
|
|---|
| 2242 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 2243 | return -1;
|
|---|
| 2244 | }
|
|---|
| 2245 |
|
|---|
| 2246 | ldap_exp = argv[0];
|
|---|
| 2247 | attrs = (argv + 1);
|
|---|
| 2248 |
|
|---|
| 2249 | rc = ads_do_search_all(ads, ads->config.bind_path,
|
|---|
| 2250 | LDAP_SCOPE_SUBTREE,
|
|---|
| 2251 | ldap_exp, attrs, &res);
|
|---|
| 2252 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 2253 | d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
|
|---|
| 2254 | ads_destroy(&ads);
|
|---|
| 2255 | return -1;
|
|---|
| 2256 | }
|
|---|
| 2257 |
|
|---|
| 2258 | d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
|
|---|
| 2259 |
|
|---|
| 2260 | /* dump the results */
|
|---|
| 2261 | ads_dump(ads, res);
|
|---|
| 2262 |
|
|---|
| 2263 | ads_msgfree(ads, res);
|
|---|
| 2264 | ads_destroy(&ads);
|
|---|
| 2265 |
|
|---|
| 2266 | return 0;
|
|---|
| 2267 | }
|
|---|
| 2268 |
|
|---|
| 2269 |
|
|---|
| 2270 | /*
|
|---|
| 2271 | help for net ads search
|
|---|
| 2272 | */
|
|---|
| 2273 | static int net_ads_dn_usage(int argc, const char **argv)
|
|---|
| 2274 | {
|
|---|
| 2275 | d_printf(
|
|---|
| 2276 | "\nnet ads dn <dn> <attributes...>\n"\
|
|---|
| 2277 | "\nperform a raw LDAP search on a ADS server and dump the results\n"\
|
|---|
| 2278 | "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
|
|---|
| 2279 | "to show in the results\n\n"\
|
|---|
| 2280 | "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
|
|---|
| 2281 | "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
|
|---|
| 2282 | );
|
|---|
| 2283 | net_common_flags_usage(argc, argv);
|
|---|
| 2284 | return -1;
|
|---|
| 2285 | }
|
|---|
| 2286 |
|
|---|
| 2287 |
|
|---|
| 2288 | /*
|
|---|
| 2289 | general ADS search function. Useful in diagnosing problems in ADS
|
|---|
| 2290 | */
|
|---|
| 2291 | static int net_ads_dn(int argc, const char **argv)
|
|---|
| 2292 | {
|
|---|
| 2293 | ADS_STRUCT *ads;
|
|---|
| 2294 | ADS_STATUS rc;
|
|---|
| 2295 | const char *dn;
|
|---|
| 2296 | const char **attrs;
|
|---|
| 2297 | LDAPMessage *res = NULL;
|
|---|
| 2298 |
|
|---|
| 2299 | if (argc < 1) {
|
|---|
| 2300 | return net_ads_dn_usage(argc, argv);
|
|---|
| 2301 | }
|
|---|
| 2302 |
|
|---|
| 2303 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 2304 | return -1;
|
|---|
| 2305 | }
|
|---|
| 2306 |
|
|---|
| 2307 | dn = argv[0];
|
|---|
| 2308 | attrs = (argv + 1);
|
|---|
| 2309 |
|
|---|
| 2310 | rc = ads_do_search_all(ads, dn,
|
|---|
| 2311 | LDAP_SCOPE_BASE,
|
|---|
| 2312 | "(objectclass=*)", attrs, &res);
|
|---|
| 2313 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 2314 | d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
|
|---|
| 2315 | ads_destroy(&ads);
|
|---|
| 2316 | return -1;
|
|---|
| 2317 | }
|
|---|
| 2318 |
|
|---|
| 2319 | d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
|
|---|
| 2320 |
|
|---|
| 2321 | /* dump the results */
|
|---|
| 2322 | ads_dump(ads, res);
|
|---|
| 2323 |
|
|---|
| 2324 | ads_msgfree(ads, res);
|
|---|
| 2325 | ads_destroy(&ads);
|
|---|
| 2326 |
|
|---|
| 2327 | return 0;
|
|---|
| 2328 | }
|
|---|
| 2329 |
|
|---|
| 2330 | /*
|
|---|
| 2331 | help for net ads sid search
|
|---|
| 2332 | */
|
|---|
| 2333 | static int net_ads_sid_usage(int argc, const char **argv)
|
|---|
| 2334 | {
|
|---|
| 2335 | d_printf(
|
|---|
| 2336 | "\nnet ads sid <sid> <attributes...>\n"\
|
|---|
| 2337 | "\nperform a raw LDAP search on a ADS server and dump the results\n"\
|
|---|
| 2338 | "The SID is in string format, and the attributes are a list of LDAP fields \n"\
|
|---|
| 2339 | "to show in the results\n\n"\
|
|---|
| 2340 | "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
|
|---|
| 2341 | );
|
|---|
| 2342 | net_common_flags_usage(argc, argv);
|
|---|
| 2343 | return -1;
|
|---|
| 2344 | }
|
|---|
| 2345 |
|
|---|
| 2346 |
|
|---|
| 2347 | /*
|
|---|
| 2348 | general ADS search function. Useful in diagnosing problems in ADS
|
|---|
| 2349 | */
|
|---|
| 2350 | static int net_ads_sid(int argc, const char **argv)
|
|---|
| 2351 | {
|
|---|
| 2352 | ADS_STRUCT *ads;
|
|---|
| 2353 | ADS_STATUS rc;
|
|---|
| 2354 | const char *sid_string;
|
|---|
| 2355 | const char **attrs;
|
|---|
| 2356 | LDAPMessage *res = NULL;
|
|---|
| 2357 | DOM_SID sid;
|
|---|
| 2358 |
|
|---|
| 2359 | if (argc < 1) {
|
|---|
| 2360 | return net_ads_sid_usage(argc, argv);
|
|---|
| 2361 | }
|
|---|
| 2362 |
|
|---|
| 2363 | if (!ADS_ERR_OK(ads_startup(False, &ads))) {
|
|---|
| 2364 | return -1;
|
|---|
| 2365 | }
|
|---|
| 2366 |
|
|---|
| 2367 | sid_string = argv[0];
|
|---|
| 2368 | attrs = (argv + 1);
|
|---|
| 2369 |
|
|---|
| 2370 | if (!string_to_sid(&sid, sid_string)) {
|
|---|
| 2371 | d_fprintf(stderr, "could not convert sid\n");
|
|---|
| 2372 | ads_destroy(&ads);
|
|---|
| 2373 | return -1;
|
|---|
| 2374 | }
|
|---|
| 2375 |
|
|---|
| 2376 | rc = ads_search_retry_sid(ads, &res, &sid, attrs);
|
|---|
| 2377 | if (!ADS_ERR_OK(rc)) {
|
|---|
| 2378 | d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
|
|---|
| 2379 | ads_destroy(&ads);
|
|---|
| 2380 | return -1;
|
|---|
| 2381 | }
|
|---|
| 2382 |
|
|---|
| 2383 | d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
|
|---|
| 2384 |
|
|---|
| 2385 | /* dump the results */
|
|---|
| 2386 | ads_dump(ads, res);
|
|---|
| 2387 |
|
|---|
| 2388 | ads_msgfree(ads, res);
|
|---|
| 2389 | ads_destroy(&ads);
|
|---|
| 2390 |
|
|---|
| 2391 | return 0;
|
|---|
| 2392 | }
|
|---|
| 2393 |
|
|---|
| 2394 |
|
|---|
| 2395 | static int net_ads_keytab_usage(int argc, const char **argv)
|
|---|
| 2396 | {
|
|---|
| 2397 | d_printf(
|
|---|
| 2398 | "net ads keytab <COMMAND>\n"\
|
|---|
| 2399 | "<COMMAND> can be either:\n"\
|
|---|
| 2400 | " CREATE Creates a fresh keytab\n"\
|
|---|
| 2401 | " ADD Adds new service principal\n"\
|
|---|
| 2402 | " FLUSH Flushes out all keytab entries\n"\
|
|---|
| 2403 | " HELP Prints this help message\n"\
|
|---|
| 2404 | "The ADD command will take arguments, the other commands\n"\
|
|---|
| 2405 | "will not take any arguments. The arguments given to ADD\n"\
|
|---|
| 2406 | "should be a list of principals to add. For example, \n"\
|
|---|
| 2407 | " net ads keytab add srv1 srv2\n"\
|
|---|
| 2408 | "will add principals for the services srv1 and srv2 to the\n"\
|
|---|
| 2409 | "system's keytab.\n"\
|
|---|
| 2410 | "\n"
|
|---|
| 2411 | );
|
|---|
| 2412 | return -1;
|
|---|
| 2413 | }
|
|---|
| 2414 |
|
|---|
| 2415 | static int net_ads_keytab_flush(int argc, const char **argv)
|
|---|
| 2416 | {
|
|---|
| 2417 | int ret;
|
|---|
| 2418 | ADS_STRUCT *ads;
|
|---|
| 2419 |
|
|---|
| 2420 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 2421 | return -1;
|
|---|
| 2422 | }
|
|---|
| 2423 | ret = ads_keytab_flush(ads);
|
|---|
| 2424 | ads_destroy(&ads);
|
|---|
| 2425 | return ret;
|
|---|
| 2426 | }
|
|---|
| 2427 |
|
|---|
| 2428 | static int net_ads_keytab_add(int argc, const char **argv)
|
|---|
| 2429 | {
|
|---|
| 2430 | int i;
|
|---|
| 2431 | int ret = 0;
|
|---|
| 2432 | ADS_STRUCT *ads;
|
|---|
| 2433 |
|
|---|
| 2434 | d_printf("Processing principals to add...\n");
|
|---|
| 2435 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 2436 | return -1;
|
|---|
| 2437 | }
|
|---|
| 2438 | for (i = 0; i < argc; i++) {
|
|---|
| 2439 | ret |= ads_keytab_add_entry(ads, argv[i]);
|
|---|
| 2440 | }
|
|---|
| 2441 | ads_destroy(&ads);
|
|---|
| 2442 | return ret;
|
|---|
| 2443 | }
|
|---|
| 2444 |
|
|---|
| 2445 | static int net_ads_keytab_create(int argc, const char **argv)
|
|---|
| 2446 | {
|
|---|
| 2447 | ADS_STRUCT *ads;
|
|---|
| 2448 | int ret;
|
|---|
| 2449 |
|
|---|
| 2450 | if (!ADS_ERR_OK(ads_startup(True, &ads))) {
|
|---|
| 2451 | return -1;
|
|---|
| 2452 | }
|
|---|
| 2453 | ret = ads_keytab_create_default(ads);
|
|---|
| 2454 | ads_destroy(&ads);
|
|---|
| 2455 | return ret;
|
|---|
| 2456 | }
|
|---|
| 2457 |
|
|---|
| 2458 | int net_ads_keytab(int argc, const char **argv)
|
|---|
| 2459 | {
|
|---|
| 2460 | struct functable func[] = {
|
|---|
| 2461 | {"CREATE", net_ads_keytab_create},
|
|---|
| 2462 | {"ADD", net_ads_keytab_add},
|
|---|
| 2463 | {"FLUSH", net_ads_keytab_flush},
|
|---|
| 2464 | {"HELP", net_ads_keytab_usage},
|
|---|
| 2465 | {NULL, NULL}
|
|---|
| 2466 | };
|
|---|
| 2467 |
|
|---|
| 2468 | if (!lp_use_kerberos_keytab()) {
|
|---|
| 2469 | d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
|
|---|
| 2470 | use keytab functions.\n");
|
|---|
| 2471 | }
|
|---|
| 2472 |
|
|---|
| 2473 | return net_run_function(argc, argv, func, net_ads_keytab_usage);
|
|---|
| 2474 | }
|
|---|
| 2475 |
|
|---|
| 2476 | int net_ads_help(int argc, const char **argv)
|
|---|
| 2477 | {
|
|---|
| 2478 | struct functable func[] = {
|
|---|
| 2479 | {"USER", net_ads_user_usage},
|
|---|
| 2480 | {"GROUP", net_ads_group_usage},
|
|---|
| 2481 | {"PRINTER", net_ads_printer_usage},
|
|---|
| 2482 | {"SEARCH", net_ads_search_usage},
|
|---|
| 2483 | {"INFO", net_ads_info},
|
|---|
| 2484 | {"JOIN", net_ads_join_usage},
|
|---|
| 2485 | {"DNS", net_ads_dns_usage},
|
|---|
| 2486 | {"LEAVE", net_ads_leave},
|
|---|
| 2487 | {"STATUS", net_ads_status},
|
|---|
| 2488 | {"PASSWORD", net_ads_password},
|
|---|
| 2489 | {"CHANGETRUSTPW", net_ads_changetrustpw},
|
|---|
| 2490 | {NULL, NULL}
|
|---|
| 2491 | };
|
|---|
| 2492 |
|
|---|
| 2493 | return net_run_function(argc, argv, func, net_ads_usage);
|
|---|
| 2494 | }
|
|---|
| 2495 |
|
|---|
| 2496 | int net_ads(int argc, const char **argv)
|
|---|
| 2497 | {
|
|---|
| 2498 | struct functable func[] = {
|
|---|
| 2499 | {"INFO", net_ads_info},
|
|---|
| 2500 | {"JOIN", net_ads_join},
|
|---|
| 2501 | {"TESTJOIN", net_ads_testjoin},
|
|---|
| 2502 | {"LEAVE", net_ads_leave},
|
|---|
| 2503 | {"STATUS", net_ads_status},
|
|---|
| 2504 | {"USER", net_ads_user},
|
|---|
| 2505 | {"GROUP", net_ads_group},
|
|---|
| 2506 | {"DNS", net_ads_dns},
|
|---|
| 2507 | {"PASSWORD", net_ads_password},
|
|---|
| 2508 | {"CHANGETRUSTPW", net_ads_changetrustpw},
|
|---|
| 2509 | {"PRINTER", net_ads_printer},
|
|---|
| 2510 | {"SEARCH", net_ads_search},
|
|---|
| 2511 | {"DN", net_ads_dn},
|
|---|
| 2512 | {"SID", net_ads_sid},
|
|---|
| 2513 | {"WORKGROUP", net_ads_workgroup},
|
|---|
| 2514 | {"LOOKUP", net_ads_lookup},
|
|---|
| 2515 | {"KEYTAB", net_ads_keytab},
|
|---|
| 2516 | {"GPO", net_ads_gpo},
|
|---|
| 2517 | {"HELP", net_ads_help},
|
|---|
| 2518 | {NULL, NULL}
|
|---|
| 2519 | };
|
|---|
| 2520 |
|
|---|
| 2521 | return net_run_function(argc, argv, func, net_ads_usage);
|
|---|
| 2522 | }
|
|---|
| 2523 |
|
|---|
| 2524 | #else
|
|---|
| 2525 |
|
|---|
| 2526 | static int net_ads_noads(void)
|
|---|
| 2527 | {
|
|---|
| 2528 | d_fprintf(stderr, "ADS support not compiled in\n");
|
|---|
| 2529 | return -1;
|
|---|
| 2530 | }
|
|---|
| 2531 |
|
|---|
| 2532 | int net_ads_keytab(int argc, const char **argv)
|
|---|
| 2533 | {
|
|---|
| 2534 | return net_ads_noads();
|
|---|
| 2535 | }
|
|---|
| 2536 |
|
|---|
| 2537 | int net_ads_usage(int argc, const char **argv)
|
|---|
| 2538 | {
|
|---|
| 2539 | return net_ads_noads();
|
|---|
| 2540 | }
|
|---|
| 2541 |
|
|---|
| 2542 | int net_ads_help(int argc, const char **argv)
|
|---|
| 2543 | {
|
|---|
| 2544 | return net_ads_noads();
|
|---|
| 2545 | }
|
|---|
| 2546 |
|
|---|
| 2547 | int net_ads_changetrustpw(int argc, const char **argv)
|
|---|
| 2548 | {
|
|---|
| 2549 | return net_ads_noads();
|
|---|
| 2550 | }
|
|---|
| 2551 |
|
|---|
| 2552 | int net_ads_join(int argc, const char **argv)
|
|---|
| 2553 | {
|
|---|
| 2554 | return net_ads_noads();
|
|---|
| 2555 | }
|
|---|
| 2556 |
|
|---|
| 2557 | int net_ads_user(int argc, const char **argv)
|
|---|
| 2558 | {
|
|---|
| 2559 | return net_ads_noads();
|
|---|
| 2560 | }
|
|---|
| 2561 |
|
|---|
| 2562 | int net_ads_group(int argc, const char **argv)
|
|---|
| 2563 | {
|
|---|
| 2564 | return net_ads_noads();
|
|---|
| 2565 | }
|
|---|
| 2566 |
|
|---|
| 2567 | /* this one shouldn't display a message */
|
|---|
| 2568 | int net_ads_check(void)
|
|---|
| 2569 | {
|
|---|
| 2570 | return -1;
|
|---|
| 2571 | }
|
|---|
| 2572 |
|
|---|
| 2573 | int net_ads_check_our_domain(void)
|
|---|
| 2574 | {
|
|---|
| 2575 | return -1;
|
|---|
| 2576 | }
|
|---|
| 2577 |
|
|---|
| 2578 | int net_ads(int argc, const char **argv)
|
|---|
| 2579 | {
|
|---|
| 2580 | return net_ads_usage(argc, argv);
|
|---|
| 2581 | }
|
|---|
| 2582 |
|
|---|
| 2583 | #endif /* WITH_ADS */
|
|---|