source: branches/samba-3.0/source/utils/net_ads.c

Last change on this file was 165, checked in by Paul Smedley, 16 years ago

Add 'missing' 3.0.34 diffs

File size: 63.6 KB
Line 
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
29int 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
68static 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*/
80static 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*/
153static 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
172static 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
206static 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
212static 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
229retry_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
246retry:
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_closest_dc(ads)) {
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
325ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
326{
327 return ads_startup_int(only_own_domain, 0, ads);
328}
329
330ADS_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*/
340static 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
360int net_ads_check_our_domain(void)
361{
362 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
363}
364
365int 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 */
372static 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
401static 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
429static int net_ads_user_usage(int argc, const char **argv)
430{
431 return net_help_user(argc, argv);
432}
433
434static 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
512static 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
569static 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
606int 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
642static int net_ads_group_usage(int argc, const char **argv)
643{
644 return net_help_group(argc, argv);
645}
646
647static 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
699static 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
736int 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
770static 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
805static 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
868done:
869
870 if ( cli )
871 cli_shutdown(cli);
872
873 ads_destroy(&ads);
874 TALLOC_FREE( ctx );
875
876 return ret;
877}
878
879static 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_krb_machine_account();
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 */
903int 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
924static 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
957static 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
982done:
983 if ( cli )
984 cli_shutdown(cli);
985
986 return ret;
987}
988
989/*******************************************************************
990 Set a machines dNSHostName and servicePrincipalName attributes
991 ********************************************************************/
992
993static 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
1056done:
1057 ads_msgfree(ads_s, res);
1058
1059 return status;
1060}
1061
1062/*******************************************************************
1063 Set a machines dNSHostName and servicePrincipalName attributes
1064 ********************************************************************/
1065
1066static 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
1114done:
1115 ads_msgfree(ads_s, res);
1116
1117 return status;
1118}
1119
1120/*******************************************************************
1121 Set a machines dNSHostName and servicePrincipalName attributes
1122 ********************************************************************/
1123
1124static 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
1179done:
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
1190static 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
1224static 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"
1282DNS_ERROR DoDNSUpdate(char *pszServerName,
1283 const char *pszDomainName,
1284 const char *pszHostName,
1285 const struct in_addr *iplist, int num_addrs );
1286
1287
1288static 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
1369done:
1370
1371 SAFE_FREE( root_domain );
1372
1373 return status;
1374}
1375
1376static 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**********************************************************/
1408static 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
1421static 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
1439int 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 saf_join_store(ads->server.workgroup, ads->config.ldap_server_name);
1579 saf_join_store(ads->server.realm, ads->config.ldap_server_name);
1580
1581 /* Verify that everything is ok */
1582
1583 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1584 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1585 goto fail;
1586 }
1587
1588 /* create the dNSHostName & servicePrincipalName values */
1589
1590 status = net_set_machine_spn( ctx, ads );
1591 if ( !ADS_ERR_OK(status) ) {
1592
1593 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1594 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1595 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1596
1597 /* Disable the machine account in AD. Better to fail than to leave
1598 a confused admin. */
1599
1600 if ( net_ads_leave( 0, NULL ) != 0 ) {
1601 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1602 }
1603
1604 /* clear out the machine password */
1605
1606 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1607 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1608
1609 nt_status = ads_ntstatus(status);
1610 goto fail;
1611 }
1612
1613 if ( !net_derive_salting_principal( ctx, ads ) ) {
1614 DEBUG(1,("Failed to determine salting principal\n"));
1615 goto fail;
1616 }
1617
1618 if ( createupn ) {
1619 pstring upn;
1620
1621 /* default to using the short UPN name */
1622 if ( !machineupn ) {
1623 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1624 ads->config.realm );
1625 machineupn = upn;
1626 }
1627
1628 status = net_set_machine_upn( ctx, ads, machineupn );
1629 if ( !ADS_ERR_OK(status) ) {
1630 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1631 }
1632 }
1633
1634 /* Try to set the operatingSystem attributes if asked */
1635
1636 if ( os_name && os_version ) {
1637 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1638 if ( !ADS_ERR_OK(status) ) {
1639 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1640 "Are you a Domain Admin?\n");
1641 }
1642 }
1643
1644 /* Now build the keytab, using the same ADS connection */
1645
1646 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1647 DEBUG(1,("Error creating host keytab!\n"));
1648 }
1649
1650#if defined(WITH_DNS_UPDATES)
1651 /* We enter this block with user creds */
1652 ads_kdestroy( NULL );
1653 ads_destroy(&ads);
1654 ads = NULL;
1655
1656 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1657 /* kinit with the machine password */
1658
1659 use_in_memory_ccache();
1660 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1661 ads->auth.password = secrets_fetch_machine_password(
1662 lp_workgroup(), NULL, NULL );
1663 ads->auth.realm = SMB_STRDUP( lp_realm() );
1664 ads_kinit_password( ads );
1665 }
1666
1667 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1668 d_fprintf( stderr, "DNS update failed!\n" );
1669 }
1670
1671 /* exit from this block using machine creds */
1672#endif
1673
1674 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1675
1676 SAFE_FREE(machine_account);
1677 TALLOC_FREE( ctx );
1678 ads_destroy(&ads);
1679
1680 return 0;
1681
1682fail:
1683 /* issue an overall failure message at the end. */
1684 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1685
1686 SAFE_FREE(machine_account);
1687 TALLOC_FREE( ctx );
1688 ads_destroy(&ads);
1689
1690 return -1;
1691
1692}
1693
1694/*******************************************************************
1695 ********************************************************************/
1696
1697static int net_ads_dns_usage(int argc, const char **argv)
1698{
1699#if defined(WITH_DNS_UPDATES)
1700 d_printf("net ads dns <command>\n");
1701 d_printf("Valid commands:\n");
1702 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1703
1704 return 0;
1705#else
1706 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1707 return -1;
1708#endif
1709}
1710
1711/*******************************************************************
1712 ********************************************************************/
1713
1714static int net_ads_dns_register(int argc, const char **argv)
1715{
1716#if defined(WITH_DNS_UPDATES)
1717 ADS_STRUCT *ads;
1718 ADS_STATUS status;
1719 TALLOC_CTX *ctx;
1720
1721#ifdef DEVELOPER
1722 talloc_enable_leak_report();
1723#endif
1724
1725 if (argc > 0) {
1726 d_fprintf(stderr, "net ads dns register\n");
1727 return -1;
1728 }
1729
1730 if (!(ctx = talloc_init("net_ads_dns"))) {
1731 d_fprintf(stderr, "Could not initialise talloc context\n");
1732 return -1;
1733 }
1734
1735 status = ads_startup(True, &ads);
1736 if ( !ADS_ERR_OK(status) ) {
1737 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1738 TALLOC_FREE(ctx);
1739 return -1;
1740 }
1741
1742 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1743 d_fprintf( stderr, "DNS update failed!\n" );
1744 ads_destroy( &ads );
1745 TALLOC_FREE( ctx );
1746 return -1;
1747 }
1748
1749 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1750
1751 ads_destroy(&ads);
1752 TALLOC_FREE( ctx );
1753
1754 return 0;
1755#else
1756 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1757 return -1;
1758#endif
1759}
1760
1761#if defined(WITH_DNS_UPDATES)
1762DNS_ERROR do_gethostbyname(const char *server, const char *host);
1763#endif
1764
1765static int net_ads_dns_gethostbyname(int argc, const char **argv)
1766{
1767#if defined(WITH_DNS_UPDATES)
1768 DNS_ERROR err;
1769
1770#ifdef DEVELOPER
1771 talloc_enable_leak_report();
1772#endif
1773
1774 if (argc != 2) {
1775 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1776 "<name>\n");
1777 return -1;
1778 }
1779
1780 err = do_gethostbyname(argv[0], argv[1]);
1781
1782 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1783#endif
1784 return 0;
1785}
1786
1787static int net_ads_dns(int argc, const char *argv[])
1788{
1789 struct functable func[] = {
1790 {"REGISTER", net_ads_dns_register},
1791 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1792 {NULL, NULL}
1793 };
1794
1795 return net_run_function(argc, argv, func, net_ads_dns_usage);
1796}
1797
1798/*******************************************************************
1799 ********************************************************************/
1800
1801int net_ads_printer_usage(int argc, const char **argv)
1802{
1803 d_printf(
1804"\nnet ads printer search <printer>"
1805"\n\tsearch for a printer in the directory\n"
1806"\nnet ads printer info <printer> <server>"
1807"\n\tlookup info in directory for printer on server"
1808"\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1809"\nnet ads printer publish <printername>"
1810"\n\tpublish printer in directory"
1811"\n\t(note: printer name is required)\n"
1812"\nnet ads printer remove <printername>"
1813"\n\tremove printer from directory"
1814"\n\t(note: printer name is required)\n");
1815 return -1;
1816}
1817
1818/*******************************************************************
1819 ********************************************************************/
1820
1821static int net_ads_printer_search(int argc, const char **argv)
1822{
1823 ADS_STRUCT *ads;
1824 ADS_STATUS rc;
1825 LDAPMessage *res = NULL;
1826
1827 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1828 return -1;
1829 }
1830
1831 rc = ads_find_printers(ads, &res);
1832
1833 if (!ADS_ERR_OK(rc)) {
1834 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1835 ads_msgfree(ads, res);
1836 ads_destroy(&ads);
1837 return -1;
1838 }
1839
1840 if (ads_count_replies(ads, res) == 0) {
1841 d_fprintf(stderr, "No results found\n");
1842 ads_msgfree(ads, res);
1843 ads_destroy(&ads);
1844 return -1;
1845 }
1846
1847 ads_dump(ads, res);
1848 ads_msgfree(ads, res);
1849 ads_destroy(&ads);
1850 return 0;
1851}
1852
1853static int net_ads_printer_info(int argc, const char **argv)
1854{
1855 ADS_STRUCT *ads;
1856 ADS_STATUS rc;
1857 const char *servername, *printername;
1858 LDAPMessage *res = NULL;
1859
1860 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1861 return -1;
1862 }
1863
1864 if (argc > 0) {
1865 printername = argv[0];
1866 } else {
1867 printername = "*";
1868 }
1869
1870 if (argc > 1) {
1871 servername = argv[1];
1872 } else {
1873 servername = global_myname();
1874 }
1875
1876 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1877
1878 if (!ADS_ERR_OK(rc)) {
1879 d_fprintf(stderr, "Server '%s' not found: %s\n",
1880 servername, ads_errstr(rc));
1881 ads_msgfree(ads, res);
1882 ads_destroy(&ads);
1883 return -1;
1884 }
1885
1886 if (ads_count_replies(ads, res) == 0) {
1887 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1888 ads_msgfree(ads, res);
1889 ads_destroy(&ads);
1890 return -1;
1891 }
1892
1893 ads_dump(ads, res);
1894 ads_msgfree(ads, res);
1895 ads_destroy(&ads);
1896
1897 return 0;
1898}
1899
1900void do_drv_upgrade_printer(int msg_type, struct process_id src,
1901 void *buf, size_t len, void *private_data)
1902{
1903 return;
1904}
1905
1906static int net_ads_printer_publish(int argc, const char **argv)
1907{
1908 ADS_STRUCT *ads;
1909 ADS_STATUS rc;
1910 const char *servername, *printername;
1911 struct cli_state *cli;
1912 struct rpc_pipe_client *pipe_hnd;
1913 struct in_addr server_ip;
1914 NTSTATUS nt_status;
1915 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1916 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1917 char *prt_dn, *srv_dn, **srv_cn;
1918 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1919 LDAPMessage *res = NULL;
1920
1921 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1922 talloc_destroy(mem_ctx);
1923 return -1;
1924 }
1925
1926 if (argc < 1) {
1927 talloc_destroy(mem_ctx);
1928 return net_ads_printer_usage(argc, argv);
1929 }
1930
1931 printername = argv[0];
1932
1933 if (argc == 2) {
1934 servername = argv[1];
1935 } else {
1936 servername = global_myname();
1937 }
1938
1939 /* Get printer data from SPOOLSS */
1940
1941 resolve_name(servername, &server_ip, 0x20);
1942
1943 nt_status = cli_full_connection(&cli, global_myname(), servername,
1944 &server_ip, 0,
1945 "IPC$", "IPC",
1946 opt_user_name, opt_workgroup,
1947 opt_password ? opt_password : "",
1948 CLI_FULL_CONNECTION_USE_KERBEROS,
1949 Undefined, NULL);
1950
1951 if (NT_STATUS_IS_ERR(nt_status)) {
1952 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1953 "for %s\n", servername, printername);
1954 ads_destroy(&ads);
1955 talloc_destroy(mem_ctx);
1956 return -1;
1957 }
1958
1959 /* Publish on AD server */
1960
1961 ads_find_machine_acct(ads, &res, servername);
1962
1963 if (ads_count_replies(ads, res) == 0) {
1964 d_fprintf(stderr, "Could not find machine account for server %s\n",
1965 servername);
1966 ads_destroy(&ads);
1967 talloc_destroy(mem_ctx);
1968 return -1;
1969 }
1970
1971 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1972 srv_cn = ldap_explode_dn(srv_dn, 1);
1973
1974 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1975 printername_escaped = escape_rdn_val_string_alloc(printername);
1976 if (!srv_cn_escaped || !printername_escaped) {
1977 SAFE_FREE(srv_cn_escaped);
1978 SAFE_FREE(printername_escaped);
1979 d_fprintf(stderr, "Internal error, out of memory!");
1980 ads_destroy(&ads);
1981 talloc_destroy(mem_ctx);
1982 return -1;
1983 }
1984
1985 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
1986
1987 SAFE_FREE(srv_cn_escaped);
1988 SAFE_FREE(printername_escaped);
1989
1990 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1991 if (!pipe_hnd) {
1992 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1993 servername);
1994 SAFE_FREE(prt_dn);
1995 ads_destroy(&ads);
1996 talloc_destroy(mem_ctx);
1997 return -1;
1998 }
1999
2000 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
2001 printername))) {
2002 SAFE_FREE(prt_dn);
2003 ads_destroy(&ads);
2004 talloc_destroy(mem_ctx);
2005 return -1;
2006 }
2007
2008 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
2009 if (!ADS_ERR_OK(rc)) {
2010 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2011 SAFE_FREE(prt_dn);
2012 ads_destroy(&ads);
2013 talloc_destroy(mem_ctx);
2014 return -1;
2015 }
2016
2017 d_printf("published printer\n");
2018 SAFE_FREE(prt_dn);
2019 ads_destroy(&ads);
2020 talloc_destroy(mem_ctx);
2021
2022 return 0;
2023}
2024
2025static int net_ads_printer_remove(int argc, const char **argv)
2026{
2027 ADS_STRUCT *ads;
2028 ADS_STATUS rc;
2029 const char *servername;
2030 char *prt_dn;
2031 LDAPMessage *res = NULL;
2032
2033 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2034 return -1;
2035 }
2036
2037 if (argc < 1) {
2038 return net_ads_printer_usage(argc, argv);
2039 }
2040
2041 if (argc > 1) {
2042 servername = argv[1];
2043 } else {
2044 servername = global_myname();
2045 }
2046
2047 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2048
2049 if (!ADS_ERR_OK(rc)) {
2050 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2051 ads_msgfree(ads, res);
2052 ads_destroy(&ads);
2053 return -1;
2054 }
2055
2056 if (ads_count_replies(ads, res) == 0) {
2057 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2058 ads_msgfree(ads, res);
2059 ads_destroy(&ads);
2060 return -1;
2061 }
2062
2063 prt_dn = ads_get_dn(ads, res);
2064 ads_msgfree(ads, res);
2065 rc = ads_del_dn(ads, prt_dn);
2066 ads_memfree(ads, prt_dn);
2067
2068 if (!ADS_ERR_OK(rc)) {
2069 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2070 ads_destroy(&ads);
2071 return -1;
2072 }
2073
2074 ads_destroy(&ads);
2075 return 0;
2076}
2077
2078static int net_ads_printer(int argc, const char **argv)
2079{
2080 struct functable func[] = {
2081 {"SEARCH", net_ads_printer_search},
2082 {"INFO", net_ads_printer_info},
2083 {"PUBLISH", net_ads_printer_publish},
2084 {"REMOVE", net_ads_printer_remove},
2085 {NULL, NULL}
2086 };
2087
2088 return net_run_function(argc, argv, func, net_ads_printer_usage);
2089}
2090
2091
2092static int net_ads_password(int argc, const char **argv)
2093{
2094 ADS_STRUCT *ads;
2095 const char *auth_principal = opt_user_name;
2096 const char *auth_password = opt_password;
2097 char *realm = NULL;
2098 char *new_password = NULL;
2099 char *c, *prompt;
2100 const char *user;
2101 ADS_STATUS ret;
2102
2103 if (opt_user_name == NULL || opt_password == NULL) {
2104 d_fprintf(stderr, "You must supply an administrator username/password\n");
2105 return -1;
2106 }
2107
2108 if (argc < 1) {
2109 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2110 return -1;
2111 }
2112
2113 user = argv[0];
2114 if (!strchr_m(user, '@')) {
2115 asprintf(&c, "%s@%s", argv[0], lp_realm());
2116 user = c;
2117 }
2118
2119 use_in_memory_ccache();
2120 c = strchr_m(auth_principal, '@');
2121 if (c) {
2122 realm = ++c;
2123 } else {
2124 realm = lp_realm();
2125 }
2126
2127 /* use the realm so we can eventually change passwords for users
2128 in realms other than default */
2129 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2130 return -1;
2131 }
2132
2133 /* we don't actually need a full connect, but it's the easy way to
2134 fill in the KDC's addresss */
2135 ads_connect(ads);
2136
2137 if (!ads || !ads->config.realm) {
2138 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2139 return -1;
2140 }
2141
2142 if (argv[1]) {
2143 new_password = (char *)argv[1];
2144 } else {
2145 asprintf(&prompt, "Enter new password for %s:", user);
2146 new_password = getpass(prompt);
2147 free(prompt);
2148 }
2149
2150 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2151 auth_password, user, new_password, ads->auth.time_offset);
2152 if (!ADS_ERR_OK(ret)) {
2153 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2154 ads_destroy(&ads);
2155 return -1;
2156 }
2157
2158 d_printf("Password change for %s completed.\n", user);
2159 ads_destroy(&ads);
2160
2161 return 0;
2162}
2163
2164int net_ads_changetrustpw(int argc, const char **argv)
2165{
2166 ADS_STRUCT *ads;
2167 char *host_principal;
2168 fstring my_name;
2169 ADS_STATUS ret;
2170
2171 if (!secrets_init()) {
2172 DEBUG(1,("Failed to initialise secrets database\n"));
2173 return -1;
2174 }
2175
2176 net_use_krb_machine_account();
2177
2178 use_in_memory_ccache();
2179
2180 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2181 return -1;
2182 }
2183
2184 fstrcpy(my_name, global_myname());
2185 strlower_m(my_name);
2186 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2187 d_printf("Changing password for principal: %s\n", host_principal);
2188
2189 ret = ads_change_trust_account_password(ads, host_principal);
2190
2191 if (!ADS_ERR_OK(ret)) {
2192 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2193 ads_destroy(&ads);
2194 SAFE_FREE(host_principal);
2195 return -1;
2196 }
2197
2198 d_printf("Password change for principal %s succeeded.\n", host_principal);
2199
2200 if (lp_use_kerberos_keytab()) {
2201 d_printf("Attempting to update system keytab with new password.\n");
2202 if (ads_keytab_create_default(ads)) {
2203 d_printf("Failed to update system keytab.\n");
2204 }
2205 }
2206
2207 ads_destroy(&ads);
2208 SAFE_FREE(host_principal);
2209
2210 return 0;
2211}
2212
2213/*
2214 help for net ads search
2215*/
2216static int net_ads_search_usage(int argc, const char **argv)
2217{
2218 d_printf(
2219 "\nnet ads search <expression> <attributes...>\n"\
2220 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2221 "The expression is a standard LDAP search expression, and the\n"\
2222 "attributes are a list of LDAP fields to show in the results\n\n"\
2223 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2224 );
2225 net_common_flags_usage(argc, argv);
2226 return -1;
2227}
2228
2229
2230/*
2231 general ADS search function. Useful in diagnosing problems in ADS
2232*/
2233static int net_ads_search(int argc, const char **argv)
2234{
2235 ADS_STRUCT *ads;
2236 ADS_STATUS rc;
2237 const char *ldap_exp;
2238 const char **attrs;
2239 LDAPMessage *res = NULL;
2240
2241 if (argc < 1) {
2242 return net_ads_search_usage(argc, argv);
2243 }
2244
2245 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2246 return -1;
2247 }
2248
2249 ldap_exp = argv[0];
2250 attrs = (argv + 1);
2251
2252 rc = ads_do_search_all(ads, ads->config.bind_path,
2253 LDAP_SCOPE_SUBTREE,
2254 ldap_exp, attrs, &res);
2255 if (!ADS_ERR_OK(rc)) {
2256 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2257 ads_destroy(&ads);
2258 return -1;
2259 }
2260
2261 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2262
2263 /* dump the results */
2264 ads_dump(ads, res);
2265
2266 ads_msgfree(ads, res);
2267 ads_destroy(&ads);
2268
2269 return 0;
2270}
2271
2272
2273/*
2274 help for net ads search
2275*/
2276static int net_ads_dn_usage(int argc, const char **argv)
2277{
2278 d_printf(
2279 "\nnet ads dn <dn> <attributes...>\n"\
2280 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2281 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2282 "to show in the results\n\n"\
2283 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2284 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2285 );
2286 net_common_flags_usage(argc, argv);
2287 return -1;
2288}
2289
2290
2291/*
2292 general ADS search function. Useful in diagnosing problems in ADS
2293*/
2294static int net_ads_dn(int argc, const char **argv)
2295{
2296 ADS_STRUCT *ads;
2297 ADS_STATUS rc;
2298 const char *dn;
2299 const char **attrs;
2300 LDAPMessage *res = NULL;
2301
2302 if (argc < 1) {
2303 return net_ads_dn_usage(argc, argv);
2304 }
2305
2306 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2307 return -1;
2308 }
2309
2310 dn = argv[0];
2311 attrs = (argv + 1);
2312
2313 rc = ads_do_search_all(ads, dn,
2314 LDAP_SCOPE_BASE,
2315 "(objectclass=*)", attrs, &res);
2316 if (!ADS_ERR_OK(rc)) {
2317 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2318 ads_destroy(&ads);
2319 return -1;
2320 }
2321
2322 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2323
2324 /* dump the results */
2325 ads_dump(ads, res);
2326
2327 ads_msgfree(ads, res);
2328 ads_destroy(&ads);
2329
2330 return 0;
2331}
2332
2333/*
2334 help for net ads sid search
2335*/
2336static int net_ads_sid_usage(int argc, const char **argv)
2337{
2338 d_printf(
2339 "\nnet ads sid <sid> <attributes...>\n"\
2340 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2341 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2342 "to show in the results\n\n"\
2343 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2344 );
2345 net_common_flags_usage(argc, argv);
2346 return -1;
2347}
2348
2349
2350/*
2351 general ADS search function. Useful in diagnosing problems in ADS
2352*/
2353static int net_ads_sid(int argc, const char **argv)
2354{
2355 ADS_STRUCT *ads;
2356 ADS_STATUS rc;
2357 const char *sid_string;
2358 const char **attrs;
2359 LDAPMessage *res = NULL;
2360 DOM_SID sid;
2361
2362 if (argc < 1) {
2363 return net_ads_sid_usage(argc, argv);
2364 }
2365
2366 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2367 return -1;
2368 }
2369
2370 sid_string = argv[0];
2371 attrs = (argv + 1);
2372
2373 if (!string_to_sid(&sid, sid_string)) {
2374 d_fprintf(stderr, "could not convert sid\n");
2375 ads_destroy(&ads);
2376 return -1;
2377 }
2378
2379 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2380 if (!ADS_ERR_OK(rc)) {
2381 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2382 ads_destroy(&ads);
2383 return -1;
2384 }
2385
2386 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2387
2388 /* dump the results */
2389 ads_dump(ads, res);
2390
2391 ads_msgfree(ads, res);
2392 ads_destroy(&ads);
2393
2394 return 0;
2395}
2396
2397
2398static int net_ads_keytab_usage(int argc, const char **argv)
2399{
2400 d_printf(
2401 "net ads keytab <COMMAND>\n"\
2402"<COMMAND> can be either:\n"\
2403" CREATE Creates a fresh keytab\n"\
2404" ADD Adds new service principal\n"\
2405" FLUSH Flushes out all keytab entries\n"\
2406" HELP Prints this help message\n"\
2407"The ADD command will take arguments, the other commands\n"\
2408"will not take any arguments. The arguments given to ADD\n"\
2409"should be a list of principals to add. For example, \n"\
2410" net ads keytab add srv1 srv2\n"\
2411"will add principals for the services srv1 and srv2 to the\n"\
2412"system's keytab.\n"\
2413"\n"
2414 );
2415 return -1;
2416}
2417
2418static int net_ads_keytab_flush(int argc, const char **argv)
2419{
2420 int ret;
2421 ADS_STRUCT *ads;
2422
2423 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2424 return -1;
2425 }
2426 ret = ads_keytab_flush(ads);
2427 ads_destroy(&ads);
2428 return ret;
2429}
2430
2431static int net_ads_keytab_add(int argc, const char **argv)
2432{
2433 int i;
2434 int ret = 0;
2435 ADS_STRUCT *ads;
2436
2437 d_printf("Processing principals to add...\n");
2438 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2439 return -1;
2440 }
2441 for (i = 0; i < argc; i++) {
2442 ret |= ads_keytab_add_entry(ads, argv[i]);
2443 }
2444 ads_destroy(&ads);
2445 return ret;
2446}
2447
2448static int net_ads_keytab_create(int argc, const char **argv)
2449{
2450 ADS_STRUCT *ads;
2451 int ret;
2452
2453 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2454 return -1;
2455 }
2456 ret = ads_keytab_create_default(ads);
2457 ads_destroy(&ads);
2458 return ret;
2459}
2460
2461int net_ads_keytab(int argc, const char **argv)
2462{
2463 struct functable func[] = {
2464 {"CREATE", net_ads_keytab_create},
2465 {"ADD", net_ads_keytab_add},
2466 {"FLUSH", net_ads_keytab_flush},
2467 {"HELP", net_ads_keytab_usage},
2468 {NULL, NULL}
2469 };
2470
2471 if (!lp_use_kerberos_keytab()) {
2472 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2473use keytab functions.\n");
2474 }
2475
2476 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2477}
2478
2479int net_ads_help(int argc, const char **argv)
2480{
2481 struct functable func[] = {
2482 {"USER", net_ads_user_usage},
2483 {"GROUP", net_ads_group_usage},
2484 {"PRINTER", net_ads_printer_usage},
2485 {"SEARCH", net_ads_search_usage},
2486 {"INFO", net_ads_info},
2487 {"JOIN", net_ads_join_usage},
2488 {"DNS", net_ads_dns_usage},
2489 {"LEAVE", net_ads_leave},
2490 {"STATUS", net_ads_status},
2491 {"PASSWORD", net_ads_password},
2492 {"CHANGETRUSTPW", net_ads_changetrustpw},
2493 {NULL, NULL}
2494 };
2495
2496 return net_run_function(argc, argv, func, net_ads_usage);
2497}
2498
2499int net_ads(int argc, const char **argv)
2500{
2501 struct functable func[] = {
2502 {"INFO", net_ads_info},
2503 {"JOIN", net_ads_join},
2504 {"TESTJOIN", net_ads_testjoin},
2505 {"LEAVE", net_ads_leave},
2506 {"STATUS", net_ads_status},
2507 {"USER", net_ads_user},
2508 {"GROUP", net_ads_group},
2509 {"DNS", net_ads_dns},
2510 {"PASSWORD", net_ads_password},
2511 {"CHANGETRUSTPW", net_ads_changetrustpw},
2512 {"PRINTER", net_ads_printer},
2513 {"SEARCH", net_ads_search},
2514 {"DN", net_ads_dn},
2515 {"SID", net_ads_sid},
2516 {"WORKGROUP", net_ads_workgroup},
2517 {"LOOKUP", net_ads_lookup},
2518 {"KEYTAB", net_ads_keytab},
2519 {"GPO", net_ads_gpo},
2520 {"HELP", net_ads_help},
2521 {NULL, NULL}
2522 };
2523
2524 return net_run_function(argc, argv, func, net_ads_usage);
2525}
2526
2527#else
2528
2529static int net_ads_noads(void)
2530{
2531 d_fprintf(stderr, "ADS support not compiled in\n");
2532 return -1;
2533}
2534
2535int net_ads_keytab(int argc, const char **argv)
2536{
2537 return net_ads_noads();
2538}
2539
2540int net_ads_usage(int argc, const char **argv)
2541{
2542 return net_ads_noads();
2543}
2544
2545int net_ads_help(int argc, const char **argv)
2546{
2547 return net_ads_noads();
2548}
2549
2550int net_ads_changetrustpw(int argc, const char **argv)
2551{
2552 return net_ads_noads();
2553}
2554
2555int net_ads_join(int argc, const char **argv)
2556{
2557 return net_ads_noads();
2558}
2559
2560int net_ads_user(int argc, const char **argv)
2561{
2562 return net_ads_noads();
2563}
2564
2565int net_ads_group(int argc, const char **argv)
2566{
2567 return net_ads_noads();
2568}
2569
2570/* this one shouldn't display a message */
2571int net_ads_check(void)
2572{
2573 return -1;
2574}
2575
2576int net_ads_check_our_domain(void)
2577{
2578 return -1;
2579}
2580
2581int net_ads(int argc, const char **argv)
2582{
2583 return net_ads_usage(argc, argv);
2584}
2585
2586#endif /* WITH_ADS */
Note: See TracBrowser for help on using the repository browser.