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