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