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