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