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