r17086: Re-add ability to contact remote domain controllers with the "net ads"
[kai/samba.git] / source3 / utils / net_ads.c
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 /* Macro for checking RPC error codes to make things more readable */
28
29 #if 0
30 #define CHECK_RPC_ERR(rpc, msg) \
31         if (!NT_STATUS_IS_OK(result = rpc)) { \
32                 DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
33                 goto done; \
34         }
35
36 #define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
37         if (!NT_STATUS_IS_OK(result = rpc)) { \
38                 DEBUG(0, debug_args); \
39                 goto done; \
40         }
41
42 #endif
43 #ifdef HAVE_ADS
44
45 int net_ads_usage(int argc, const char **argv)
46 {
47         d_printf(
48 "\nnet ads join <org_unit>"\
49 "\n\tjoins the local machine to a ADS realm\n"\
50 "\nnet ads leave"\
51 "\n\tremoves the local machine from a ADS realm\n"\
52 "\nnet ads testjoin"\
53 "\n\ttests that an exiting join is OK\n"\
54 "\nnet ads user"\
55 "\n\tlist, add, or delete users in the realm\n"\
56 "\nnet ads group"\
57 "\n\tlist, add, or delete groups in the realm\n"\
58 "\nnet ads info"\
59 "\n\tshows some info on the server\n"\
60 "\nnet ads status"\
61 "\n\tdump the machine account details to stdout\n"
62 "\nnet ads lookup"\
63 "\n\tperform a CLDAP search on the server\n"
64 "\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
65 "\n\tchange a user's password using an admin account"\
66 "\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
67 "\nnet ads changetrustpw"\
68 "\n\tchange the trust account password of this machine in the AD tree\n"\
69 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
70 "\n\t lookup, add, or remove directory entry for a printer\n"\
71 "\nnet ads search"\
72 "\n\tperform a raw LDAP search and dump the results\n"
73 "\nnet ads dn"\
74 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
75 "\nnet ads sid"\
76 "\n\tperform a raw LDAP search and dump attributes of a particular SID\n"
77 "\nnet ads keytab"\
78 "\n\tcreates and updates the kerberos system keytab file\n"
79                 );
80         return -1;
81 }
82
83 /* when we do not have sufficient input parameters to contact a remote domain
84  * we always fall back to our own realm - Guenther*/
85
86 static const char *assume_own_realm(void)
87 {
88         if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
89                 return lp_realm();
90         }
91
92         return NULL;
93 }
94
95 /*
96   do a cldap netlogon query
97 */
98 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
99 {
100         struct cldap_netlogon_reply reply;
101
102         if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
103                 d_fprintf(stderr, "CLDAP query failed!\n");
104                 return -1;
105         }
106
107         d_printf("Information for Domain Controller: %s\n\n", 
108                 inet_ntoa(ads->ldap_ip));
109
110         d_printf("Response Type: ");
111         switch (reply.type) {
112         case SAMLOGON_AD_UNK_R:
113                 d_printf("SAMLOGON\n");
114                 break;
115         case SAMLOGON_AD_R:
116                 d_printf("SAMLOGON_USER\n");
117                 break;
118         default:
119                 d_printf("0x%x\n", reply.type);
120                 break;
121         }
122         d_printf("GUID: %s\n", 
123                  smb_uuid_string_static(smb_uuid_unpack_static(reply.guid))); 
124         d_printf("Flags:\n"
125                  "\tIs a PDC:                                   %s\n"
126                  "\tIs a GC of the forest:                      %s\n"
127                  "\tIs an LDAP server:                          %s\n"
128                  "\tSupports DS:                                %s\n"
129                  "\tIs running a KDC:                           %s\n"
130                  "\tIs running time services:                   %s\n"
131                  "\tIs the closest DC:                          %s\n"
132                  "\tIs writable:                                %s\n"
133                  "\tHas a hardware clock:                       %s\n"
134                  "\tIs a non-domain NC serviced by LDAP server: %s\n",
135                  (reply.flags & ADS_PDC) ? "yes" : "no",
136                  (reply.flags & ADS_GC) ? "yes" : "no",
137                  (reply.flags & ADS_LDAP) ? "yes" : "no",
138                  (reply.flags & ADS_DS) ? "yes" : "no",
139                  (reply.flags & ADS_KDC) ? "yes" : "no",
140                  (reply.flags & ADS_TIMESERV) ? "yes" : "no",
141                  (reply.flags & ADS_CLOSEST) ? "yes" : "no",
142                  (reply.flags & ADS_WRITABLE) ? "yes" : "no",
143                  (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
144                  (reply.flags & ADS_NDNC) ? "yes" : "no");
145
146         printf("Forest:\t\t\t%s\n", reply.forest);
147         printf("Domain:\t\t\t%s\n", reply.domain);
148         printf("Domain Controller:\t%s\n", reply.hostname);
149
150         printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
151         printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
152
153         if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
154         if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
155
156         printf("Site Name:\t\t%s\n", reply.site_name);
157         printf("Site Name (2):\t\t%s\n", reply.site_name_2);
158
159         d_printf("NT Version: %d\n", reply.version);
160         d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
161         d_printf("LM20 Token: %.2x\n", reply.lm20_token);
162
163         return 0;
164 }
165
166
167 /*
168   this implements the CLDAP based netlogon lookup requests
169   for finding the domain controller of a ADS domain
170 */
171 static int net_ads_lookup(int argc, const char **argv)
172 {
173         ADS_STRUCT *ads;
174         ADS_STATUS status;
175         const char *realm = assume_own_realm();
176
177         ads = ads_init(realm, opt_target_workgroup, opt_host);
178         if (ads) {
179                 ads->auth.flags |= ADS_AUTH_NO_BIND;
180         }
181
182         status = ads_connect(ads);
183         if (!ADS_ERR_OK(status) || !ads) {
184                 d_fprintf(stderr, "Didn't find the cldap server!\n");
185                 return -1;
186         }
187         
188         if (!ads->config.realm) {
189                 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
190                 ads->ldap_port = 389;
191         }
192
193         return net_ads_cldap_netlogon(ads);
194 }
195
196
197
198 static int net_ads_info(int argc, const char **argv)
199 {
200         ADS_STRUCT *ads;
201         const char *realm = assume_own_realm();
202
203         if ( (ads = ads_init(realm, opt_target_workgroup, opt_host)) != NULL ) {
204                 ads->auth.flags |= ADS_AUTH_NO_BIND;
205         }
206
207         ads_connect(ads);
208
209         if (!ads || !ads->config.realm) {
210                 d_fprintf(stderr, "Didn't find the ldap server!\n");
211                 return -1;
212         }
213
214         /* Try to set the server's current time since we didn't do a full
215            TCP LDAP session initially */
216
217         if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
218                 d_fprintf( stderr, "Failed to get server's current time!\n");
219         }
220
221         d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
222         d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
223         d_printf("Realm: %s\n", ads->config.realm);
224         d_printf("Bind Path: %s\n", ads->config.bind_path);
225         d_printf("LDAP port: %d\n", ads->ldap_port);
226         d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
227
228         d_printf("KDC server: %s\n", ads->auth.kdc_server );
229         d_printf("Server time offset: %d\n", ads->auth.time_offset );
230
231         return 0;
232 }
233
234 static void use_in_memory_ccache(void) {
235         /* Use in-memory credentials cache so we do not interfere with
236          * existing credentials */
237         setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
238 }
239
240 static ADS_STRUCT *ads_startup(BOOL only_own_domain)
241 {
242         ADS_STRUCT *ads;
243         ADS_STATUS status;
244         BOOL need_password = False;
245         BOOL second_time = False;
246         char *cp;
247         const char *realm = NULL;
248         
249         /* lp_realm() should be handled by a command line param, 
250            However, the join requires that realm be set in smb.conf
251            and compares our realm with the remote server's so this is
252            ok until someone needs more flexibility */
253
254         if (only_own_domain) {
255                 realm = lp_realm();
256         }
257    
258         ads = ads_init(realm, opt_target_workgroup, opt_host);
259
260         if (!opt_user_name) {
261                 opt_user_name = "administrator";
262         }
263
264         if (opt_user_specified) {
265                 need_password = True;
266         }
267
268 retry:
269         if (!opt_password && need_password && !opt_machine_pass) {
270                 char *prompt;
271                 asprintf(&prompt,"%s's password: ", opt_user_name);
272                 opt_password = getpass(prompt);
273                 free(prompt);
274         }
275
276         if (opt_password) {
277                 use_in_memory_ccache();
278                 ads->auth.password = smb_xstrdup(opt_password);
279         }
280
281         ads->auth.user_name = smb_xstrdup(opt_user_name);
282
283        /*
284         * If the username is of the form "name@realm", 
285         * extract the realm and convert to upper case.
286         * This is only used to establish the connection.
287         */
288        if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
289                *cp++ = '\0';
290                ads->auth.realm = smb_xstrdup(cp);
291                strupper_m(ads->auth.realm);
292        }
293
294         status = ads_connect(ads);
295
296         if (!ADS_ERR_OK(status)) {
297                 if (!need_password && !second_time) {
298                         need_password = True;
299                         second_time = True;
300                         goto retry;
301                 } else {
302                         DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
303                         ads_destroy(&ads);
304                         return NULL;
305                 }
306         }
307         return ads;
308 }
309
310
311 /*
312   Check to see if connection can be made via ads.
313   ads_startup() stores the password in opt_password if it needs to so
314   that rpc or rap can use it without re-prompting.
315 */
316 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
317 {
318         ADS_STRUCT *ads;
319         ADS_STATUS status;
320
321         if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
322                 return -1;
323         }
324
325         ads->auth.flags |= ADS_AUTH_NO_BIND;
326
327         status = ads_connect(ads);
328         if ( !ADS_ERR_OK(status) ) {
329                 return -1;
330         }
331
332         ads_destroy(&ads);
333         return 0;
334 }
335
336 int net_ads_check_our_domain(void)
337 {
338         return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
339 }
340
341 int net_ads_check(void)
342 {
343         return net_ads_check_int(NULL, opt_workgroup, opt_host);
344 }
345 /* 
346    determine the netbios workgroup name for a domain
347  */
348 static int net_ads_workgroup(int argc, const char **argv)
349 {
350         ADS_STRUCT *ads;
351         ADS_STATUS status;
352         const char *realm = assume_own_realm();
353         struct cldap_netlogon_reply reply;
354
355         ads = ads_init(realm, opt_target_workgroup, opt_host);
356         if (ads) {
357                 ads->auth.flags |= ADS_AUTH_NO_BIND;
358         }
359
360         status = ads_connect(ads);
361         if (!ADS_ERR_OK(status) || !ads) {
362                 d_fprintf(stderr, "Didn't find the cldap server!\n");
363                 return -1;
364         }
365         
366         if (!ads->config.realm) {
367                 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
368                 ads->ldap_port = 389;
369         }
370         
371         if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
372                 d_fprintf(stderr, "CLDAP query failed!\n");
373                 return -1;
374         }
375
376         d_printf("Workgroup: %s\n", reply.netbios_domain);
377
378         ads_destroy(&ads);
379         
380         return 0;
381 }
382
383
384
385 static BOOL usergrp_display(char *field, void **values, void *data_area)
386 {
387         char **disp_fields = (char **) data_area;
388
389         if (!field) { /* must be end of record */
390                 if (disp_fields[0]) {
391                         if (!strchr_m(disp_fields[0], '$')) {
392                                 if (disp_fields[1])
393                                         d_printf("%-21.21s %s\n", 
394                                                disp_fields[0], disp_fields[1]);
395                                 else
396                                         d_printf("%s\n", disp_fields[0]);
397                         }
398                 }
399                 SAFE_FREE(disp_fields[0]);
400                 SAFE_FREE(disp_fields[1]);
401                 return True;
402         }
403         if (!values) /* must be new field, indicate string field */
404                 return True;
405         if (StrCaseCmp(field, "sAMAccountName") == 0) {
406                 disp_fields[0] = SMB_STRDUP((char *) values[0]);
407         }
408         if (StrCaseCmp(field, "description") == 0)
409                 disp_fields[1] = SMB_STRDUP((char *) values[0]);
410         return True;
411 }
412
413 static int net_ads_user_usage(int argc, const char **argv)
414 {
415         return net_help_user(argc, argv);
416
417
418 static int ads_user_add(int argc, const char **argv)
419 {
420         ADS_STRUCT *ads;
421         ADS_STATUS status;
422         char *upn, *userdn;
423         void *res=NULL;
424         int rc = -1;
425
426         if (argc < 1) return net_ads_user_usage(argc, argv);
427         
428         if (!(ads = ads_startup(False))) {
429                 return -1;
430         }
431
432         status = ads_find_user_acct(ads, &res, argv[0]);
433
434         if (!ADS_ERR_OK(status)) {
435                 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
436                 goto done;
437         }
438         
439         if (ads_count_replies(ads, res)) {
440                 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
441                 goto done;
442         }
443
444         if (opt_container == NULL) {
445                 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
446         }
447
448         status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
449
450         if (!ADS_ERR_OK(status)) {
451                 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
452                          ads_errstr(status));
453                 goto done;
454         }
455
456         /* if no password is to be set, we're done */
457         if (argc == 1) { 
458                 d_printf("User %s added\n", argv[0]);
459                 rc = 0;
460                 goto done;
461         }
462
463         /* try setting the password */
464         asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
465         status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1], 
466                                        ads->auth.time_offset);
467         safe_free(upn);
468         if (ADS_ERR_OK(status)) {
469                 d_printf("User %s added\n", argv[0]);
470                 rc = 0;
471                 goto done;
472         }
473
474         /* password didn't set, delete account */
475         d_fprintf(stderr, "Could not add user %s.  Error setting password %s\n",
476                  argv[0], ads_errstr(status));
477         ads_msgfree(ads, res);
478         status=ads_find_user_acct(ads, &res, argv[0]);
479         if (ADS_ERR_OK(status)) {
480                 userdn = ads_get_dn(ads, res);
481                 ads_del_dn(ads, userdn);
482                 ads_memfree(ads, userdn);
483         }
484
485  done:
486         if (res)
487                 ads_msgfree(ads, res);
488         ads_destroy(&ads);
489         return rc;
490 }
491
492 static int ads_user_info(int argc, const char **argv)
493 {
494         ADS_STRUCT *ads;
495         ADS_STATUS rc;
496         void *res;
497         const char *attrs[] = {"memberOf", NULL};
498         char *searchstring=NULL;
499         char **grouplist;
500         char *escaped_user;
501
502         if (argc < 1) {
503                 return net_ads_user_usage(argc, argv);
504         }
505
506         escaped_user = escape_ldap_string_alloc(argv[0]);
507
508         if (!escaped_user) {
509                 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
510                 return -1;
511         }
512
513         if (!(ads = ads_startup(False))) {
514                 SAFE_FREE(escaped_user);
515                 return -1;
516         }
517
518         asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
519         rc = ads_search(ads, &res, searchstring, attrs);
520         safe_free(searchstring);
521
522         if (!ADS_ERR_OK(rc)) {
523                 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
524                 ads_destroy(&ads);
525                 SAFE_FREE(escaped_user);
526                 return -1;
527         }
528         
529         grouplist = ldap_get_values((LDAP *)ads->ld,
530                                     (LDAPMessage *)res, "memberOf");
531
532         if (grouplist) {
533                 int i;
534                 char **groupname;
535                 for (i=0;grouplist[i];i++) {
536                         groupname = ldap_explode_dn(grouplist[i], 1);
537                         d_printf("%s\n", groupname[0]);
538                         ldap_value_free(groupname);
539                 }
540                 ldap_value_free(grouplist);
541         }
542         
543         ads_msgfree(ads, res);
544         ads_destroy(&ads);
545         SAFE_FREE(escaped_user);
546         return 0;
547 }
548
549 static int ads_user_delete(int argc, const char **argv)
550 {
551         ADS_STRUCT *ads;
552         ADS_STATUS rc;
553         void *res;
554         char *userdn;
555
556         if (argc < 1) {
557                 return net_ads_user_usage(argc, argv);
558         }
559         
560         if (!(ads = ads_startup(False))) {
561                 return -1;
562         }
563
564         rc = ads_find_user_acct(ads, &res, argv[0]);
565         if (!ADS_ERR_OK(rc)) {
566                 DEBUG(0, ("User %s does not exist\n", argv[0]));
567                 ads_destroy(&ads);
568                 return -1;
569         }
570         userdn = ads_get_dn(ads, res);
571         ads_msgfree(ads, res);
572         rc = ads_del_dn(ads, userdn);
573         ads_memfree(ads, userdn);
574         if (!ADS_ERR_OK(rc)) {
575                 d_printf("User %s deleted\n", argv[0]);
576                 ads_destroy(&ads);
577                 return 0;
578         }
579         d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0], 
580                  ads_errstr(rc));
581         ads_destroy(&ads);
582         return -1;
583 }
584
585 int net_ads_user(int argc, const char **argv)
586 {
587         struct functable func[] = {
588                 {"ADD", ads_user_add},
589                 {"INFO", ads_user_info},
590                 {"DELETE", ads_user_delete},
591                 {NULL, NULL}
592         };
593         ADS_STRUCT *ads;
594         ADS_STATUS rc;
595         const char *shortattrs[] = {"sAMAccountName", NULL};
596         const char *longattrs[] = {"sAMAccountName", "description", NULL};
597         char *disp_fields[2] = {NULL, NULL};
598         
599         if (argc == 0) {
600                 if (!(ads = ads_startup(False))) {
601                         return -1;
602                 }
603
604                 if (opt_long_list_entries)
605                         d_printf("\nUser name             Comment"\
606                                  "\n-----------------------------\n");
607
608                 rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
609                                           LDAP_SCOPE_SUBTREE,
610                                           "(objectCategory=user)", 
611                                           opt_long_list_entries ? longattrs :
612                                           shortattrs, usergrp_display, 
613                                           disp_fields);
614                 ads_destroy(&ads);
615                 return ADS_ERR_OK(rc) ? 0 : -1;
616         }
617
618         return net_run_function(argc, argv, func, net_ads_user_usage);
619 }
620
621 static int net_ads_group_usage(int argc, const char **argv)
622 {
623         return net_help_group(argc, argv);
624
625
626 static int ads_group_add(int argc, const char **argv)
627 {
628         ADS_STRUCT *ads;
629         ADS_STATUS status;
630         void *res=NULL;
631         int rc = -1;
632
633         if (argc < 1) {
634                 return net_ads_group_usage(argc, argv);
635         }
636         
637         if (!(ads = ads_startup(False))) {
638                 return -1;
639         }
640
641         status = ads_find_user_acct(ads, &res, argv[0]);
642
643         if (!ADS_ERR_OK(status)) {
644                 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
645                 goto done;
646         }
647         
648         if (ads_count_replies(ads, res)) {
649                 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
650                 ads_msgfree(ads, res);
651                 goto done;
652         }
653
654         if (opt_container == NULL) {
655                 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
656         }
657
658         status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
659
660         if (ADS_ERR_OK(status)) {
661                 d_printf("Group %s added\n", argv[0]);
662                 rc = 0;
663         } else {
664                 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
665                          ads_errstr(status));
666         }
667
668  done:
669         if (res)
670                 ads_msgfree(ads, res);
671         ads_destroy(&ads);
672         return rc;
673 }
674
675 static int ads_group_delete(int argc, const char **argv)
676 {
677         ADS_STRUCT *ads;
678         ADS_STATUS rc;
679         void *res;
680         char *groupdn;
681
682         if (argc < 1) {
683                 return net_ads_group_usage(argc, argv);
684         }
685         
686         if (!(ads = ads_startup(False))) {
687                 return -1;
688         }
689
690         rc = ads_find_user_acct(ads, &res, argv[0]);
691         if (!ADS_ERR_OK(rc)) {
692                 DEBUG(0, ("Group %s does not exist\n", argv[0]));
693                 ads_destroy(&ads);
694                 return -1;
695         }
696         groupdn = ads_get_dn(ads, res);
697         ads_msgfree(ads, res);
698         rc = ads_del_dn(ads, groupdn);
699         ads_memfree(ads, groupdn);
700         if (!ADS_ERR_OK(rc)) {
701                 d_printf("Group %s deleted\n", argv[0]);
702                 ads_destroy(&ads);
703                 return 0;
704         }
705         d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0], 
706                  ads_errstr(rc));
707         ads_destroy(&ads);
708         return -1;
709 }
710
711 int net_ads_group(int argc, const char **argv)
712 {
713         struct functable func[] = {
714                 {"ADD", ads_group_add},
715                 {"DELETE", ads_group_delete},
716                 {NULL, NULL}
717         };
718         ADS_STRUCT *ads;
719         ADS_STATUS rc;
720         const char *shortattrs[] = {"sAMAccountName", NULL};
721         const char *longattrs[] = {"sAMAccountName", "description", NULL};
722         char *disp_fields[2] = {NULL, NULL};
723
724         if (argc == 0) {
725                 if (!(ads = ads_startup(False))) {
726                         return -1;
727                 }
728
729                 if (opt_long_list_entries)
730                         d_printf("\nGroup name            Comment"\
731                                  "\n-----------------------------\n");
732                 rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
733                                           LDAP_SCOPE_SUBTREE, 
734                                           "(objectCategory=group)", 
735                                           opt_long_list_entries ? longattrs : 
736                                           shortattrs, usergrp_display, 
737                                           disp_fields);
738
739                 ads_destroy(&ads);
740                 return ADS_ERR_OK(rc) ? 0 : -1;
741         }
742         return net_run_function(argc, argv, func, net_ads_group_usage);
743 }
744
745 static int net_ads_status(int argc, const char **argv)
746 {
747         ADS_STRUCT *ads;
748         ADS_STATUS rc;
749         void *res;
750
751         if (!(ads = ads_startup(True))) {
752                 return -1;
753         }
754
755         rc = ads_find_machine_acct(ads, &res, global_myname());
756         if (!ADS_ERR_OK(rc)) {
757                 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
758                 ads_destroy(&ads);
759                 return -1;
760         }
761
762         if (ads_count_replies(ads, res) == 0) {
763                 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
764                 ads_destroy(&ads);
765                 return -1;
766         }
767
768         ads_dump(ads, res);
769         ads_destroy(&ads);
770         return 0;
771 }
772
773 /*******************************************************************
774  Leave an AD domain.  Windows XP disables the machine account.
775  We'll try the same.  The old code would do an LDAP delete.
776  That only worked using the machine creds because added the machine
777  with full control to the computer object's ACL.
778 *******************************************************************/
779 static int net_ads_leave(int argc, const char **argv)
780 {
781         ADS_STRUCT *ads = NULL;
782         int ret = -1;
783         struct cli_state *cli = NULL;
784         TALLOC_CTX *ctx;
785         DOM_SID *dom_sid = NULL;
786
787         if (!secrets_init()) {
788                 DEBUG(1,("Failed to initialise secrets database\n"));
789                 return -1;
790         }
791
792         if (!(ctx = talloc_init("net_ads_leave"))) {
793                 DEBUG(0, ("Could not initialise talloc context\n"));
794                 return -1;
795         }
796
797         /* The finds a DC and takes care of getting the 
798            user creds if necessary */
799
800         if (!(ads = ads_startup(True))) {
801                 return -1;
802         }
803
804         /* make RPC calls here */
805
806         if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip, 
807                 ads->config.ldap_server_name)) )
808         {
809                 goto done;
810         }
811         
812         saf_store( cli->server_domain, cli->desthost );
813
814         if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
815                 goto done;
816         }
817
818         if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
819                 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
820                         global_myname(), ads->config.realm);
821                 goto done;
822         }
823         
824         d_printf("Disabled account for '%s' in realm '%s'\n", 
825                 global_myname(), ads->config.realm);
826                 
827         ret = 0;
828
829 done:
830         if ( cli ) 
831                 cli_shutdown(cli);
832
833         ads_destroy(&ads);
834         TALLOC_FREE( ctx );
835
836         return ret;
837 }
838
839 static int net_ads_join_ok(void)
840 {
841         ADS_STRUCT *ads = NULL;
842
843         if (!secrets_init()) {
844                 DEBUG(1,("Failed to initialise secrets database\n"));
845                 return -1;
846         }
847
848         net_use_machine_password();
849
850         if (!(ads = ads_startup(True))) {
851                 return -1;
852         }
853
854         ads_destroy(&ads);
855         return 0;
856 }
857
858 /*
859   check that an existing join is OK
860  */
861 int net_ads_testjoin(int argc, const char **argv)
862 {
863         use_in_memory_ccache();
864
865         /* Display success or failure */
866         if (net_ads_join_ok() != 0) {
867                 fprintf(stderr,"Join to domain is not valid\n");
868                 return -1;
869         }
870
871         printf("Join is OK\n");
872         return 0;
873 }
874
875 /*******************************************************************
876   Simple configu checks before beginning the join
877  ********************************************************************/
878
879 static int check_ads_config( void )
880 {
881         if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
882                 d_printf("Host is not configured as a member server.\n");
883                 return -1;
884         }
885
886         if (strlen(global_myname()) > 15) {
887                 d_printf("Our netbios name can be at most 15 chars long, "
888                          "\"%s\" is %u chars long\n",
889                          global_myname(), (unsigned int)strlen(global_myname()));
890                 return -1;
891         }
892
893         if ( lp_security() == SEC_ADS && !*lp_realm()) {
894                 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
895                         "join to succeed.\n");
896                 return -1;
897         }
898
899         if (!secrets_init()) {
900                 DEBUG(1,("Failed to initialise secrets database\n"));
901                 return -1;
902         }
903         
904         return 0;
905 }
906
907 /*******************************************************************
908  Do the domain join
909  ********************************************************************/
910
911 static int net_join_domain( TALLOC_CTX *ctx, const char *servername, 
912                             struct in_addr *ip, DOM_SID **dom_sid, const char *password )
913 {
914         int ret = -1;
915         struct cli_state *cli = NULL;
916
917         if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
918                 goto done;
919         
920         saf_store( cli->server_domain, cli->desthost );
921
922         if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
923                 goto done;
924
925         if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid, 
926                 password, ND_TYPE_AD )) )
927         {
928                 goto done;
929         }
930         
931         ret = 0;
932
933 done:
934         if ( cli ) 
935                 cli_shutdown(cli);
936
937         return ret;
938 }
939
940 /*******************************************************************
941  Set a machines dNSHostName and servicePrincipalName attributes
942  ********************************************************************/
943
944 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
945 {
946         ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
947         char *host_upn, *new_dn;
948         ADS_MODLIST mods;
949         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
950         char *psp;
951         fstring my_fqdn;
952         LDAPMessage *res = NULL;
953         char *dn_string = NULL;
954         const char *machine_name = global_myname();
955         int count;
956         
957         if ( !machine_name ) {
958                 return ADS_ERROR(LDAP_NO_MEMORY);
959         }
960         
961         /* Find our DN */
962         
963         status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
964         if (!ADS_ERR_OK(status)) 
965                 return status;
966                 
967         if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
968                 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
969                 return ADS_ERROR(LDAP_NO_MEMORY);       
970         }
971         
972         if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
973                 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
974                 goto done;
975         }
976         
977         new_dn = talloc_strdup(ctx, dn_string);
978         ads_memfree(ads_s, dn_string);
979         if (!new_dn) {
980                 return ADS_ERROR(LDAP_NO_MEMORY);
981         }
982
983         /* Windows only creates HOST/shortname & HOST/fqdn.  We create 
984            the UPN as well so that 'kinit -k' will work.  You can only 
985            request a TGT for entries with a UPN in AD. */
986            
987         if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) ) 
988                 goto done;
989         strupper_m(psp);
990         servicePrincipalName[0] = psp;
991
992         name_to_fqdn(my_fqdn, machine_name);
993         strlower_m(my_fqdn);
994         if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) ) 
995                 goto done;
996         servicePrincipalName[1] = psp;
997         
998         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
999                 goto done;
1000
1001         /* now do the mods */
1002         
1003         if (!(mods = ads_init_mods(ctx))) {
1004                 goto done;
1005         }
1006         
1007         /* fields of primary importance */
1008         
1009         ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1010         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1011
1012         status = ads_gen_mod(ads_s, new_dn, mods);
1013
1014 done:
1015         ads_msgfree(ads_s, res);
1016         
1017         return status;
1018 }
1019
1020
1021 /*******************************************************************
1022   join a domain using ADS (LDAP mods)
1023  ********************************************************************/
1024
1025 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1026 {
1027         ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1028         char *dn, *ou_str;
1029         LDAPMessage *res = NULL;
1030
1031         ou_str = ads_ou_string(ads, ou);
1032         asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1033         free(ou_str);
1034
1035         rc = ads_search_dn(ads, &res, dn, NULL);
1036         ads_msgfree(ads, res);
1037
1038         if (ADS_ERR_OK(rc)) {
1039                 /* Attempt to create the machine account and bail if this fails.
1040                    Assume that the admin wants exactly what they requested */
1041
1042                 rc = ads_create_machine_acct( ads, global_myname(), dn );
1043                 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1044                         rc = ADS_SUCCESS;
1045                 }
1046         }
1047
1048         SAFE_FREE( dn );
1049
1050         return rc;
1051 }
1052
1053 /************************************************************************
1054  ************************************************************************/
1055
1056 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1057 {
1058         uint32 domain_func;
1059         ADS_STATUS status;
1060         fstring salt;
1061         char *std_salt;
1062         LDAPMessage *res = NULL;
1063         const char *machine_name = global_myname();
1064
1065         status = ads_domain_func_level( ads, &domain_func );
1066         if ( !ADS_ERR_OK(status) ) {
1067                 DEBUG(2,("Failed to determine domain functional level!\n"));
1068                 return False;
1069         }
1070
1071         /* go ahead and setup the default salt */
1072
1073         if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1074                 DEBUG(0,("net_derive_salting_principal: failed to obtain stanard DES salt\n"));
1075                 return False;
1076         }
1077
1078         fstrcpy( salt, std_salt );
1079         SAFE_FREE( std_salt );
1080         
1081         /* if it's a Windows functional domain, we have to look for the UPN */
1082            
1083         if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) { 
1084                 char *upn;
1085                 int count;
1086                 
1087                 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1088                 if (!ADS_ERR_OK(status)) {
1089                         return False;
1090                 }
1091                 
1092                 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1093                         DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1094                         return False;
1095                 }
1096                 
1097                 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1098                 if ( upn ) {
1099                         fstrcpy( salt, upn );
1100                 }
1101                 
1102                 ads_msgfree(ads, res);
1103         }
1104
1105         return kerberos_secrets_store_des_salt( salt );
1106 }
1107
1108 /*******************************************************************
1109   join a domain using ADS (LDAP mods)
1110  ********************************************************************/
1111  
1112 int net_ads_join(int argc, const char **argv)
1113 {
1114         ADS_STRUCT *ads;
1115         ADS_STATUS status;
1116         char *machine_account = NULL;
1117         const char *short_domain_name = NULL;
1118         char *tmp_password, *password;
1119         struct cldap_netlogon_reply cldap_reply;
1120         TALLOC_CTX *ctx;
1121         DOM_SID *domain_sid = NULL;
1122         
1123         if ( check_ads_config() != 0 ) {
1124                 d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
1125                 return -1;
1126         }
1127
1128         if ( (ads = ads_startup(True)) == NULL ) {
1129                 return -1;
1130         }
1131
1132         if (strcmp(ads->config.realm, lp_realm()) != 0) {
1133                 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1134                         "(%s) DO NOT match.  Aborting join\n", ads->config.realm, 
1135                         lp_realm());
1136                 ads_destroy(&ads);
1137                 return -1;
1138         }
1139
1140         if (!(ctx = talloc_init("net_ads_join"))) {
1141                 DEBUG(0, ("Could not initialise talloc context\n"));
1142                 return -1;
1143         }
1144
1145         /* If we were given an OU, try to create the machine in the OU account 
1146            first and then do the normal RPC join */
1147
1148         if ( argc > 0 ) {
1149                 status = net_precreate_machine_acct( ads, argv[0] );
1150                 if ( !ADS_ERR_OK(status) ) {
1151                         d_fprintf( stderr, "Failed to pre-create the machine object "
1152                                 "in OU %s.\n", argv[0]);
1153                         ads_destroy( &ads );
1154                         return -1;
1155                 }
1156         }
1157
1158         /* Do the domain join here */
1159
1160         tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1161         password = talloc_strdup(ctx, tmp_password);
1162         
1163         if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1164                 d_fprintf(stderr, "Failed to join domain!\n");
1165                 return -1;
1166         }
1167         
1168         /* Check the short name of the domain */
1169         
1170         ZERO_STRUCT( cldap_reply );
1171         
1172         if ( ads_cldap_netlogon( ads->config.ldap_server_name, 
1173                 ads->server.realm, &cldap_reply ) ) 
1174         {
1175                 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1176                 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1177                         d_printf("The workgroup in smb.conf does not match the short\n");
1178                         d_printf("domain name obtained from the server.\n");
1179                         d_printf("Using the name [%s] from the server.\n", short_domain_name);
1180                         d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1181                 }
1182         } else {
1183                 short_domain_name = lp_workgroup();
1184         }
1185         
1186         d_printf("Using short domain name -- %s\n", short_domain_name);
1187
1188         /*  HACK ALERT!  Store the sid and password under both the lp_workgroup() 
1189             value from smb.conf and the string returned from the server.  The former is
1190             neede to bootstrap winbindd's first connection to the DC to get the real 
1191             short domain name   --jerry */
1192            
1193         if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1194                 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1195         {
1196                 ads_destroy(&ads);
1197                 return -1;
1198         }
1199
1200         /* Verify that everything is ok */
1201
1202         if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1203                 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1204                 return -1;
1205         }       
1206
1207         /* create the dNSHostName & servicePrincipalName values */
1208         
1209         status = net_set_machine_spn( ctx, ads );
1210         if ( !ADS_ERR_OK(status) )  {
1211                 d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
1212                 d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
1213                 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1214
1215                 /* don't fail */
1216         }
1217
1218         if ( !net_derive_salting_principal( ctx, ads ) ) {
1219                 DEBUG(1,("Failed to determine salting principal\n"));
1220                 ads_destroy(&ads);
1221                 return -1;
1222         }
1223
1224         /* Now build the keytab, using the same ADS connection */
1225         if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1226                 DEBUG(1,("Error creating host keytab!\n"));
1227         }
1228
1229         d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1230
1231         SAFE_FREE(machine_account);
1232         TALLOC_FREE( ctx );
1233         ads_destroy(&ads);
1234         
1235         return 0;
1236 }
1237
1238 /*******************************************************************
1239  ********************************************************************/
1240
1241 int net_ads_printer_usage(int argc, const char **argv)
1242 {
1243         d_printf(
1244 "\nnet ads printer search <printer>"
1245 "\n\tsearch for a printer in the directory\n"
1246 "\nnet ads printer info <printer> <server>"
1247 "\n\tlookup info in directory for printer on server"
1248 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1249 "\nnet ads printer publish <printername>"
1250 "\n\tpublish printer in directory"
1251 "\n\t(note: printer name is required)\n"
1252 "\nnet ads printer remove <printername>"
1253 "\n\tremove printer from directory"
1254 "\n\t(note: printer name is required)\n");
1255         return -1;
1256 }
1257
1258 /*******************************************************************
1259  ********************************************************************/
1260
1261 static int net_ads_printer_search(int argc, const char **argv)
1262 {
1263         ADS_STRUCT *ads;
1264         ADS_STATUS rc;
1265         void *res = NULL;
1266
1267         if (!(ads = ads_startup(False))) {
1268                 return -1;
1269         }
1270
1271         rc = ads_find_printers(ads, &res);
1272
1273         if (!ADS_ERR_OK(rc)) {
1274                 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1275                 ads_msgfree(ads, res);
1276                 ads_destroy(&ads);
1277                 return -1;
1278         }
1279
1280         if (ads_count_replies(ads, res) == 0) {
1281                 d_fprintf(stderr, "No results found\n");
1282                 ads_msgfree(ads, res);
1283                 ads_destroy(&ads);
1284                 return -1;
1285         }
1286
1287         ads_dump(ads, res);
1288         ads_msgfree(ads, res);
1289         ads_destroy(&ads);
1290         return 0;
1291 }
1292
1293 static int net_ads_printer_info(int argc, const char **argv)
1294 {
1295         ADS_STRUCT *ads;
1296         ADS_STATUS rc;
1297         const char *servername, *printername;
1298         void *res = NULL;
1299
1300         if (!(ads = ads_startup(False))) {
1301                 return -1;
1302         }
1303
1304         if (argc > 0) {
1305                 printername = argv[0];
1306         } else {
1307                 printername = "*";
1308         }
1309
1310         if (argc > 1) {
1311                 servername =  argv[1];
1312         } else {
1313                 servername = global_myname();
1314         }
1315
1316         rc = ads_find_printer_on_server(ads, &res, printername, servername);
1317
1318         if (!ADS_ERR_OK(rc)) {
1319                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1320                 ads_msgfree(ads, res);
1321                 ads_destroy(&ads);
1322                 return -1;
1323         }
1324
1325         if (ads_count_replies(ads, res) == 0) {
1326                 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1327                 ads_msgfree(ads, res);
1328                 ads_destroy(&ads);
1329                 return -1;
1330         }
1331
1332         ads_dump(ads, res);
1333         ads_msgfree(ads, res);
1334         ads_destroy(&ads);
1335
1336         return 0;
1337 }
1338
1339 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1340                             void *buf, size_t len)
1341 {
1342         return;
1343 }
1344
1345 static int net_ads_printer_publish(int argc, const char **argv)
1346 {
1347         ADS_STRUCT *ads;
1348         ADS_STATUS rc;
1349         const char *servername, *printername;
1350         struct cli_state *cli;
1351         struct rpc_pipe_client *pipe_hnd;
1352         struct in_addr          server_ip;
1353         NTSTATUS nt_status;
1354         TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1355         ADS_MODLIST mods = ads_init_mods(mem_ctx);
1356         char *prt_dn, *srv_dn, **srv_cn;
1357         void *res = NULL;
1358
1359         if (!(ads = ads_startup(True))) {
1360                 return -1;
1361         }
1362
1363         if (argc < 1) {
1364                 return net_ads_printer_usage(argc, argv);
1365         }
1366         
1367         printername = argv[0];
1368
1369         if (argc == 2) {
1370                 servername = argv[1];
1371         } else {
1372                 servername = global_myname();
1373         }
1374                 
1375         /* Get printer data from SPOOLSS */
1376
1377         resolve_name(servername, &server_ip, 0x20);
1378
1379         nt_status = cli_full_connection(&cli, global_myname(), servername, 
1380                                         &server_ip, 0,
1381                                         "IPC$", "IPC",  
1382                                         opt_user_name, opt_workgroup,
1383                                         opt_password ? opt_password : "", 
1384                                         CLI_FULL_CONNECTION_USE_KERBEROS, 
1385                                         Undefined, NULL);
1386
1387         if (NT_STATUS_IS_ERR(nt_status)) {
1388                 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1389                          "for %s\n", servername, printername);
1390                 ads_destroy(&ads);
1391                 return -1;
1392         }
1393
1394         /* Publish on AD server */
1395
1396         ads_find_machine_acct(ads, &res, servername);
1397
1398         if (ads_count_replies(ads, res) == 0) {
1399                 d_fprintf(stderr, "Could not find machine account for server %s\n", 
1400                          servername);
1401                 ads_destroy(&ads);
1402                 return -1;
1403         }
1404
1405         srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1406         srv_cn = ldap_explode_dn(srv_dn, 1);
1407
1408         asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1409
1410         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1411         if (!pipe_hnd) {
1412                 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1413                          servername);
1414                 ads_destroy(&ads);
1415                 return -1;
1416         }
1417
1418         get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1419                                            printername);
1420
1421         rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1422         if (!ADS_ERR_OK(rc)) {
1423                 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1424                 ads_destroy(&ads);
1425                 return -1;
1426         }
1427  
1428         d_printf("published printer\n");
1429         ads_destroy(&ads);
1430  
1431         return 0;
1432 }
1433
1434 static int net_ads_printer_remove(int argc, const char **argv)
1435 {
1436         ADS_STRUCT *ads;
1437         ADS_STATUS rc;
1438         const char *servername;
1439         char *prt_dn;
1440         void *res = NULL;
1441
1442         if (!(ads = ads_startup(True))) {
1443                 return -1;
1444         }
1445
1446         if (argc < 1) {
1447                 return net_ads_printer_usage(argc, argv);
1448         }
1449
1450         if (argc > 1) {
1451                 servername = argv[1];
1452         } else {
1453                 servername = global_myname();
1454         }
1455
1456         rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1457
1458         if (!ADS_ERR_OK(rc)) {
1459                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1460                 ads_msgfree(ads, res);
1461                 ads_destroy(&ads);
1462                 return -1;
1463         }
1464
1465         if (ads_count_replies(ads, res) == 0) {
1466                 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1467                 ads_msgfree(ads, res);
1468                 ads_destroy(&ads);
1469                 return -1;
1470         }
1471
1472         prt_dn = ads_get_dn(ads, res);
1473         ads_msgfree(ads, res);
1474         rc = ads_del_dn(ads, prt_dn);
1475         ads_memfree(ads, prt_dn);
1476
1477         if (!ADS_ERR_OK(rc)) {
1478                 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1479                 ads_destroy(&ads);
1480                 return -1;
1481         }
1482
1483         ads_destroy(&ads);
1484         return 0;
1485 }
1486
1487 static int net_ads_printer(int argc, const char **argv)
1488 {
1489         struct functable func[] = {
1490                 {"SEARCH", net_ads_printer_search},
1491                 {"INFO", net_ads_printer_info},
1492                 {"PUBLISH", net_ads_printer_publish},
1493                 {"REMOVE", net_ads_printer_remove},
1494                 {NULL, NULL}
1495         };
1496         
1497         return net_run_function(argc, argv, func, net_ads_printer_usage);
1498 }
1499
1500
1501 static int net_ads_password(int argc, const char **argv)
1502 {
1503         ADS_STRUCT *ads;
1504         const char *auth_principal = opt_user_name;
1505         const char *auth_password = opt_password;
1506         char *realm = NULL;
1507         char *new_password = NULL;
1508         char *c, *prompt;
1509         const char *user;
1510         ADS_STATUS ret;
1511
1512         if (opt_user_name == NULL || opt_password == NULL) {
1513                 d_fprintf(stderr, "You must supply an administrator username/password\n");
1514                 return -1;
1515         }
1516
1517         if (argc < 1) {
1518                 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1519                 return -1;
1520         }
1521
1522         user = argv[0];
1523         if (!strchr_m(user, '@')) {
1524                 asprintf(&c, "%s@%s", argv[0], lp_realm());
1525                 user = c;
1526         }
1527
1528         use_in_memory_ccache();    
1529         c = strchr_m(auth_principal, '@');
1530         if (c) {
1531                 realm = ++c;
1532         } else {
1533                 realm = lp_realm();
1534         }
1535
1536         /* use the realm so we can eventually change passwords for users 
1537         in realms other than default */
1538         if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
1539                 return -1;
1540         }
1541
1542         /* we don't actually need a full connect, but it's the easy way to
1543                 fill in the KDC's addresss */
1544         ads_connect(ads);
1545     
1546         if (!ads || !ads->config.realm) {
1547                 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1548                 return -1;
1549         }
1550
1551         if (argv[1]) {
1552                 new_password = (char *)argv[1];
1553         } else {
1554                 asprintf(&prompt, "Enter new password for %s:", user);
1555                 new_password = getpass(prompt);
1556                 free(prompt);
1557         }
1558
1559         ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
1560                                 auth_password, user, new_password, ads->auth.time_offset);
1561         if (!ADS_ERR_OK(ret)) {
1562                 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1563                 ads_destroy(&ads);
1564                 return -1;
1565         }
1566
1567         d_printf("Password change for %s completed.\n", user);
1568         ads_destroy(&ads);
1569
1570         return 0;
1571 }
1572
1573 int net_ads_changetrustpw(int argc, const char **argv)
1574 {    
1575         ADS_STRUCT *ads;
1576         char *host_principal;
1577         fstring my_name;
1578         ADS_STATUS ret;
1579
1580         if (!secrets_init()) {
1581                 DEBUG(1,("Failed to initialise secrets database\n"));
1582                 return -1;
1583         }
1584
1585         net_use_machine_password();
1586
1587         use_in_memory_ccache();
1588
1589         if (!(ads = ads_startup(True))) {
1590                 return -1;
1591         }
1592
1593         fstrcpy(my_name, global_myname());
1594         strlower_m(my_name);
1595         asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1596         d_printf("Changing password for principal: %s\n", host_principal);
1597
1598         ret = ads_change_trust_account_password(ads, host_principal);
1599
1600         if (!ADS_ERR_OK(ret)) {
1601                 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1602                 ads_destroy(&ads);
1603                 SAFE_FREE(host_principal);
1604                 return -1;
1605         }
1606     
1607         d_printf("Password change for principal %s succeeded.\n", host_principal);
1608
1609         if (lp_use_kerberos_keytab()) {
1610                 d_printf("Attempting to update system keytab with new password.\n");
1611                 if (ads_keytab_create_default(ads)) {
1612                         d_printf("Failed to update system keytab.\n");
1613                 }
1614         }
1615
1616         ads_destroy(&ads);
1617         SAFE_FREE(host_principal);
1618
1619         return 0;
1620 }
1621
1622 /*
1623   help for net ads search
1624 */
1625 static int net_ads_search_usage(int argc, const char **argv)
1626 {
1627         d_printf(
1628                 "\nnet ads search <expression> <attributes...>\n"\
1629                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1630                 "The expression is a standard LDAP search expression, and the\n"\
1631                 "attributes are a list of LDAP fields to show in the results\n\n"\
1632                 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1633                 );
1634         net_common_flags_usage(argc, argv);
1635         return -1;
1636 }
1637
1638
1639 /*
1640   general ADS search function. Useful in diagnosing problems in ADS
1641 */
1642 static int net_ads_search(int argc, const char **argv)
1643 {
1644         ADS_STRUCT *ads;
1645         ADS_STATUS rc;
1646         const char *ldap_exp;
1647         const char **attrs;
1648         void *res = NULL;
1649
1650         if (argc < 1) {
1651                 return net_ads_search_usage(argc, argv);
1652         }
1653
1654         if (!(ads = ads_startup(False))) {
1655                 return -1;
1656         }
1657
1658         ldap_exp = argv[0];
1659         attrs = (argv + 1);
1660
1661         rc = ads_do_search_all(ads, ads->config.bind_path,
1662                                LDAP_SCOPE_SUBTREE,
1663                                ldap_exp, attrs, &res);
1664         if (!ADS_ERR_OK(rc)) {
1665                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1666                 ads_destroy(&ads);
1667                 return -1;
1668         }       
1669
1670         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1671
1672         /* dump the results */
1673         ads_dump(ads, res);
1674
1675         ads_msgfree(ads, res);
1676         ads_destroy(&ads);
1677
1678         return 0;
1679 }
1680
1681
1682 /*
1683   help for net ads search
1684 */
1685 static int net_ads_dn_usage(int argc, const char **argv)
1686 {
1687         d_printf(
1688                 "\nnet ads dn <dn> <attributes...>\n"\
1689                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1690                 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1691                 "to show in the results\n\n"\
1692                 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1693                 );
1694         net_common_flags_usage(argc, argv);
1695         return -1;
1696 }
1697
1698
1699 /*
1700   general ADS search function. Useful in diagnosing problems in ADS
1701 */
1702 static int net_ads_dn(int argc, const char **argv)
1703 {
1704         ADS_STRUCT *ads;
1705         ADS_STATUS rc;
1706         const char *dn;
1707         const char **attrs;
1708         void *res = NULL;
1709
1710         if (argc < 1) {
1711                 return net_ads_dn_usage(argc, argv);
1712         }
1713
1714         if (!(ads = ads_startup(False))) {
1715                 return -1;
1716         }
1717
1718         dn = argv[0];
1719         attrs = (argv + 1);
1720
1721         rc = ads_do_search_all(ads, dn, 
1722                                LDAP_SCOPE_BASE,
1723                                "(objectclass=*)", attrs, &res);
1724         if (!ADS_ERR_OK(rc)) {
1725                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1726                 ads_destroy(&ads);
1727                 return -1;
1728         }       
1729
1730         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1731
1732         /* dump the results */
1733         ads_dump(ads, res);
1734
1735         ads_msgfree(ads, res);
1736         ads_destroy(&ads);
1737
1738         return 0;
1739 }
1740
1741 /*
1742   help for net ads sid search
1743 */
1744 static int net_ads_sid_usage(int argc, const char **argv)
1745 {
1746         d_printf(
1747                 "\nnet ads sid <sid> <attributes...>\n"\
1748                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1749                 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1750                 "to show in the results\n\n"\
1751                 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1752                 );
1753         net_common_flags_usage(argc, argv);
1754         return -1;
1755 }
1756
1757
1758 /*
1759   general ADS search function. Useful in diagnosing problems in ADS
1760 */
1761 static int net_ads_sid(int argc, const char **argv)
1762 {
1763         ADS_STRUCT *ads;
1764         ADS_STATUS rc;
1765         const char *sid_string;
1766         const char **attrs;
1767         void *res = NULL;
1768         DOM_SID sid;
1769
1770         if (argc < 1) {
1771                 return net_ads_sid_usage(argc, argv);
1772         }
1773
1774         if (!(ads = ads_startup(False))) {
1775                 return -1;
1776         }
1777
1778         sid_string = argv[0];
1779         attrs = (argv + 1);
1780
1781         if (!string_to_sid(&sid, sid_string)) {
1782                 d_fprintf(stderr, "could not convert sid\n");
1783                 ads_destroy(&ads);
1784                 return -1;
1785         }
1786
1787         rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1788         if (!ADS_ERR_OK(rc)) {
1789                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1790                 ads_destroy(&ads);
1791                 return -1;
1792         }       
1793
1794         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1795
1796         /* dump the results */
1797         ads_dump(ads, res);
1798
1799         ads_msgfree(ads, res);
1800         ads_destroy(&ads);
1801
1802         return 0;
1803 }
1804
1805
1806 static int net_ads_keytab_usage(int argc, const char **argv)
1807 {
1808         d_printf(
1809                 "net ads keytab <COMMAND>\n"\
1810 "<COMMAND> can be either:\n"\
1811 "  CREATE    Creates a fresh keytab\n"\
1812 "  ADD       Adds new service principal\n"\
1813 "  FLUSH     Flushes out all keytab entries\n"\
1814 "  HELP      Prints this help message\n"\
1815 "The ADD command will take arguments, the other commands\n"\
1816 "will not take any arguments.   The arguments given to ADD\n"\
1817 "should be a list of principals to add.  For example, \n"\
1818 "   net ads keytab add srv1 srv2\n"\
1819 "will add principals for the services srv1 and srv2 to the\n"\
1820 "system's keytab.\n"\
1821 "\n"
1822                 );
1823         return -1;
1824 }
1825
1826 static int net_ads_keytab_flush(int argc, const char **argv)
1827 {
1828         int ret;
1829         ADS_STRUCT *ads;
1830
1831         if (!(ads = ads_startup(True))) {
1832                 return -1;
1833         }
1834         ret = ads_keytab_flush(ads);
1835         ads_destroy(&ads);
1836         return ret;
1837 }
1838
1839 static int net_ads_keytab_add(int argc, const char **argv)
1840 {
1841         int i;
1842         int ret = 0;
1843         ADS_STRUCT *ads;
1844
1845         d_printf("Processing principals to add...\n");
1846         if (!(ads = ads_startup(True))) {
1847                 return -1;
1848         }
1849         for (i = 0; i < argc; i++) {
1850                 ret |= ads_keytab_add_entry(ads, argv[i]);
1851         }
1852         ads_destroy(&ads);
1853         return ret;
1854 }
1855
1856 static int net_ads_keytab_create(int argc, const char **argv)
1857 {
1858         ADS_STRUCT *ads;
1859         int ret;
1860
1861         if (!(ads = ads_startup(True))) {
1862                 return -1;
1863         }
1864         ret = ads_keytab_create_default(ads);
1865         ads_destroy(&ads);
1866         return ret;
1867 }
1868
1869 int net_ads_keytab(int argc, const char **argv)
1870 {
1871         struct functable func[] = {
1872                 {"CREATE", net_ads_keytab_create},
1873                 {"ADD", net_ads_keytab_add},
1874                 {"FLUSH", net_ads_keytab_flush},
1875                 {"HELP", net_ads_keytab_usage},
1876                 {NULL, NULL}
1877         };
1878
1879         if (!lp_use_kerberos_keytab()) {
1880                 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1881 use keytab functions.\n");
1882         }
1883
1884         return net_run_function(argc, argv, func, net_ads_keytab_usage);
1885 }
1886
1887 int net_ads_help(int argc, const char **argv)
1888 {
1889         struct functable func[] = {
1890                 {"USER", net_ads_user_usage},
1891                 {"GROUP", net_ads_group_usage},
1892                 {"PRINTER", net_ads_printer_usage},
1893                 {"SEARCH", net_ads_search_usage},
1894 #if 0
1895                 {"INFO", net_ads_info},
1896                 {"JOIN", net_ads_join},
1897                 {"JOIN2", net_ads_join2},
1898                 {"LEAVE", net_ads_leave},
1899                 {"STATUS", net_ads_status},
1900                 {"PASSWORD", net_ads_password},
1901                 {"CHANGETRUSTPW", net_ads_changetrustpw},
1902 #endif
1903                 {NULL, NULL}
1904         };
1905
1906         return net_run_function(argc, argv, func, net_ads_usage);
1907 }
1908
1909 int net_ads(int argc, const char **argv)
1910 {
1911         struct functable func[] = {
1912                 {"INFO", net_ads_info},
1913                 {"JOIN", net_ads_join},
1914                 {"TESTJOIN", net_ads_testjoin},
1915                 {"LEAVE", net_ads_leave},
1916                 {"STATUS", net_ads_status},
1917                 {"USER", net_ads_user},
1918                 {"GROUP", net_ads_group},
1919                 {"PASSWORD", net_ads_password},
1920                 {"CHANGETRUSTPW", net_ads_changetrustpw},
1921                 {"PRINTER", net_ads_printer},
1922                 {"SEARCH", net_ads_search},
1923                 {"DN", net_ads_dn},
1924                 {"SID", net_ads_sid},
1925                 {"WORKGROUP", net_ads_workgroup},
1926                 {"LOOKUP", net_ads_lookup},
1927                 {"KEYTAB", net_ads_keytab},
1928                 {"HELP", net_ads_help},
1929                 {NULL, NULL}
1930         };
1931         
1932         return net_run_function(argc, argv, func, net_ads_usage);
1933 }
1934
1935 #else
1936
1937 static int net_ads_noads(void)
1938 {
1939         d_fprintf(stderr, "ADS support not compiled in\n");
1940         return -1;
1941 }
1942
1943 int net_ads_keytab(int argc, const char **argv)
1944 {
1945         return net_ads_noads();
1946 }
1947
1948 int net_ads_usage(int argc, const char **argv)
1949 {
1950         return net_ads_noads();
1951 }
1952
1953 int net_ads_help(int argc, const char **argv)
1954 {
1955         return net_ads_noads();
1956 }
1957
1958 int net_ads_changetrustpw(int argc, const char **argv)
1959 {
1960         return net_ads_noads();
1961 }
1962
1963 int net_ads_join(int argc, const char **argv)
1964 {
1965         return net_ads_noads();
1966 }
1967
1968 int net_ads_user(int argc, const char **argv)
1969 {
1970         return net_ads_noads();
1971 }
1972
1973 int net_ads_group(int argc, const char **argv)
1974 {
1975         return net_ads_noads();
1976 }
1977
1978 /* this one shouldn't display a message */
1979 int net_ads_check(void)
1980 {
1981         return -1;
1982 }
1983
1984 int net_ads_check_our_domain(void)
1985 {
1986         return -1;
1987 }
1988
1989 int net_ads(int argc, const char **argv)
1990 {
1991         return net_ads_usage(argc, argv);
1992 }
1993
1994 #endif