r17551: Move some DEBUG to d_printf in interactive functions and return
[kai/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         
59         return -1;
60 }
61
62 /* when we do not have sufficient input parameters to contact a remote domain
63  * we always fall back to our own realm - Guenther*/
64
65 static const char *assume_own_realm(void)
66 {
67         if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
68                 return lp_realm();
69         }
70
71         return NULL;
72 }
73
74 /*
75   do a cldap netlogon query
76 */
77 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
78 {
79         struct cldap_netlogon_reply reply;
80
81         if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
82                 d_fprintf(stderr, "CLDAP query failed!\n");
83                 return -1;
84         }
85
86         d_printf("Information for Domain Controller: %s\n\n", 
87                 inet_ntoa(ads->ldap_ip));
88
89         d_printf("Response Type: ");
90         switch (reply.type) {
91         case SAMLOGON_AD_UNK_R:
92                 d_printf("SAMLOGON\n");
93                 break;
94         case SAMLOGON_AD_R:
95                 d_printf("SAMLOGON_USER\n");
96                 break;
97         default:
98                 d_printf("0x%x\n", reply.type);
99                 break;
100         }
101         d_printf("GUID: %s\n", 
102                  smb_uuid_string_static(smb_uuid_unpack_static(reply.guid))); 
103         d_printf("Flags:\n"
104                  "\tIs a PDC:                                   %s\n"
105                  "\tIs a GC of the forest:                      %s\n"
106                  "\tIs an LDAP server:                          %s\n"
107                  "\tSupports DS:                                %s\n"
108                  "\tIs running a KDC:                           %s\n"
109                  "\tIs running time services:                   %s\n"
110                  "\tIs the closest DC:                          %s\n"
111                  "\tIs writable:                                %s\n"
112                  "\tHas a hardware clock:                       %s\n"
113                  "\tIs a non-domain NC serviced by LDAP server: %s\n",
114                  (reply.flags & ADS_PDC) ? "yes" : "no",
115                  (reply.flags & ADS_GC) ? "yes" : "no",
116                  (reply.flags & ADS_LDAP) ? "yes" : "no",
117                  (reply.flags & ADS_DS) ? "yes" : "no",
118                  (reply.flags & ADS_KDC) ? "yes" : "no",
119                  (reply.flags & ADS_TIMESERV) ? "yes" : "no",
120                  (reply.flags & ADS_CLOSEST) ? "yes" : "no",
121                  (reply.flags & ADS_WRITABLE) ? "yes" : "no",
122                  (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
123                  (reply.flags & ADS_NDNC) ? "yes" : "no");
124
125         printf("Forest:\t\t\t%s\n", reply.forest);
126         printf("Domain:\t\t\t%s\n", reply.domain);
127         printf("Domain Controller:\t%s\n", reply.hostname);
128
129         printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
130         printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
131
132         if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
133         if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
134
135         printf("Site Name:\t\t%s\n", reply.site_name);
136         printf("Site Name (2):\t\t%s\n", reply.site_name_2);
137
138         d_printf("NT Version: %d\n", reply.version);
139         d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
140         d_printf("LM20 Token: %.2x\n", reply.lm20_token);
141
142         return 0;
143 }
144
145
146 /*
147   this implements the CLDAP based netlogon lookup requests
148   for finding the domain controller of a ADS domain
149 */
150 static int net_ads_lookup(int argc, const char **argv)
151 {
152         ADS_STRUCT *ads;
153         ADS_STATUS status;
154         const char *realm = assume_own_realm();
155
156         ads = ads_init(realm, opt_target_workgroup, opt_host);
157         if (ads) {
158                 ads->auth.flags |= ADS_AUTH_NO_BIND;
159         }
160
161         status = ads_connect(ads);
162         if (!ADS_ERR_OK(status) || !ads) {
163                 d_fprintf(stderr, "Didn't find the cldap server!\n");
164                 return -1;
165         }
166         
167         if (!ads->config.realm) {
168                 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
169                 ads->ldap_port = 389;
170         }
171
172         return net_ads_cldap_netlogon(ads);
173 }
174
175
176
177 static int net_ads_info(int argc, const char **argv)
178 {
179         ADS_STRUCT *ads;
180         const char *realm = assume_own_realm();
181
182         if ( (ads = ads_init(realm, opt_target_workgroup, opt_host)) != NULL ) {
183                 ads->auth.flags |= ADS_AUTH_NO_BIND;
184         }
185
186         ads_connect(ads);
187
188         if (!ads || !ads->config.realm) {
189                 d_fprintf(stderr, "Didn't find the ldap server!\n");
190                 return -1;
191         }
192
193         /* Try to set the server's current time since we didn't do a full
194            TCP LDAP session initially */
195
196         if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
197                 d_fprintf( stderr, "Failed to get server's current time!\n");
198         }
199
200         d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
201         d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
202         d_printf("Realm: %s\n", ads->config.realm);
203         d_printf("Bind Path: %s\n", ads->config.bind_path);
204         d_printf("LDAP port: %d\n", ads->ldap_port);
205         d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
206
207         d_printf("KDC server: %s\n", ads->auth.kdc_server );
208         d_printf("Server time offset: %d\n", ads->auth.time_offset );
209
210         return 0;
211 }
212
213 static void use_in_memory_ccache(void) {
214         /* Use in-memory credentials cache so we do not interfere with
215          * existing credentials */
216         setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
217 }
218
219 static ADS_STRUCT *ads_startup(BOOL only_own_domain)
220 {
221         ADS_STRUCT *ads;
222         ADS_STATUS status;
223         BOOL need_password = False;
224         BOOL second_time = False;
225         char *cp;
226         const char *realm = NULL;
227         
228         /* lp_realm() should be handled by a command line param, 
229            However, the join requires that realm be set in smb.conf
230            and compares our realm with the remote server's so this is
231            ok until someone needs more flexibility */
232
233         if (only_own_domain) {
234                 realm = lp_realm();
235         }
236    
237         ads = ads_init(realm, opt_target_workgroup, opt_host);
238
239         if (!opt_user_name) {
240                 opt_user_name = "administrator";
241         }
242
243         if (opt_user_specified) {
244                 need_password = True;
245         }
246
247 retry:
248         if (!opt_password && need_password && !opt_machine_pass) {
249                 char *prompt;
250                 asprintf(&prompt,"%s's password: ", opt_user_name);
251                 opt_password = getpass(prompt);
252                 free(prompt);
253         }
254
255         if (opt_password) {
256                 use_in_memory_ccache();
257                 ads->auth.password = smb_xstrdup(opt_password);
258         }
259
260         ads->auth.user_name = smb_xstrdup(opt_user_name);
261
262        /*
263         * If the username is of the form "name@realm", 
264         * extract the realm and convert to upper case.
265         * This is only used to establish the connection.
266         */
267        if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
268                *cp++ = '\0';
269                ads->auth.realm = smb_xstrdup(cp);
270                strupper_m(ads->auth.realm);
271        }
272
273         status = ads_connect(ads);
274
275         if (!ADS_ERR_OK(status)) {
276                 if (!need_password && !second_time) {
277                         need_password = True;
278                         second_time = True;
279                         goto retry;
280                 } else {
281                         d_printf("%s.\n", ads_errstr(status));
282                         ads_destroy(&ads);
283                         return NULL;
284                 }
285         }
286         return ads;
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         void *res=NULL;
403         int rc = -1;
404
405         if (argc < 1) return net_ads_user_usage(argc, argv);
406         
407         if (!(ads = ads_startup(False))) {
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         void *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 = ads_startup(False))) {
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         void *res;
533         char *userdn;
534
535         if (argc < 1) {
536                 return net_ads_user_usage(argc, argv);
537         }
538         
539         if (!(ads = ads_startup(False))) {
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 = ads_startup(False))) {
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         void *res=NULL;
610         int rc = -1;
611
612         if (argc < 1) {
613                 return net_ads_group_usage(argc, argv);
614         }
615         
616         if (!(ads = ads_startup(False))) {
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         void *res;
659         char *groupdn;
660
661         if (argc < 1) {
662                 return net_ads_group_usage(argc, argv);
663         }
664         
665         if (!(ads = ads_startup(False))) {
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 = ads_startup(False))) {
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         void *res;
729
730         if (!(ads = ads_startup(True))) {
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 static int net_ads_leave(int argc, const char **argv)
759 {
760         ADS_STRUCT *ads = NULL;
761         int ret = -1;
762         struct cli_state *cli = NULL;
763         TALLOC_CTX *ctx;
764         DOM_SID *dom_sid = NULL;
765
766         if (!secrets_init()) {
767                 DEBUG(1,("Failed to initialise secrets database\n"));
768                 return -1;
769         }
770
771         if (!(ctx = talloc_init("net_ads_leave"))) {
772                 d_fprintf(stderr, "Could not initialise talloc context.\n");
773                 return -1;
774         }
775
776         /* The finds a DC and takes care of getting the 
777            user creds if necessary */
778
779         if (!(ads = ads_startup(True))) {
780                 return -1;
781         }
782
783         /* make RPC calls here */
784
785         if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip, 
786                 ads->config.ldap_server_name)) )
787         {
788                 goto done;
789         }
790         
791         saf_store( cli->server_domain, cli->desthost );
792
793         if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
794                 goto done;
795         }
796
797         if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
798                 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
799                         global_myname(), ads->config.realm);
800                 goto done;
801         }
802         
803         d_printf("Disabled account for '%s' in realm '%s'\n", 
804                 global_myname(), ads->config.realm);
805                 
806         ret = 0;
807
808 done:
809         if ( cli ) 
810                 cli_shutdown(cli);
811
812         ads_destroy(&ads);
813         TALLOC_FREE( ctx );
814
815         return ret;
816 }
817
818 static int net_ads_join_ok(void)
819 {
820         ADS_STRUCT *ads = NULL;
821
822         if (!secrets_init()) {
823                 DEBUG(1,("Failed to initialise secrets database\n"));
824                 return -1;
825         }
826
827         net_use_machine_password();
828
829         if (!(ads = ads_startup(True))) {
830                 return -1;
831         }
832
833         ads_destroy(&ads);
834         return 0;
835 }
836
837 /*
838   check that an existing join is OK
839  */
840 int net_ads_testjoin(int argc, const char **argv)
841 {
842         use_in_memory_ccache();
843
844         /* Display success or failure */
845         if (net_ads_join_ok() != 0) {
846                 fprintf(stderr,"Join to domain is not valid\n");
847                 return -1;
848         }
849
850         printf("Join is OK\n");
851         return 0;
852 }
853
854 /*******************************************************************
855   Simple configu checks before beginning the join
856  ********************************************************************/
857
858 static int check_ads_config( void )
859 {
860         if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
861                 d_printf("Host is not configured as a member server.\n");
862                 return -1;
863         }
864
865         if (strlen(global_myname()) > 15) {
866                 d_printf("Our netbios name can be at most 15 chars long, "
867                          "\"%s\" is %u chars long\n",
868                          global_myname(), (unsigned int)strlen(global_myname()));
869                 return -1;
870         }
871
872         if ( lp_security() == SEC_ADS && !*lp_realm()) {
873                 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
874                         "join to succeed.\n");
875                 return -1;
876         }
877
878         if (!secrets_init()) {
879                 DEBUG(1,("Failed to initialise secrets database\n"));
880                 return -1;
881         }
882         
883         return 0;
884 }
885
886 /*******************************************************************
887  Do the domain join
888  ********************************************************************/
889
890 static int net_join_domain( TALLOC_CTX *ctx, const char *servername, 
891                             struct in_addr *ip, DOM_SID **dom_sid, const char *password )
892 {
893         int ret = -1;
894         struct cli_state *cli = NULL;
895
896         if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
897                 goto done;
898         
899         saf_store( cli->server_domain, cli->desthost );
900
901         if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
902                 goto done;
903
904         if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid, 
905                 password, ND_TYPE_AD )) )
906         {
907                 goto done;
908         }
909         
910         ret = 0;
911
912 done:
913         if ( cli ) 
914                 cli_shutdown(cli);
915
916         return ret;
917 }
918
919 /*******************************************************************
920  Set a machines dNSHostName and servicePrincipalName attributes
921  ********************************************************************/
922
923 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
924 {
925         ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
926         char *new_dn;
927         ADS_MODLIST mods;
928         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
929         char *psp;
930         fstring my_fqdn;
931         LDAPMessage *res = NULL;
932         char *dn_string = NULL;
933         const char *machine_name = global_myname();
934         int count;
935         
936         if ( !machine_name ) {
937                 return ADS_ERROR(LDAP_NO_MEMORY);
938         }
939         
940         /* Find our DN */
941         
942         status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
943         if (!ADS_ERR_OK(status)) 
944                 return status;
945                 
946         if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
947                 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
948                 return ADS_ERROR(LDAP_NO_MEMORY);       
949         }
950         
951         if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
952                 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
953                 goto done;
954         }
955         
956         new_dn = talloc_strdup(ctx, dn_string);
957         ads_memfree(ads_s, dn_string);
958         if (!new_dn) {
959                 return ADS_ERROR(LDAP_NO_MEMORY);
960         }
961
962         /* Windows only creates HOST/shortname & HOST/fqdn. */
963            
964         if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) ) 
965                 goto done;
966         strupper_m(psp);
967         servicePrincipalName[0] = psp;
968
969         name_to_fqdn(my_fqdn, machine_name);
970         strlower_m(my_fqdn);
971         if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) ) 
972                 goto done;
973         servicePrincipalName[1] = psp;
974         
975         if (!(mods = ads_init_mods(ctx))) {
976                 goto done;
977         }
978         
979         /* fields of primary importance */
980         
981         ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
982         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
983
984         status = ads_gen_mod(ads_s, new_dn, mods);
985
986 done:
987         ads_msgfree(ads_s, res);
988         
989         return status;
990 }
991
992 /*******************************************************************
993  Set a machines dNSHostName and servicePrincipalName attributes
994  ********************************************************************/
995
996 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
997 {
998         ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
999         char *new_dn;
1000         ADS_MODLIST mods;
1001         LDAPMessage *res = NULL;
1002         char *dn_string = NULL;
1003         const char *machine_name = global_myname();
1004         int count;
1005         
1006         if ( !machine_name ) {
1007                 return ADS_ERROR(LDAP_NO_MEMORY);
1008         }
1009         
1010         /* Find our DN */
1011         
1012         status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
1013         if (!ADS_ERR_OK(status)) 
1014                 return status;
1015                 
1016         if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1017                 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1018                 return ADS_ERROR(LDAP_NO_MEMORY);       
1019         }
1020         
1021         if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1022                 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1023                 goto done;
1024         }
1025         
1026         new_dn = talloc_strdup(ctx, dn_string);
1027         ads_memfree(ads_s, dn_string);
1028         if (!new_dn) {
1029                 return ADS_ERROR(LDAP_NO_MEMORY);
1030         }
1031         
1032         /* now do the mods */
1033         
1034         if (!(mods = ads_init_mods(ctx))) {
1035                 goto done;
1036         }
1037         
1038         /* fields of primary importance */
1039         
1040         ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1041
1042         status = ads_gen_mod(ads_s, new_dn, mods);
1043
1044 done:
1045         ads_msgfree(ads_s, res);
1046         
1047         return status;
1048 }
1049
1050 /*******************************************************************
1051   join a domain using ADS (LDAP mods)
1052  ********************************************************************/
1053
1054 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1055 {
1056         ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1057         char *dn, *ou_str;
1058         LDAPMessage *res = NULL;
1059
1060         ou_str = ads_ou_string(ads, ou);
1061         asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1062         free(ou_str);
1063
1064         rc = ads_search_dn(ads, &res, dn, NULL);
1065         ads_msgfree(ads, res);
1066
1067         if (ADS_ERR_OK(rc)) {
1068                 /* Attempt to create the machine account and bail if this fails.
1069                    Assume that the admin wants exactly what they requested */
1070
1071                 rc = ads_create_machine_acct( ads, global_myname(), dn );
1072                 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1073                         rc = ADS_SUCCESS;
1074                 }
1075         }
1076
1077         SAFE_FREE( dn );
1078
1079         return rc;
1080 }
1081
1082 /************************************************************************
1083  ************************************************************************/
1084
1085 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1086 {
1087         uint32 domain_func;
1088         ADS_STATUS status;
1089         fstring salt;
1090         char *std_salt;
1091         LDAPMessage *res = NULL;
1092         const char *machine_name = global_myname();
1093
1094         status = ads_domain_func_level( ads, &domain_func );
1095         if ( !ADS_ERR_OK(status) ) {
1096                 DEBUG(2,("Failed to determine domain functional level!\n"));
1097                 return False;
1098         }
1099
1100         /* go ahead and setup the default salt */
1101
1102         if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1103                 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1104                 return False;
1105         }
1106
1107         fstrcpy( salt, std_salt );
1108         SAFE_FREE( std_salt );
1109         
1110         /* if it's a Windows functional domain, we have to look for the UPN */
1111            
1112         if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) { 
1113                 char *upn;
1114                 int count;
1115                 
1116                 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1117                 if (!ADS_ERR_OK(status)) {
1118                         return False;
1119                 }
1120                 
1121                 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1122                         DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1123                         return False;
1124                 }
1125                 
1126                 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1127                 if ( upn ) {
1128                         fstrcpy( salt, upn );
1129                 }
1130                 
1131                 ads_msgfree(ads, res);
1132         }
1133
1134         return kerberos_secrets_store_des_salt( salt );
1135 }
1136
1137 /*********************************************************
1138  utility function to parse an integer parameter from 
1139  "parameter = value"
1140 **********************************************************/
1141 static char* get_string_param( const char* param )
1142 {
1143         char *p;
1144         
1145         if ( (p = strchr( param, '=' )) == NULL )
1146                 return NULL;
1147                 
1148         return (p+1);
1149 }
1150
1151 /*******************************************************************
1152  ********************************************************************/
1153  
1154 static int net_ads_join_usage(int argc, const char **argv)
1155 {
1156         d_printf("net ads join [options]\n");
1157         d_printf("Valid options:\n");
1158         d_printf("   createupn[=UPN]    Set the userPrincipalName attribute during the join.\n");
1159         d_printf("                      The deault UPN is in the form host/netbiosname@REALM.\n");
1160         d_printf("   createcomputer=OU  Precreate the computer account in a specific OU.\n");
1161         d_printf("                      The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1162         d_printf("                      E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1163
1164         return -1;
1165 }
1166
1167 /*******************************************************************
1168  ********************************************************************/
1169  
1170 int net_ads_join(int argc, const char **argv)
1171 {
1172         ADS_STRUCT *ads = NULL;
1173         ADS_STATUS status;
1174         char *machine_account = NULL;
1175         const char *short_domain_name = NULL;
1176         char *tmp_password, *password;
1177         struct cldap_netlogon_reply cldap_reply;
1178         TALLOC_CTX *ctx;
1179         DOM_SID *domain_sid = NULL;
1180         BOOL createupn = False;
1181         const char *machineupn = NULL;
1182         const char *create_in_ou = NULL;
1183         int i;
1184         
1185         if ( check_ads_config() != 0 ) {
1186                 d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
1187                 goto fail;
1188         }
1189
1190         if ( (ads = ads_startup(True)) == NULL ) {
1191                 goto fail;
1192         }
1193
1194         if (strcmp(ads->config.realm, lp_realm()) != 0) {
1195                 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1196                         "(%s) DO NOT match.  Aborting join\n", ads->config.realm, 
1197                         lp_realm());
1198                 goto fail;
1199         }
1200
1201         if (!(ctx = talloc_init("net_ads_join"))) {
1202                 d_fprintf(stderr, "Could not initialise talloc context.\n");
1203                 goto fail;
1204         }
1205
1206         /* process additional command line args */
1207         
1208         for ( i=0; i<argc; i++ ) {
1209                 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1210                         createupn = True;
1211                         machineupn = get_string_param(argv[i]);
1212                 }
1213                 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1214                         if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1215                                 d_fprintf(stderr, "Please supply a valid OU path\n");
1216                                 goto fail;
1217                         }               
1218                 }
1219                 else {
1220                         d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1221                         goto fail;
1222                 }
1223         }
1224
1225         /* If we were given an OU, try to create the machine in 
1226            the OU account first and then do the normal RPC join */
1227
1228         if  ( create_in_ou ) {
1229                 status = net_precreate_machine_acct( ads, create_in_ou );
1230                 if ( !ADS_ERR_OK(status) ) {
1231                         d_fprintf( stderr, "Failed to pre-create the machine object "
1232                                 "in OU %s.\n", argv[0]);
1233                         goto fail;
1234                 }
1235         }
1236
1237         /* Do the domain join here */
1238
1239         tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1240         password = talloc_strdup(ctx, tmp_password);
1241         
1242         if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1243                 /* There should be more detailed output here... */
1244                 d_fprintf(stderr, "call of net_join_domain failed\n");
1245                 goto fail;
1246         }
1247         
1248         /* Check the short name of the domain */
1249         
1250         ZERO_STRUCT( cldap_reply );
1251         
1252         if ( ads_cldap_netlogon( ads->config.ldap_server_name, 
1253                 ads->server.realm, &cldap_reply ) ) 
1254         {
1255                 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1256                 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1257                         d_printf("The workgroup in smb.conf does not match the short\n");
1258                         d_printf("domain name obtained from the server.\n");
1259                         d_printf("Using the name [%s] from the server.\n", short_domain_name);
1260                         d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1261                 }
1262         } else {
1263                 short_domain_name = lp_workgroup();
1264         }
1265         
1266         d_printf("Using short domain name -- %s\n", short_domain_name);
1267
1268         /*  HACK ALERT!  Store the sid and password under both the lp_workgroup() 
1269             value from smb.conf and the string returned from the server.  The former is
1270             neede to bootstrap winbindd's first connection to the DC to get the real 
1271             short domain name   --jerry */
1272            
1273         if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1274                 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1275         {
1276                 goto fail;
1277         }
1278
1279         /* Verify that everything is ok */
1280
1281         if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1282                 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1283                 goto fail;
1284         }       
1285
1286         /* create the dNSHostName & servicePrincipalName values */
1287         
1288         status = net_set_machine_spn( ctx, ads );
1289         if ( !ADS_ERR_OK(status) )  {
1290
1291                 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1292                 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1293                 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1294                 
1295                 /* Disable the machine account in AD.  Better to fail than to leave 
1296                    a confused admin.  */
1297                 
1298                 if ( net_ads_leave( 0, NULL ) != 0 ) {
1299                         d_fprintf( stderr, "Failed to disable machine account in AD.  Please do so manually.\n");
1300                 }
1301                 
1302                 /* clear out the machine password */
1303                 
1304                 netdom_store_machine_account( lp_workgroup(), domain_sid, "" ); 
1305                 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1306                 
1307                 goto fail;
1308         }
1309
1310         if ( !net_derive_salting_principal( ctx, ads ) ) {
1311                 DEBUG(1,("Failed to determine salting principal\n"));
1312                 goto fail;
1313         }
1314
1315         if ( createupn ) {
1316                 pstring upn;
1317                 
1318                 /* default to using the short UPN name */
1319                 if ( !machineupn ) {
1320                         snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(), 
1321                                 ads->config.realm );
1322                         machineupn = upn;
1323                 }
1324                 
1325                 status = net_set_machine_upn( ctx, ads, machineupn );
1326                 if ( !ADS_ERR_OK(status) )  {
1327                         d_fprintf(stderr, "Failed to set userPrincipalName.  Are you a Domain Admin?\n");
1328                 }
1329         }
1330
1331         /* Now build the keytab, using the same ADS connection */
1332         if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1333                 DEBUG(1,("Error creating host keytab!\n"));
1334         }
1335
1336         d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1337
1338         SAFE_FREE(machine_account);
1339         TALLOC_FREE( ctx );
1340         ads_destroy(&ads);
1341         
1342         return 0;
1343
1344 fail:
1345         /* issue an overall failure message at the end. */
1346         d_printf("Failed to join domain!\n");
1347         ads_destroy(&ads);
1348         return -1;
1349 }
1350
1351 /*******************************************************************
1352  ********************************************************************/
1353
1354 int net_ads_printer_usage(int argc, const char **argv)
1355 {
1356         d_printf(
1357 "\nnet ads printer search <printer>"
1358 "\n\tsearch for a printer in the directory\n"
1359 "\nnet ads printer info <printer> <server>"
1360 "\n\tlookup info in directory for printer on server"
1361 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1362 "\nnet ads printer publish <printername>"
1363 "\n\tpublish printer in directory"
1364 "\n\t(note: printer name is required)\n"
1365 "\nnet ads printer remove <printername>"
1366 "\n\tremove printer from directory"
1367 "\n\t(note: printer name is required)\n");
1368         return -1;
1369 }
1370
1371 /*******************************************************************
1372  ********************************************************************/
1373
1374 static int net_ads_printer_search(int argc, const char **argv)
1375 {
1376         ADS_STRUCT *ads;
1377         ADS_STATUS rc;
1378         void *res = NULL;
1379
1380         if (!(ads = ads_startup(False))) {
1381                 return -1;
1382         }
1383
1384         rc = ads_find_printers(ads, &res);
1385
1386         if (!ADS_ERR_OK(rc)) {
1387                 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1388                 ads_msgfree(ads, res);
1389                 ads_destroy(&ads);
1390                 return -1;
1391         }
1392
1393         if (ads_count_replies(ads, res) == 0) {
1394                 d_fprintf(stderr, "No results found\n");
1395                 ads_msgfree(ads, res);
1396                 ads_destroy(&ads);
1397                 return -1;
1398         }
1399
1400         ads_dump(ads, res);
1401         ads_msgfree(ads, res);
1402         ads_destroy(&ads);
1403         return 0;
1404 }
1405
1406 static int net_ads_printer_info(int argc, const char **argv)
1407 {
1408         ADS_STRUCT *ads;
1409         ADS_STATUS rc;
1410         const char *servername, *printername;
1411         void *res = NULL;
1412
1413         if (!(ads = ads_startup(False))) {
1414                 return -1;
1415         }
1416
1417         if (argc > 0) {
1418                 printername = argv[0];
1419         } else {
1420                 printername = "*";
1421         }
1422
1423         if (argc > 1) {
1424                 servername =  argv[1];
1425         } else {
1426                 servername = global_myname();
1427         }
1428
1429         rc = ads_find_printer_on_server(ads, &res, printername, servername);
1430
1431         if (!ADS_ERR_OK(rc)) {
1432                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1433                 ads_msgfree(ads, res);
1434                 ads_destroy(&ads);
1435                 return -1;
1436         }
1437
1438         if (ads_count_replies(ads, res) == 0) {
1439                 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1440                 ads_msgfree(ads, res);
1441                 ads_destroy(&ads);
1442                 return -1;
1443         }
1444
1445         ads_dump(ads, res);
1446         ads_msgfree(ads, res);
1447         ads_destroy(&ads);
1448
1449         return 0;
1450 }
1451
1452 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1453                             void *buf, size_t len)
1454 {
1455         return;
1456 }
1457
1458 static int net_ads_printer_publish(int argc, const char **argv)
1459 {
1460         ADS_STRUCT *ads;
1461         ADS_STATUS rc;
1462         const char *servername, *printername;
1463         struct cli_state *cli;
1464         struct rpc_pipe_client *pipe_hnd;
1465         struct in_addr          server_ip;
1466         NTSTATUS nt_status;
1467         TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1468         ADS_MODLIST mods = ads_init_mods(mem_ctx);
1469         char *prt_dn, *srv_dn, **srv_cn;
1470         void *res = NULL;
1471
1472         if (!(ads = ads_startup(True))) {
1473                 return -1;
1474         }
1475
1476         if (argc < 1) {
1477                 return net_ads_printer_usage(argc, argv);
1478         }
1479         
1480         printername = argv[0];
1481
1482         if (argc == 2) {
1483                 servername = argv[1];
1484         } else {
1485                 servername = global_myname();
1486         }
1487                 
1488         /* Get printer data from SPOOLSS */
1489
1490         resolve_name(servername, &server_ip, 0x20);
1491
1492         nt_status = cli_full_connection(&cli, global_myname(), servername, 
1493                                         &server_ip, 0,
1494                                         "IPC$", "IPC",  
1495                                         opt_user_name, opt_workgroup,
1496                                         opt_password ? opt_password : "", 
1497                                         CLI_FULL_CONNECTION_USE_KERBEROS, 
1498                                         Undefined, NULL);
1499
1500         if (NT_STATUS_IS_ERR(nt_status)) {
1501                 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1502                          "for %s\n", servername, printername);
1503                 ads_destroy(&ads);
1504                 return -1;
1505         }
1506
1507         /* Publish on AD server */
1508
1509         ads_find_machine_acct(ads, &res, servername);
1510
1511         if (ads_count_replies(ads, res) == 0) {
1512                 d_fprintf(stderr, "Could not find machine account for server %s\n", 
1513                          servername);
1514                 ads_destroy(&ads);
1515                 return -1;
1516         }
1517
1518         srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1519         srv_cn = ldap_explode_dn(srv_dn, 1);
1520
1521         asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1522
1523         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1524         if (!pipe_hnd) {
1525                 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1526                          servername);
1527                 ads_destroy(&ads);
1528                 return -1;
1529         }
1530
1531         get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1532                                            printername);
1533
1534         rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1535         if (!ADS_ERR_OK(rc)) {
1536                 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1537                 ads_destroy(&ads);
1538                 return -1;
1539         }
1540  
1541         d_printf("published printer\n");
1542         ads_destroy(&ads);
1543  
1544         return 0;
1545 }
1546
1547 static int net_ads_printer_remove(int argc, const char **argv)
1548 {
1549         ADS_STRUCT *ads;
1550         ADS_STATUS rc;
1551         const char *servername;
1552         char *prt_dn;
1553         void *res = NULL;
1554
1555         if (!(ads = ads_startup(True))) {
1556                 return -1;
1557         }
1558
1559         if (argc < 1) {
1560                 return net_ads_printer_usage(argc, argv);
1561         }
1562
1563         if (argc > 1) {
1564                 servername = argv[1];
1565         } else {
1566                 servername = global_myname();
1567         }
1568
1569         rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1570
1571         if (!ADS_ERR_OK(rc)) {
1572                 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1573                 ads_msgfree(ads, res);
1574                 ads_destroy(&ads);
1575                 return -1;
1576         }
1577
1578         if (ads_count_replies(ads, res) == 0) {
1579                 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1580                 ads_msgfree(ads, res);
1581                 ads_destroy(&ads);
1582                 return -1;
1583         }
1584
1585         prt_dn = ads_get_dn(ads, res);
1586         ads_msgfree(ads, res);
1587         rc = ads_del_dn(ads, prt_dn);
1588         ads_memfree(ads, prt_dn);
1589
1590         if (!ADS_ERR_OK(rc)) {
1591                 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1592                 ads_destroy(&ads);
1593                 return -1;
1594         }
1595
1596         ads_destroy(&ads);
1597         return 0;
1598 }
1599
1600 static int net_ads_printer(int argc, const char **argv)
1601 {
1602         struct functable func[] = {
1603                 {"SEARCH", net_ads_printer_search},
1604                 {"INFO", net_ads_printer_info},
1605                 {"PUBLISH", net_ads_printer_publish},
1606                 {"REMOVE", net_ads_printer_remove},
1607                 {NULL, NULL}
1608         };
1609         
1610         return net_run_function(argc, argv, func, net_ads_printer_usage);
1611 }
1612
1613
1614 static int net_ads_password(int argc, const char **argv)
1615 {
1616         ADS_STRUCT *ads;
1617         const char *auth_principal = opt_user_name;
1618         const char *auth_password = opt_password;
1619         char *realm = NULL;
1620         char *new_password = NULL;
1621         char *c, *prompt;
1622         const char *user;
1623         ADS_STATUS ret;
1624
1625         if (opt_user_name == NULL || opt_password == NULL) {
1626                 d_fprintf(stderr, "You must supply an administrator username/password\n");
1627                 return -1;
1628         }
1629
1630         if (argc < 1) {
1631                 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1632                 return -1;
1633         }
1634
1635         user = argv[0];
1636         if (!strchr_m(user, '@')) {
1637                 asprintf(&c, "%s@%s", argv[0], lp_realm());
1638                 user = c;
1639         }
1640
1641         use_in_memory_ccache();    
1642         c = strchr_m(auth_principal, '@');
1643         if (c) {
1644                 realm = ++c;
1645         } else {
1646                 realm = lp_realm();
1647         }
1648
1649         /* use the realm so we can eventually change passwords for users 
1650         in realms other than default */
1651         if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
1652                 return -1;
1653         }
1654
1655         /* we don't actually need a full connect, but it's the easy way to
1656                 fill in the KDC's addresss */
1657         ads_connect(ads);
1658     
1659         if (!ads || !ads->config.realm) {
1660                 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1661                 return -1;
1662         }
1663
1664         if (argv[1]) {
1665                 new_password = (char *)argv[1];
1666         } else {
1667                 asprintf(&prompt, "Enter new password for %s:", user);
1668                 new_password = getpass(prompt);
1669                 free(prompt);
1670         }
1671
1672         ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
1673                                 auth_password, user, new_password, ads->auth.time_offset);
1674         if (!ADS_ERR_OK(ret)) {
1675                 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1676                 ads_destroy(&ads);
1677                 return -1;
1678         }
1679
1680         d_printf("Password change for %s completed.\n", user);
1681         ads_destroy(&ads);
1682
1683         return 0;
1684 }
1685
1686 int net_ads_changetrustpw(int argc, const char **argv)
1687 {    
1688         ADS_STRUCT *ads;
1689         char *host_principal;
1690         fstring my_name;
1691         ADS_STATUS ret;
1692
1693         if (!secrets_init()) {
1694                 DEBUG(1,("Failed to initialise secrets database\n"));
1695                 return -1;
1696         }
1697
1698         net_use_machine_password();
1699
1700         use_in_memory_ccache();
1701
1702         if (!(ads = ads_startup(True))) {
1703                 return -1;
1704         }
1705
1706         fstrcpy(my_name, global_myname());
1707         strlower_m(my_name);
1708         asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1709         d_printf("Changing password for principal: %s\n", host_principal);
1710
1711         ret = ads_change_trust_account_password(ads, host_principal);
1712
1713         if (!ADS_ERR_OK(ret)) {
1714                 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1715                 ads_destroy(&ads);
1716                 SAFE_FREE(host_principal);
1717                 return -1;
1718         }
1719     
1720         d_printf("Password change for principal %s succeeded.\n", host_principal);
1721
1722         if (lp_use_kerberos_keytab()) {
1723                 d_printf("Attempting to update system keytab with new password.\n");
1724                 if (ads_keytab_create_default(ads)) {
1725                         d_printf("Failed to update system keytab.\n");
1726                 }
1727         }
1728
1729         ads_destroy(&ads);
1730         SAFE_FREE(host_principal);
1731
1732         return 0;
1733 }
1734
1735 /*
1736   help for net ads search
1737 */
1738 static int net_ads_search_usage(int argc, const char **argv)
1739 {
1740         d_printf(
1741                 "\nnet ads search <expression> <attributes...>\n"\
1742                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1743                 "The expression is a standard LDAP search expression, and the\n"\
1744                 "attributes are a list of LDAP fields to show in the results\n\n"\
1745                 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1746                 );
1747         net_common_flags_usage(argc, argv);
1748         return -1;
1749 }
1750
1751
1752 /*
1753   general ADS search function. Useful in diagnosing problems in ADS
1754 */
1755 static int net_ads_search(int argc, const char **argv)
1756 {
1757         ADS_STRUCT *ads;
1758         ADS_STATUS rc;
1759         const char *ldap_exp;
1760         const char **attrs;
1761         void *res = NULL;
1762
1763         if (argc < 1) {
1764                 return net_ads_search_usage(argc, argv);
1765         }
1766
1767         if (!(ads = ads_startup(False))) {
1768                 return -1;
1769         }
1770
1771         ldap_exp = argv[0];
1772         attrs = (argv + 1);
1773
1774         rc = ads_do_search_all(ads, ads->config.bind_path,
1775                                LDAP_SCOPE_SUBTREE,
1776                                ldap_exp, attrs, &res);
1777         if (!ADS_ERR_OK(rc)) {
1778                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1779                 ads_destroy(&ads);
1780                 return -1;
1781         }       
1782
1783         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1784
1785         /* dump the results */
1786         ads_dump(ads, res);
1787
1788         ads_msgfree(ads, res);
1789         ads_destroy(&ads);
1790
1791         return 0;
1792 }
1793
1794
1795 /*
1796   help for net ads search
1797 */
1798 static int net_ads_dn_usage(int argc, const char **argv)
1799 {
1800         d_printf(
1801                 "\nnet ads dn <dn> <attributes...>\n"\
1802                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1803                 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1804                 "to show in the results\n\n"\
1805                 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1806                 );
1807         net_common_flags_usage(argc, argv);
1808         return -1;
1809 }
1810
1811
1812 /*
1813   general ADS search function. Useful in diagnosing problems in ADS
1814 */
1815 static int net_ads_dn(int argc, const char **argv)
1816 {
1817         ADS_STRUCT *ads;
1818         ADS_STATUS rc;
1819         const char *dn;
1820         const char **attrs;
1821         void *res = NULL;
1822
1823         if (argc < 1) {
1824                 return net_ads_dn_usage(argc, argv);
1825         }
1826
1827         if (!(ads = ads_startup(False))) {
1828                 return -1;
1829         }
1830
1831         dn = argv[0];
1832         attrs = (argv + 1);
1833
1834         rc = ads_do_search_all(ads, dn, 
1835                                LDAP_SCOPE_BASE,
1836                                "(objectclass=*)", attrs, &res);
1837         if (!ADS_ERR_OK(rc)) {
1838                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1839                 ads_destroy(&ads);
1840                 return -1;
1841         }       
1842
1843         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1844
1845         /* dump the results */
1846         ads_dump(ads, res);
1847
1848         ads_msgfree(ads, res);
1849         ads_destroy(&ads);
1850
1851         return 0;
1852 }
1853
1854 /*
1855   help for net ads sid search
1856 */
1857 static int net_ads_sid_usage(int argc, const char **argv)
1858 {
1859         d_printf(
1860                 "\nnet ads sid <sid> <attributes...>\n"\
1861                 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1862                 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1863                 "to show in the results\n\n"\
1864                 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1865                 );
1866         net_common_flags_usage(argc, argv);
1867         return -1;
1868 }
1869
1870
1871 /*
1872   general ADS search function. Useful in diagnosing problems in ADS
1873 */
1874 static int net_ads_sid(int argc, const char **argv)
1875 {
1876         ADS_STRUCT *ads;
1877         ADS_STATUS rc;
1878         const char *sid_string;
1879         const char **attrs;
1880         void *res = NULL;
1881         DOM_SID sid;
1882
1883         if (argc < 1) {
1884                 return net_ads_sid_usage(argc, argv);
1885         }
1886
1887         if (!(ads = ads_startup(False))) {
1888                 return -1;
1889         }
1890
1891         sid_string = argv[0];
1892         attrs = (argv + 1);
1893
1894         if (!string_to_sid(&sid, sid_string)) {
1895                 d_fprintf(stderr, "could not convert sid\n");
1896                 ads_destroy(&ads);
1897                 return -1;
1898         }
1899
1900         rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1901         if (!ADS_ERR_OK(rc)) {
1902                 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1903                 ads_destroy(&ads);
1904                 return -1;
1905         }       
1906
1907         d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1908
1909         /* dump the results */
1910         ads_dump(ads, res);
1911
1912         ads_msgfree(ads, res);
1913         ads_destroy(&ads);
1914
1915         return 0;
1916 }
1917
1918
1919 static int net_ads_keytab_usage(int argc, const char **argv)
1920 {
1921         d_printf(
1922                 "net ads keytab <COMMAND>\n"\
1923 "<COMMAND> can be either:\n"\
1924 "  CREATE    Creates a fresh keytab\n"\
1925 "  ADD       Adds new service principal\n"\
1926 "  FLUSH     Flushes out all keytab entries\n"\
1927 "  HELP      Prints this help message\n"\
1928 "The ADD command will take arguments, the other commands\n"\
1929 "will not take any arguments.   The arguments given to ADD\n"\
1930 "should be a list of principals to add.  For example, \n"\
1931 "   net ads keytab add srv1 srv2\n"\
1932 "will add principals for the services srv1 and srv2 to the\n"\
1933 "system's keytab.\n"\
1934 "\n"
1935                 );
1936         return -1;
1937 }
1938
1939 static int net_ads_keytab_flush(int argc, const char **argv)
1940 {
1941         int ret;
1942         ADS_STRUCT *ads;
1943
1944         if (!(ads = ads_startup(True))) {
1945                 return -1;
1946         }
1947         ret = ads_keytab_flush(ads);
1948         ads_destroy(&ads);
1949         return ret;
1950 }
1951
1952 static int net_ads_keytab_add(int argc, const char **argv)
1953 {
1954         int i;
1955         int ret = 0;
1956         ADS_STRUCT *ads;
1957
1958         d_printf("Processing principals to add...\n");
1959         if (!(ads = ads_startup(True))) {
1960                 return -1;
1961         }
1962         for (i = 0; i < argc; i++) {
1963                 ret |= ads_keytab_add_entry(ads, argv[i]);
1964         }
1965         ads_destroy(&ads);
1966         return ret;
1967 }
1968
1969 static int net_ads_keytab_create(int argc, const char **argv)
1970 {
1971         ADS_STRUCT *ads;
1972         int ret;
1973
1974         if (!(ads = ads_startup(True))) {
1975                 return -1;
1976         }
1977         ret = ads_keytab_create_default(ads);
1978         ads_destroy(&ads);
1979         return ret;
1980 }
1981
1982 int net_ads_keytab(int argc, const char **argv)
1983 {
1984         struct functable func[] = {
1985                 {"CREATE", net_ads_keytab_create},
1986                 {"ADD", net_ads_keytab_add},
1987                 {"FLUSH", net_ads_keytab_flush},
1988                 {"HELP", net_ads_keytab_usage},
1989                 {NULL, NULL}
1990         };
1991
1992         if (!lp_use_kerberos_keytab()) {
1993                 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1994 use keytab functions.\n");
1995         }
1996
1997         return net_run_function(argc, argv, func, net_ads_keytab_usage);
1998 }
1999
2000 int net_ads_help(int argc, const char **argv)
2001 {
2002         struct functable func[] = {
2003                 {"USER", net_ads_user_usage},
2004                 {"GROUP", net_ads_group_usage},
2005                 {"PRINTER", net_ads_printer_usage},
2006                 {"SEARCH", net_ads_search_usage},
2007                 {"INFO", net_ads_info},
2008                 {"JOIN", net_ads_join_usage},
2009                 {"LEAVE", net_ads_leave},
2010                 {"STATUS", net_ads_status},
2011                 {"PASSWORD", net_ads_password},
2012                 {"CHANGETRUSTPW", net_ads_changetrustpw},
2013                 {NULL, NULL}
2014         };
2015
2016         return net_run_function(argc, argv, func, net_ads_usage);
2017 }
2018
2019 int net_ads(int argc, const char **argv)
2020 {
2021         struct functable func[] = {
2022                 {"INFO", net_ads_info},
2023                 {"JOIN", net_ads_join},
2024                 {"TESTJOIN", net_ads_testjoin},
2025                 {"LEAVE", net_ads_leave},
2026                 {"STATUS", net_ads_status},
2027                 {"USER", net_ads_user},
2028                 {"GROUP", net_ads_group},
2029                 {"PASSWORD", net_ads_password},
2030                 {"CHANGETRUSTPW", net_ads_changetrustpw},
2031                 {"PRINTER", net_ads_printer},
2032                 {"SEARCH", net_ads_search},
2033                 {"DN", net_ads_dn},
2034                 {"SID", net_ads_sid},
2035                 {"WORKGROUP", net_ads_workgroup},
2036                 {"LOOKUP", net_ads_lookup},
2037                 {"KEYTAB", net_ads_keytab},
2038                 {"HELP", net_ads_help},
2039                 {NULL, NULL}
2040         };
2041         
2042         return net_run_function(argc, argv, func, net_ads_usage);
2043 }
2044
2045 #else
2046
2047 static int net_ads_noads(void)
2048 {
2049         d_fprintf(stderr, "ADS support not compiled in\n");
2050         return -1;
2051 }
2052
2053 int net_ads_keytab(int argc, const char **argv)
2054 {
2055         return net_ads_noads();
2056 }
2057
2058 int net_ads_usage(int argc, const char **argv)
2059 {
2060         return net_ads_noads();
2061 }
2062
2063 int net_ads_help(int argc, const char **argv)
2064 {
2065         return net_ads_noads();
2066 }
2067
2068 int net_ads_changetrustpw(int argc, const char **argv)
2069 {
2070         return net_ads_noads();
2071 }
2072
2073 int net_ads_join(int argc, const char **argv)
2074 {
2075         return net_ads_noads();
2076 }
2077
2078 int net_ads_user(int argc, const char **argv)
2079 {
2080         return net_ads_noads();
2081 }
2082
2083 int net_ads_group(int argc, const char **argv)
2084 {
2085         return net_ads_noads();
2086 }
2087
2088 /* this one shouldn't display a message */
2089 int net_ads_check(void)
2090 {
2091         return -1;
2092 }
2093
2094 int net_ads_check_our_domain(void)
2095 {
2096         return -1;
2097 }
2098
2099 int net_ads(int argc, const char **argv)
2100 {
2101         return net_ads_usage(argc, argv);
2102 }
2103
2104 #endif  /* WITH_ADS */