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