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