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