r15194: We need to be able to join as PDC as well. Thanks to Andrew Bartlett.
[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
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
21 */
22
23 #include "includes.h"
24 #include "utils/net.h"
25
26 #ifdef HAVE_ADS
27
28 int net_ads_usage(int argc, const char **argv)
29 {
30         d_printf(
31 "\nnet ads join <org_unit>"\
32 "\n\tjoins the local machine to a ADS realm\n"\
33 "\nnet ads leave"\
34 "\n\tremoves the local machine from a ADS realm\n"\
35 "\nnet ads testjoin"\
36 "\n\ttests that an exiting join is OK\n"\
37 "\nnet ads user"\
38 "\n\tlist, add, or delete users in the realm\n"\
39 "\nnet ads group"\
40 "\n\tlist, add, or delete groups in the realm\n"\
41 "\nnet ads info"\
42 "\n\tshows some info on the server\n"\
43 "\nnet ads status"\
44 "\n\tdump the machine account details to stdout\n"
45 "\nnet ads lookup"\
46 "\n\tperform a CLDAP search on the server\n"
47 "\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
48 "\n\tchange a user's password using an admin account"\
49 "\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
50 "\nnet ads changetrustpw"\
51 "\n\tchange the trust account password of this machine in the AD tree\n"\
52 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
53 "\n\t lookup, add, or remove directory entry for a printer\n"\
54 "\nnet ads search"\
55 "\n\tperform a raw LDAP search and dump the results\n"
56 "\nnet ads dn"\
57 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
58 "\nnet ads keytab"\
59 "\n\tcreates and updates the kerberos system keytab file\n"
60                 );
61         return -1;
62 }
63
64
65 /*
66   this implements the CLDAP based netlogon lookup requests
67   for finding the domain controller of a ADS domain
68 */
69 static int net_ads_lookup(int argc, const char **argv)
70 {
71         ADS_STRUCT *ads;
72         ADS_STATUS status;
73
74         ads = ads_init(NULL, opt_target_workgroup, opt_host);
75         if (ads) {
76                 ads->auth.flags |= ADS_AUTH_NO_BIND;
77         }
78
79         status = ads_connect(ads);
80         if (!ADS_ERR_OK(status) || !ads) {
81                 d_fprintf(stderr, "Didn't find the cldap server!\n");
82                 return -1;
83         }
84         
85         if (!ads->config.realm) {
86                 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
87                 ads->ldap_port = 389;
88         }
89
90         return ads_cldap_netlogon(ads);
91 }
92
93
94
95 static int net_ads_info(int argc, const char **argv)
96 {
97         ADS_STRUCT *ads;
98
99         /* if netbios is disabled we have to default to the realm from smb.conf */
100
101         if ( lp_disable_netbios() && *lp_realm() )
102                 ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
103         else
104                 ads = ads_init(NULL, opt_target_workgroup, opt_host);
105
106         if (ads) {
107                 ads->auth.flags |= ADS_AUTH_NO_BIND;
108         }
109
110         ads_connect(ads);
111
112         if (!ads || !ads->config.realm) {
113                 d_fprintf(stderr, "Didn't find the ldap server!\n");
114                 return -1;
115         }
116
117         d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
118         d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
119         d_printf("Realm: %s\n", ads->config.realm);
120         d_printf("Bind Path: %s\n", ads->config.bind_path);
121         d_printf("LDAP port: %d\n", ads->ldap_port);
122         d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
123
124         d_printf("KDC server: %s\n", ads->auth.kdc_server );
125         d_printf("Server time offset: %d\n", ads->auth.time_offset );
126
127         return 0;
128 }
129
130 static void use_in_memory_ccache(void) {
131         /* Use in-memory credentials cache so we do not interfere with
132          * existing credentials */
133         setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
134 }
135
136 static ADS_STRUCT *ads_startup(void)
137 {
138         ADS_STRUCT *ads;
139         ADS_STATUS status;
140         BOOL need_password = False;
141         BOOL second_time = False;
142         char *cp;
143         
144         /* lp_realm() should be handled by a command line param, 
145            However, the join requires that realm be set in smb.conf
146            and compares our realm with the remote server's so this is
147            ok until someone needs more flexibility */
148            
149         ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
150
151         if (!opt_user_name) {
152                 opt_user_name = "administrator";
153         }
154
155         if (opt_user_specified) {
156                 need_password = True;
157         }
158
159 retry:
160         if (!opt_password && need_password && !opt_machine_pass) {
161                 char *prompt;
162                 asprintf(&prompt,"%s's password: ", opt_user_name);
163                 opt_password = getpass(prompt);
164                 free(prompt);
165         }
166
167         if (opt_password) {
168                 use_in_memory_ccache();
169                 ads->auth.password = smb_xstrdup(opt_password);
170         }
171
172         ads->auth.user_name = smb_xstrdup(opt_user_name);
173
174        /*
175         * If the username is of the form "name@realm", 
176         * extract the realm and convert to upper case.
177         * This is only used to establish the connection.
178         */
179        if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
180                *cp++ = '\0';
181                ads->auth.realm = smb_xstrdup(cp);
182                strupper_m(ads->auth.realm);
183        }
184
185         status = ads_connect(ads);
186
187         if (!ADS_ERR_OK(status)) {
188                 if (!need_password && !second_time) {
189                         need_password = True;
190                         second_time = True;
191                         goto retry;
192                 } else {
193                         DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
194                         return NULL;
195                 }
196         }
197         return ads;
198 }
199
200
201 /*
202   Check to see if connection can be made via ads.
203   ads_startup() stores the password in opt_password if it needs to so
204   that rpc or rap can use it without re-prompting.
205 */
206 int net_ads_check(void)
207 {
208         ADS_STRUCT *ads;
209
210         ads = ads_startup();
211         if (!ads)
212                 return -1;
213         ads_destroy(&ads);
214         return 0;
215 }
216
217 /* 
218    determine the netbios workgroup name for a domain
219  */
220 static int net_ads_workgroup(int argc, const char **argv)
221 {
222         ADS_STRUCT *ads;
223         TALLOC_CTX *ctx;
224         const char *workgroup;
225
226         if (!(ads = ads_startup())) return -1;
227
228         if (!(ctx = talloc_init("net_ads_workgroup"))) {
229                 ads_destroy(&ads);
230                 return -1;
231         }
232
233         if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
234                 d_fprintf(stderr, "Failed to find workgroup for realm '%s'\n", 
235                          ads->config.realm);
236                 talloc_destroy(ctx);
237                 ads_destroy(&ads);
238                 return -1;
239         }
240
241         d_printf("Workgroup: %s\n", workgroup);
242
243         talloc_destroy(ctx);
244         ads_destroy(&ads);
245         return 0;
246 }
247
248
249
250 static BOOL usergrp_display(char *field, void **values, void *data_area)
251 {
252         char **disp_fields = (char **) data_area;
253
254         if (!field) { /* must be end of record */
255                 if (disp_fields[0]) {
256                         if (!strchr_m(disp_fields[0], '$')) {
257                                 if (disp_fields[1])
258                                         d_printf("%-21.21s %s\n", 
259                                                disp_fields[0], disp_fields[1]);
260                                 else
261                                         d_printf("%s\n", disp_fields[0]);
262                         }
263                 }
264                 SAFE_FREE(disp_fields[0]);
265                 SAFE_FREE(disp_fields[1]);
266                 return True;
267         }
268         if (!values) /* must be new field, indicate string field */
269                 return True;
270         if (StrCaseCmp(field, "sAMAccountName") == 0) {
271                 disp_fields[0] = SMB_STRDUP((char *) values[0]);
272         }
273         if (StrCaseCmp(field, "description") == 0)
274                 disp_fields[1] = SMB_STRDUP((char *) values[0]);
275         return True;
276 }
277
278 static int net_ads_user_usage(int argc, const char **argv)
279 {
280         return net_help_user(argc, argv);
281
282
283 static int ads_user_add(int argc, const char **argv)
284 {
285         ADS_STRUCT *ads;
286         ADS_STATUS status;
287         char *upn, *userdn;
288         void *res=NULL;
289         int rc = -1;
290
291         if (argc < 1) return net_ads_user_usage(argc, argv);
292         
293         if (!(ads = ads_startup())) {
294                 return -1;
295         }
296
297         status = ads_find_user_acct(ads, &res, argv[0]);
298
299         if (!ADS_ERR_OK(status)) {
300                 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
301                 goto done;
302         }
303         
304         if (ads_count_replies(ads, res)) {
305                 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
306                 goto done;
307         }
308
309         if (opt_container == NULL) {
310                 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
311         }
312
313         status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
314
315         if (!ADS_ERR_OK(status)) {
316                 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
317                          ads_errstr(status));
318                 goto done;
319         }
320
321         /* if no password is to be set, we're done */
322         if (argc == 1) { 
323                 d_printf("User %s added\n", argv[0]);
324                 rc = 0;
325                 goto done;
326         }
327
328         /* try setting the password */
329         asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
330         status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1], 
331                                        ads->auth.time_offset);
332         safe_free(upn);
333         if (ADS_ERR_OK(status)) {
334                 d_printf("User %s added\n", argv[0]);
335                 rc = 0;
336                 goto done;
337         }
338
339         /* password didn't set, delete account */
340         d_fprintf(stderr, "Could not add user %s.  Error setting password %s\n",
341                  argv[0], ads_errstr(status));
342         ads_msgfree(ads, res);
343         status=ads_find_user_acct(ads, &res, argv[0]);
344         if (ADS_ERR_OK(status)) {
345                 userdn = ads_get_dn(ads, res);
346                 ads_del_dn(ads, userdn);
347                 ads_memfree(ads, userdn);
348         }
349
350  done:
351         if (res)
352                 ads_msgfree(ads, res);
353         ads_destroy(&ads);
354         return rc;
355 }
356
357 static int ads_user_info(int argc, const char **argv)
358 {
359         ADS_STRUCT *ads;
360         ADS_STATUS rc;
361         void *res;
362         const char *attrs[] = {"memberOf", NULL};
363         char *searchstring=NULL;
364         char **grouplist;
365         char *escaped_user;
366
367         if (argc < 1) {
368                 return net_ads_user_usage(argc, argv);
369         }
370
371         escaped_user = escape_ldap_string_alloc(argv[0]);
372
373         if (!escaped_user) {
374                 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
375                 return -1;
376         }
377
378         if (!(ads = ads_startup())) {
379                 SAFE_FREE(escaped_user);
380                 return -1;
381         }
382
383         asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
384         rc = ads_search(ads, &res, searchstring, attrs);
385         safe_free(searchstring);
386
387         if (!ADS_ERR_OK(rc)) {
388                 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
389                 ads_destroy(&ads);
390                 SAFE_FREE(escaped_user);
391                 return -1;
392         }
393         
394         grouplist = ldap_get_values(ads->ld, res, "memberOf");
395
396         if (grouplist) {
397                 int i;
398                 char **groupname;
399                 for (i=0;grouplist[i];i++) {
400                         groupname = ldap_explode_dn(grouplist[i], 1);
401                         d_printf("%s\n", groupname[0]);
402                         ldap_value_free(groupname);
403                 }
404                 ldap_value_free(grouplist);
405         }
406         
407         ads_msgfree(ads, res);
408         ads_destroy(&ads);
409         SAFE_FREE(escaped_user);
410         return 0;
411 }
412
413 static int ads_user_delete(int argc, const char **argv)
414 {
415         ADS_STRUCT *ads;
416         ADS_STATUS rc;
417         void *res;
418         char *userdn;
419
420         if (argc < 1) {
421                 return net_ads_user_usage(argc, argv);
422         }
423         
424         if (!(ads = ads_startup())) {
425                 return -1;
426         }
427
428         rc = ads_find_user_acct(ads, &res, argv[0]);
429         if (!ADS_ERR_OK(rc)) {
430                 DEBUG(0, ("User %s does not exist\n", argv[0]));
431                 ads_destroy(&ads);
432                 return -1;
433         }
434         userdn = ads_get_dn(ads, res);
435         ads_msgfree(ads, res);
436         rc = ads_del_dn(ads, userdn);
437         ads_memfree(ads, userdn);
438         if (!ADS_ERR_OK(rc)) {
439                 d_printf("User %s deleted\n", argv[0]);
440                 ads_destroy(&ads);
441                 return 0;
442         }
443         d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0], 
444                  ads_errstr(rc));
445         ads_destroy(&ads);
446         return -1;
447 }
448
449 int net_ads_user(int argc, const char **argv)
450 {
451         struct functable func[] = {
452                 {"ADD", ads_user_add},
453                 {"INFO", ads_user_info},
454                 {"DELETE", ads_user_delete},
455                 {NULL, NULL}
456         };
457         ADS_STRUCT *ads;
458         ADS_STATUS rc;
459         const char *shortattrs[] = {"sAMAccountName", NULL};
460         const char *longattrs[] = {"sAMAccountName", "description", NULL};
461         char *disp_fields[2] = {NULL, NULL};
462         
463         if (argc == 0) {
464                 if (!(ads = ads_startup())) {
465                         return -1;
466                 }
467
468                 if (opt_long_list_entries)
469                         d_printf("\nUser name             Comment"\
470                                  "\n-----------------------------\n");
471
472                 rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
473                                           LDAP_SCOPE_SUBTREE,
474                                           "(objectclass=user)", 
475                                           opt_long_list_entries ? longattrs :
476                                           shortattrs, usergrp_display, 
477                                           disp_fields);
478                 ads_destroy(&ads);
479                 return 0;
480         }
481
482         return net_run_function(argc, argv, func, net_ads_user_usage);
483 }
484
485 static int net_ads_group_usage(int argc, const char **argv)
486 {
487         return net_help_group(argc, argv);
488
489
490 static int ads_group_add(int argc, const char **argv)
491 {
492         ADS_STRUCT *ads;
493         ADS_STATUS status;
494         void *res=NULL;
495         int rc = -1;
496
497         if (argc < 1) {
498                 return net_ads_group_usage(argc, argv);
499         }
500         
501         if (!(ads = ads_startup())) {
502                 return -1;
503         }
504
505         status = ads_find_user_acct(ads, &res, argv[0]);
506
507         if (!ADS_ERR_OK(status)) {
508                 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
509                 goto done;
510         }
511         
512         if (ads_count_replies(ads, res)) {
513                 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
514                 ads_msgfree(ads, res);
515                 goto done;
516         }
517
518         if (opt_container == NULL) {
519                 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
520         }
521
522         status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
523
524         if (ADS_ERR_OK(status)) {
525                 d_printf("Group %s added\n", argv[0]);
526                 rc = 0;
527         } else {
528                 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
529                          ads_errstr(status));
530         }
531
532  done:
533         if (res)
534                 ads_msgfree(ads, res);
535         ads_destroy(&ads);
536         return rc;
537 }
538
539 static int ads_group_delete(int argc, const char **argv)
540 {
541         ADS_STRUCT *ads;
542         ADS_STATUS rc;
543         void *res;
544         char *groupdn;
545
546         if (argc < 1) {
547                 return net_ads_group_usage(argc, argv);
548         }
549         
550         if (!(ads = ads_startup())) {
551                 return -1;
552         }
553
554         rc = ads_find_user_acct(ads, &res, argv[0]);
555         if (!ADS_ERR_OK(rc)) {
556                 DEBUG(0, ("Group %s does not exist\n", argv[0]));
557                 ads_destroy(&ads);
558                 return -1;
559         }
560         groupdn = ads_get_dn(ads, res);
561         ads_msgfree(ads, res);
562         rc = ads_del_dn(ads, groupdn);
563         ads_memfree(ads, groupdn);
564         if (!ADS_ERR_OK(rc)) {
565                 d_printf("Group %s deleted\n", argv[0]);
566                 ads_destroy(&ads);
567                 return 0;
568         }
569         d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0], 
570                  ads_errstr(rc));
571         ads_destroy(&ads);
572         return -1;
573 }
574
575 int net_ads_group(int argc, const char **argv)
576 {
577         struct functable func[] = {
578                 {"ADD", ads_group_add},
579                 {"DELETE", ads_group_delete},
580                 {NULL, NULL}
581         };
582         ADS_STRUCT *ads;
583         ADS_STATUS rc;
584         const char *shortattrs[] = {"sAMAccountName", NULL};
585         const char *longattrs[] = {"sAMAccountName", "description", NULL};
586         char *disp_fields[2] = {NULL, NULL};
587
588         if (argc == 0) {
589                 if (!(ads = ads_startup())) {
590                         return -1;
591                 }
592
593                 if (opt_long_list_entries)
594                         d_printf("\nGroup name            Comment"\
595                                  "\n-----------------------------\n");
596                 rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
597                                           LDAP_SCOPE_SUBTREE, 
598                                           "(objectclass=group)", 
599                                           opt_long_list_entries ? longattrs : 
600                                           shortattrs, usergrp_display, 
601                                           disp_fields);
602
603                 ads_destroy(&ads);
604                 return 0;
605         }
606         return net_run_function(argc, argv, func, net_ads_group_usage);
607 }
608
609 static int net_ads_status(int argc, const char **argv)
610 {
611         ADS_STRUCT *ads;
612         ADS_STATUS rc;
613         void *res;
614
615         if (!(ads = ads_startup())) {
616                 return -1;
617         }
618
619         rc = ads_find_machine_acct(ads, &res, global_myname());
620         if (!ADS_ERR_OK(rc)) {
621                 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
622                 ads_destroy(&ads);
623                 return -1;
624         }
625
626         if (ads_count_replies(ads, res) == 0) {
627                 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
628                 ads_destroy(&ads);
629                 return -1;
630         }
631
632         ads_dump(ads, res);
633         ads_destroy(&ads);
634         return 0;
635 }
636
637 static int net_ads_leave(int argc, const char **argv)
638 {
639         ADS_STRUCT *ads = NULL;
640         ADS_STATUS rc;
641
642         if (!secrets_init()) {
643                 DEBUG(1,("Failed to initialise secrets database\n"));
644                 return -1;
645         }
646
647         if (!opt_password) {
648                 net_use_machine_password();
649         }
650
651         if (!(ads = ads_startup())) {
652                 return -1;
653         }
654
655         rc = ads_leave_realm(ads, global_myname());
656         if (!ADS_ERR_OK(rc)) {
657                 d_fprintf(stderr, "Failed to delete host '%s' from the '%s' realm.\n", 
658                         global_myname(), ads->config.realm);
659                 ads_destroy(&ads);
660                 return -1;
661         }
662
663         d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
664         ads_destroy(&ads);
665         return 0;
666 }
667
668 static int net_ads_join_ok(void)
669 {
670         ADS_STRUCT *ads = NULL;
671
672         if (!secrets_init()) {
673                 DEBUG(1,("Failed to initialise secrets database\n"));
674                 return -1;
675         }
676
677         net_use_machine_password();
678
679         if (!(ads = ads_startup())) {
680                 return -1;
681         }
682
683         ads_destroy(&ads);
684         return 0;
685 }
686
687 /*
688   check that an existing join is OK
689  */
690 int net_ads_testjoin(int argc, const char **argv)
691 {
692         use_in_memory_ccache();
693
694         /* Display success or failure */
695         if (net_ads_join_ok() != 0) {
696                 fprintf(stderr,"Join to domain is not valid\n");
697                 return -1;
698         }
699
700         printf("Join is OK\n");
701         return 0;
702 }
703
704 /*
705   join a domain using ADS
706  */
707 int net_ads_join(int argc, const char **argv)
708 {
709         ADS_STRUCT *ads;
710         ADS_STATUS rc;
711         char *password;
712         char *machine_account = NULL;
713         char *tmp_password;
714         const char *org_unit = NULL;
715         char *dn;
716         void *res;
717         DOM_SID dom_sid;
718         char *ou_str;
719         uint32 sec_channel_type = SEC_CHAN_WKSTA;
720         uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT;
721         const char *short_domain_name = NULL;
722         TALLOC_CTX *ctx = NULL;
723
724         if (lp_server_role() == ROLE_STANDALONE) {
725                 d_printf("cannot join as standalone machine\n");
726                 return -1;
727         }
728
729         if (strlen(global_myname()) > 15) {
730                 d_printf("Our netbios name can only be 15 chars long, \"%s\""
731                          " is %d chars long\n",
732                          global_myname(), strlen(global_myname()));
733                 return -1;
734         }
735
736         if (argc > 0) {
737                 org_unit = argv[0];
738         }
739
740         if (!secrets_init()) {
741                 DEBUG(1,("Failed to initialise secrets database\n"));
742                 return -1;
743         }
744
745         tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
746         password = SMB_STRDUP(tmp_password);
747
748         if (!(ads = ads_startup())) {
749                 return -1;
750         }
751
752         if (!*lp_realm()) {
753                 d_fprintf(stderr, "realm must be set in in smb.conf for ADS join to succeed.\n");
754                 ads_destroy(&ads);
755                 return -1;
756         }
757
758         if (strcmp(ads->config.realm, lp_realm()) != 0) {
759                 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf (%s) DO NOT match.  Aborting join\n", ads->config.realm, lp_realm());
760                 ads_destroy(&ads);
761                 return -1;
762         }
763
764         ou_str = ads_ou_string(ads,org_unit);
765         asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
766         free(ou_str);
767
768         rc = ads_search_dn(ads, &res, dn, NULL);
769         ads_msgfree(ads, res);
770
771         if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
772                 d_fprintf(stderr, "ads_join_realm: organizational unit %s does not exist (dn:%s)\n", 
773                          org_unit, dn);
774                 ads_destroy(&ads);
775                 return -1;
776         }
777         free(dn);
778
779         if (!ADS_ERR_OK(rc)) {
780                 d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc));
781                 ads_destroy(&ads);
782                 return -1;
783         }       
784
785         rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
786         if (!ADS_ERR_OK(rc)) {
787                 d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc));
788                 ads_destroy(&ads);
789                 return -1;
790         }
791
792         rc = ads_domain_sid(ads, &dom_sid);
793         if (!ADS_ERR_OK(rc)) {
794                 d_fprintf(stderr, "ads_domain_sid: %s\n", ads_errstr(rc));      
795                 ads_destroy(&ads);
796                 return -1;
797         }
798
799         if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
800                 d_fprintf(stderr, "asprintf failed\n");
801                 ads_destroy(&ads);
802                 return -1;
803         }
804
805         rc = ads_set_machine_password(ads, machine_account, password);
806         if (!ADS_ERR_OK(rc)) {
807                 d_fprintf(stderr, "ads_set_machine_password: %s\n", ads_errstr(rc));
808                 ads_destroy(&ads);
809                 return -1;
810         }
811         
812         /* make sure we get the right workgroup */
813         
814         if ( !(ctx = talloc_init("net ads join")) ) {
815                 d_fprintf(stderr, "talloc_init() failed!\n");
816                 ads_destroy(&ads);
817                 return -1;
818         }
819         
820         rc = ads_workgroup_name(ads, ctx, &short_domain_name);
821         if ( ADS_ERR_OK(rc) ) {
822                 if ( !strequal(lp_workgroup(), short_domain_name) ) {
823                         d_printf("The workgroup in smb.conf does not match the short\n");
824                         d_printf("domain name obtained from the server.\n");
825                         d_printf("Using the name [%s] from the server.\n", short_domain_name);
826                         d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
827                 }
828         } else {
829                 short_domain_name = lp_workgroup();
830         }
831         
832         d_printf("Using short domain name -- %s\n", short_domain_name);
833         
834         /*  HACK ALRET!  Store the sid and password under bother the lp_workgroup() 
835             value from smb.conf and the string returned from the server.  The former is
836             neede to bootstrap winbindd's first connection to the DC to get the real 
837             short domain name   --jerry */
838             
839         if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
840                 DEBUG(1,("Failed to save domain sid\n"));
841                 ads_destroy(&ads);
842                 return -1;
843         }
844
845         if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
846                 DEBUG(1,("Failed to save machine password\n"));
847                 ads_destroy(&ads);
848                 return -1;
849         }
850
851 #ifdef HAVE_KRB5
852         if (!kerberos_derive_salting_principal(machine_account)) {
853                 DEBUG(1,("Failed to determine salting principal\n"));
854                 ads_destroy(&ads);
855                 return -1;
856         }
857
858         if (!kerberos_derive_cifs_salting_principals()) {
859                 DEBUG(1,("Failed to determine salting principals\n"));
860                 ads_destroy(&ads);
861                 return -1;
862         }
863 #endif
864
865         if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
866                 DEBUG(1,("Failed to save domain sid\n"));
867                 ads_destroy(&ads);
868                 return -1;
869         }
870
871         if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
872                 DEBUG(1,("Failed to save machine password\n"));
873                 ads_destroy(&ads);
874                 return -1;
875         }
876         
877         /* Now build the keytab, using the same ADS connection */
878         if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
879                 DEBUG(1,("Error creating host keytab!\n"));
880         }
881
882         d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
883
884         SAFE_FREE(password);
885         SAFE_FREE(machine_account);
886         if ( ctx ) {
887                 talloc_destroy(ctx);
888         }
889         ads_destroy(&ads);
890         return 0;
891 }
892
893 int net_ads_printer_usage(int argc, const char **argv)
894 {
895         d_printf(
896 "\nnet ads printer search <printer>"
897 "\n\tsearch for a printer in the directory\n"
898 "\nnet ads printer info <printer> <server>"
899 "\n\tlookup info in directory for printer on server"
900 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
901 "\nnet ads printer publish <printername>"
902 "\n\tpublish printer in directory"
903 "\n\t(note: printer name is required)\n"
904 "\nnet ads printer remove <printername>"
905 "\n\tremove printer from directory"
906 "\n\t(note: printer name is required)\n");
907         return -1;
908 }
909
910 static int net_ads_printer_search(int argc, const char **argv)
911 {
912         ADS_STRUCT *ads;
913         ADS_STATUS rc;
914         void *res = NULL;
915
916         if (!(ads = ads_startup())) {
917                 return -1;
918         }
919
920         rc = ads_find_printers(ads, &res);
921
922         if (!ADS_ERR_OK(rc)) {
923                 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
924                 ads_msgfree(ads, res);
925                 ads_destroy(&ads);
926                 return -1;
927         }
928
929         if (ads_count_replies(ads, res) == 0) {
930                 d_fprintf(stderr, "No results found\n");
931                 ads_msgfree(ads, res);
932                 ads_destroy(&ads);
933                 return -1;
934         }
935
936         ads_dump(ads, res);
937         ads_msgfree(ads, res);
938         ads_destroy(&ads);
939         return 0;
940 }
941
942 static int net_ads_printer_info(int argc, const char **argv)
943 {
944         ADS_STRUCT *ads;
945         ADS_STATUS rc;
946         const char *servername, *printername;
947         void *res = NULL;
948
949         if (!(ads = ads_startup())) {
950                 return -1;
951         }
952
953         if (argc > 0) {
954                 printername = argv[0];
955         } else {
956                 printername = "*";
957         }
958
959         if (argc > 1) {
960                 servername =  argv[1];
961         } else {
962                 servername = global_myname();
963         }
964
965         rc = ads_find_printer_on_server(ads, &res, printername, servername);
966
967         if (!ADS_ERR_OK(rc)) {
968                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
969                 ads_msgfree(ads, res);
970                 ads_destroy(&ads);
971                 return -1;
972         }
973
974         if (ads_count_replies(ads, res) == 0) {
975                 d_fprintf(stderr, "Printer '%s' not found\n", printername);
976                 ads_msgfree(ads, res);
977                 ads_destroy(&ads);
978                 return -1;
979         }
980
981         ads_dump(ads, res);
982         ads_msgfree(ads, res);
983         ads_destroy(&ads);
984
985         return 0;
986 }
987
988 void do_drv_upgrade_printer(int msg_type, struct process_id src,
989                             void *buf, size_t len)
990 {
991         return;
992 }
993
994 static int net_ads_printer_publish(int argc, const char **argv)
995 {
996         ADS_STRUCT *ads;
997         ADS_STATUS rc;
998         const char *servername, *printername;
999         struct cli_state *cli;
1000         struct rpc_pipe_client *pipe_hnd;
1001         struct in_addr          server_ip;
1002         NTSTATUS nt_status;
1003         TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1004         ADS_MODLIST mods = ads_init_mods(mem_ctx);
1005         char *prt_dn, *srv_dn, **srv_cn;
1006         void *res = NULL;
1007
1008         if (!(ads = ads_startup())) {
1009                 return -1;
1010         }
1011
1012         if (argc < 1) {
1013                 return net_ads_printer_usage(argc, argv);
1014         }
1015         
1016         printername = argv[0];
1017
1018         if (argc == 2) {
1019                 servername = argv[1];
1020         } else {
1021                 servername = global_myname();
1022         }
1023                 
1024         /* Get printer data from SPOOLSS */
1025
1026         resolve_name(servername, &server_ip, 0x20);
1027
1028         nt_status = cli_full_connection(&cli, global_myname(), servername, 
1029                                         &server_ip, 0,
1030                                         "IPC$", "IPC",  
1031                                         opt_user_name, opt_workgroup,
1032                                         opt_password ? opt_password : "", 
1033                                         CLI_FULL_CONNECTION_USE_KERBEROS, 
1034                                         Undefined, NULL);
1035
1036         if (NT_STATUS_IS_ERR(nt_status)) {
1037                 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1038                          "for %s\n", servername, printername);
1039                 ads_destroy(&ads);
1040                 return -1;
1041         }
1042
1043         /* Publish on AD server */
1044
1045         ads_find_machine_acct(ads, &res, servername);
1046
1047         if (ads_count_replies(ads, res) == 0) {
1048                 d_fprintf(stderr, "Could not find machine account for server %s\n", 
1049                          servername);
1050                 ads_destroy(&ads);
1051                 return -1;
1052         }
1053
1054         srv_dn = ldap_get_dn(ads->ld, res);
1055         srv_cn = ldap_explode_dn(srv_dn, 1);
1056
1057         asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1058
1059         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1060         if (!pipe_hnd) {
1061                 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1062                          servername);
1063                 ads_destroy(&ads);
1064                 return -1;
1065         }
1066
1067         get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1068                                            printername);
1069
1070         rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1071         if (!ADS_ERR_OK(rc)) {
1072                 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1073                 ads_destroy(&ads);
1074                 return -1;
1075         }
1076  
1077         d_printf("published printer\n");
1078         ads_destroy(&ads);
1079  
1080         return 0;
1081 }
1082
1083 static int net_ads_printer_remove(int argc, const char **argv)
1084 {
1085         ADS_STRUCT *ads;
1086         ADS_STATUS rc;
1087         const char *servername;
1088         char *prt_dn;
1089         void *res = NULL;
1090
1091         if (!(ads = ads_startup())) {
1092                 return -1;
1093         }
1094
1095         if (argc < 1) {
1096                 return net_ads_printer_usage(argc, argv);
1097         }
1098
1099         if (argc > 1) {
1100                 servername = argv[1];
1101         } else {
1102                 servername = global_myname();
1103         }
1104
1105         rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1106
1107         if (!ADS_ERR_OK(rc)) {
1108                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1109                 ads_msgfree(ads, res);
1110                 ads_destroy(&ads);
1111                 return -1;
1112         }
1113
1114         if (ads_count_replies(ads, res) == 0) {
1115                 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1116                 ads_msgfree(ads, res);
1117                 ads_destroy(&ads);
1118                 return -1;
1119         }
1120
1121         prt_dn = ads_get_dn(ads, res);
1122         ads_msgfree(ads, res);
1123         rc = ads_del_dn(ads, prt_dn);
1124         ads_memfree(ads, prt_dn);
1125
1126         if (!ADS_ERR_OK(rc)) {
1127                 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1128                 ads_destroy(&ads);
1129                 return -1;
1130         }
1131
1132         ads_destroy(&ads);
1133         return 0;
1134 }
1135
1136 static int net_ads_printer(int argc, const char **argv)
1137 {
1138         struct functable func[] = {
1139                 {"SEARCH", net_ads_printer_search},
1140                 {"INFO", net_ads_printer_info},
1141                 {"PUBLISH", net_ads_printer_publish},
1142                 {"REMOVE", net_ads_printer_remove},
1143                 {NULL, NULL}
1144         };
1145         
1146         return net_run_function(argc, argv, func, net_ads_printer_usage);
1147 }
1148
1149
1150 static int net_ads_password(int argc, const char **argv)
1151 {
1152         ADS_STRUCT *ads;
1153         const char *auth_principal = opt_user_name;
1154         const char *auth_password = opt_password;
1155         char *realm = NULL;
1156         char *new_password = NULL;
1157         char *c, *prompt;
1158         const char *user;
1159         ADS_STATUS ret;
1160
1161         if (opt_user_name == NULL || opt_password == NULL) {
1162                 d_fprintf(stderr, "You must supply an administrator username/password\n");
1163                 return -1;
1164         }
1165
1166         if (argc < 1) {
1167                 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1168                 return -1;
1169         }
1170
1171         user = argv[0];
1172         if (!strchr_m(user, '@')) {
1173                 asprintf(&c, "%s@%s", argv[0], lp_realm());
1174                 user = c;
1175         }
1176
1177         use_in_memory_ccache();    
1178         c = strchr_m(auth_principal, '@');
1179         if (c) {
1180                 realm = ++c;
1181         } else {
1182                 realm = lp_realm();
1183         }
1184
1185         /* use the realm so we can eventually change passwords for users 
1186         in realms other than default */
1187         if (!(ads = ads_init(realm, NULL, NULL))) {
1188                 return -1;
1189         }
1190
1191         /* we don't actually need a full connect, but it's the easy way to
1192                 fill in the KDC's addresss */
1193         ads_connect(ads);
1194     
1195         if (!ads || !ads->config.realm) {
1196                 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1197                 return -1;
1198         }
1199
1200         if (argv[1]) {
1201                 new_password = (char *)argv[1];
1202         } else {
1203                 asprintf(&prompt, "Enter new password for %s:", user);
1204                 new_password = getpass(prompt);
1205                 free(prompt);
1206         }
1207
1208         ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
1209                                 auth_password, user, new_password, ads->auth.time_offset);
1210         if (!ADS_ERR_OK(ret)) {
1211                 d_fprintf(stderr, "Password change failed :-( ...\n");
1212                 ads_destroy(&ads);
1213                 return -1;
1214         }
1215
1216         d_printf("Password change for %s completed.\n", user);
1217         ads_destroy(&ads);
1218
1219         return 0;
1220 }
1221
1222 int net_ads_changetrustpw(int argc, const char **argv)
1223 {    
1224         ADS_STRUCT *ads;
1225         char *host_principal;
1226         fstring my_name;
1227         ADS_STATUS ret;
1228
1229         if (!secrets_init()) {
1230                 DEBUG(1,("Failed to initialise secrets database\n"));
1231                 return -1;
1232         }
1233
1234         net_use_machine_password();
1235
1236         use_in_memory_ccache();
1237
1238         if (!(ads = ads_startup())) {
1239                 return -1;
1240         }
1241
1242         fstrcpy(my_name, global_myname());
1243         strlower_m(my_name);
1244         asprintf(&host_principal, "%s@%s", my_name, ads->config.realm);
1245         d_printf("Changing password for principal: HOST/%s\n", host_principal);
1246
1247         ret = ads_change_trust_account_password(ads, host_principal);
1248
1249         if (!ADS_ERR_OK(ret)) {
1250                 d_fprintf(stderr, "Password change failed :-( ...\n");
1251                 ads_destroy(&ads);
1252                 SAFE_FREE(host_principal);
1253                 return -1;
1254         }
1255     
1256         d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
1257
1258         if (lp_use_kerberos_keytab()) {
1259                 d_printf("Attempting to update system keytab with new password.\n");
1260                 if (ads_keytab_create_default(ads)) {
1261                         d_printf("Failed to update system keytab.\n");
1262                 }
1263         }
1264
1265         ads_destroy(&ads);
1266         SAFE_FREE(host_principal);
1267
1268         return 0;
1269 }
1270
1271 /*
1272   help for net ads search
1273 */
1274 static int net_ads_search_usage(int argc, const char **argv)
1275 {
1276         d_printf(
1277                 "\nnet ads search <expression> <attributes...>\n"\
1278                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1279                 "The expression is a standard LDAP search expression, and the\n"\
1280                 "attributes are a list of LDAP fields to show in the results\n\n"\
1281                 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1282                 );
1283         net_common_flags_usage(argc, argv);
1284         return -1;
1285 }
1286
1287
1288 /*
1289   general ADS search function. Useful in diagnosing problems in ADS
1290 */
1291 static int net_ads_search(int argc, const char **argv)
1292 {
1293         ADS_STRUCT *ads;
1294         ADS_STATUS rc;
1295         const char *ldap_exp;
1296         const char **attrs;
1297         void *res = NULL;
1298
1299         if (argc < 1) {
1300                 return net_ads_search_usage(argc, argv);
1301         }
1302
1303         if (!(ads = ads_startup())) {
1304                 return -1;
1305         }
1306
1307         ldap_exp = argv[0];
1308         attrs = (argv + 1);
1309
1310         rc = ads_do_search_all(ads, ads->config.bind_path,
1311                                LDAP_SCOPE_SUBTREE,
1312                                ldap_exp, attrs, &res);
1313         if (!ADS_ERR_OK(rc)) {
1314                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1315                 ads_destroy(&ads);
1316                 return -1;
1317         }       
1318
1319         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1320
1321         /* dump the results */
1322         ads_dump(ads, res);
1323
1324         ads_msgfree(ads, res);
1325         ads_destroy(&ads);
1326
1327         return 0;
1328 }
1329
1330
1331 /*
1332   help for net ads search
1333 */
1334 static int net_ads_dn_usage(int argc, const char **argv)
1335 {
1336         d_printf(
1337                 "\nnet ads dn <dn> <attributes...>\n"\
1338                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1339                 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1340                 "to show in the results\n\n"\
1341                 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1342                 );
1343         net_common_flags_usage(argc, argv);
1344         return -1;
1345 }
1346
1347
1348 /*
1349   general ADS search function. Useful in diagnosing problems in ADS
1350 */
1351 static int net_ads_dn(int argc, const char **argv)
1352 {
1353         ADS_STRUCT *ads;
1354         ADS_STATUS rc;
1355         const char *dn;
1356         const char **attrs;
1357         void *res = NULL;
1358
1359         if (argc < 1) {
1360                 return net_ads_dn_usage(argc, argv);
1361         }
1362
1363         if (!(ads = ads_startup())) {
1364                 return -1;
1365         }
1366
1367         dn = argv[0];
1368         attrs = (argv + 1);
1369
1370         rc = ads_do_search_all(ads, dn, 
1371                                LDAP_SCOPE_BASE,
1372                                "(objectclass=*)", attrs, &res);
1373         if (!ADS_ERR_OK(rc)) {
1374                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1375                 ads_destroy(&ads);
1376                 return -1;
1377         }       
1378
1379         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1380
1381         /* dump the results */
1382         ads_dump(ads, res);
1383
1384         ads_msgfree(ads, res);
1385         ads_destroy(&ads);
1386
1387         return 0;
1388 }
1389
1390 static int net_ads_keytab_usage(int argc, const char **argv)
1391 {
1392         d_printf(
1393                 "net ads keytab <COMMAND>\n"\
1394 "<COMMAND> can be either:\n"\
1395 "  CREATE    Creates a fresh keytab\n"\
1396 "  ADD       Adds new service principal\n"\
1397 "  FLUSH     Flushes out all keytab entries\n"\
1398 "  HELP      Prints this help message\n"\
1399 "The ADD command will take arguments, the other commands\n"\
1400 "will not take any arguments.   The arguments given to ADD\n"\
1401 "should be a list of principals to add.  For example, \n"\
1402 "   net ads keytab add srv1 srv2\n"\
1403 "will add principals for the services srv1 and srv2 to the\n"\
1404 "system's keytab.\n"\
1405 "\n"
1406                 );
1407         return -1;
1408 }
1409
1410 static int net_ads_keytab_flush(int argc, const char **argv)
1411 {
1412         int ret;
1413         ADS_STRUCT *ads;
1414
1415         if (!(ads = ads_startup())) {
1416                 return -1;
1417         }
1418         ret = ads_keytab_flush(ads);
1419         ads_destroy(&ads);
1420         return ret;
1421 }
1422
1423 static int net_ads_keytab_add(int argc, const char **argv)
1424 {
1425         int i;
1426         int ret = 0;
1427         ADS_STRUCT *ads;
1428
1429         d_printf("Processing principals to add...\n");
1430         if (!(ads = ads_startup())) {
1431                 return -1;
1432         }
1433         for (i = 0; i < argc; i++) {
1434                 ret |= ads_keytab_add_entry(ads, argv[i]);
1435         }
1436         ads_destroy(&ads);
1437         return ret;
1438 }
1439
1440 static int net_ads_keytab_create(int argc, const char **argv)
1441 {
1442         ADS_STRUCT *ads;
1443         int ret;
1444
1445         if (!(ads = ads_startup())) {
1446                 return -1;
1447         }
1448         ret = ads_keytab_create_default(ads);
1449         ads_destroy(&ads);
1450         return ret;
1451 }
1452
1453 int net_ads_keytab(int argc, const char **argv)
1454 {
1455         struct functable func[] = {
1456                 {"CREATE", net_ads_keytab_create},
1457                 {"ADD", net_ads_keytab_add},
1458                 {"FLUSH", net_ads_keytab_flush},
1459                 {"HELP", net_ads_keytab_usage},
1460                 {NULL, NULL}
1461         };
1462
1463         if (!lp_use_kerberos_keytab()) {
1464                 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1465 use keytab functions.\n");
1466         }
1467
1468         return net_run_function(argc, argv, func, net_ads_keytab_usage);
1469 }
1470
1471 int net_ads_help(int argc, const char **argv)
1472 {
1473         struct functable func[] = {
1474                 {"USER", net_ads_user_usage},
1475                 {"GROUP", net_ads_group_usage},
1476                 {"PRINTER", net_ads_printer_usage},
1477                 {"SEARCH", net_ads_search_usage},
1478 #if 0
1479                 {"INFO", net_ads_info},
1480                 {"JOIN", net_ads_join},
1481                 {"LEAVE", net_ads_leave},
1482                 {"STATUS", net_ads_status},
1483                 {"PASSWORD", net_ads_password},
1484                 {"CHANGETRUSTPW", net_ads_changetrustpw},
1485 #endif
1486                 {NULL, NULL}
1487         };
1488
1489         return net_run_function(argc, argv, func, net_ads_usage);
1490 }
1491
1492 int net_ads(int argc, const char **argv)
1493 {
1494         struct functable func[] = {
1495                 {"INFO", net_ads_info},
1496                 {"JOIN", net_ads_join},
1497                 {"TESTJOIN", net_ads_testjoin},
1498                 {"LEAVE", net_ads_leave},
1499                 {"STATUS", net_ads_status},
1500                 {"USER", net_ads_user},
1501                 {"GROUP", net_ads_group},
1502                 {"PASSWORD", net_ads_password},
1503                 {"CHANGETRUSTPW", net_ads_changetrustpw},
1504                 {"PRINTER", net_ads_printer},
1505                 {"SEARCH", net_ads_search},
1506                 {"DN", net_ads_dn},
1507                 {"WORKGROUP", net_ads_workgroup},
1508                 {"LOOKUP", net_ads_lookup},
1509                 {"KEYTAB", net_ads_keytab},
1510                 {"HELP", net_ads_help},
1511                 {NULL, NULL}
1512         };
1513         
1514         return net_run_function(argc, argv, func, net_ads_usage);
1515 }
1516
1517 #else
1518
1519 static int net_ads_noads(void)
1520 {
1521         d_fprintf(stderr, "ADS support not compiled in\n");
1522         return -1;
1523 }
1524
1525 int net_ads_keytab(int argc, const char **argv)
1526 {
1527         return net_ads_noads();
1528 }
1529
1530 int net_ads_usage(int argc, const char **argv)
1531 {
1532         return net_ads_noads();
1533 }
1534
1535 int net_ads_help(int argc, const char **argv)
1536 {
1537         return net_ads_noads();
1538 }
1539
1540 int net_ads_changetrustpw(int argc, const char **argv)
1541 {
1542         return net_ads_noads();
1543 }
1544
1545 int net_ads_join(int argc, const char **argv)
1546 {
1547         return net_ads_noads();
1548 }
1549
1550 int net_ads_user(int argc, const char **argv)
1551 {
1552         return net_ads_noads();
1553 }
1554
1555 int net_ads_group(int argc, const char **argv)
1556 {
1557         return net_ads_noads();
1558 }
1559
1560 /* this one shouldn't display a message */
1561 int net_ads_check(void)
1562 {
1563         return -1;
1564 }
1565
1566 int net_ads(int argc, const char **argv)
1567 {
1568         return net_ads_usage(argc, argv);
1569 }
1570
1571 #endif