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