forked from msktutil/msktutil
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmsktutil.cpp
1482 lines (1335 loc) · 52.2 KB
/
msktutil.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
*----------------------------------------------------------------------------
*
* msktutil.cpp
*
* (C) 2004-2006 Dan Perry (dperry@pppl.gov)
* (C) 2006 Brian Elliott Finley (finley@anl.gov)
* (C) 2009-2010 Doug Engert (deengert@anl.gov)
* (C) 2010 James Y Knight (foom@fuhm.net)
* (C) 2010-2013 Ken Dreyer <ktdreyer at ktdreyer.com>
* (C) 2012-2017 Mark Proehl <mark at mproehl.net>
* (C) 2012-2017 Olaf Flebbe <of at oflebbe.de>
* (C) 2013-2017 Daniel Kobras <d.kobras at science-computing.de>
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*-----------------------------------------------------------------------------
*/
#include "msktutil.h"
#ifndef HAVE_STRTOLL
#include "strtoll.h"
#endif
#include <cctype>
#include <memory>
#include <algorithm>
/* GLOBALS */
int g_verbose = 2;
/* Fatal error */
void error_exit( const char *text) {
v_error_exit("error_exit: %s: %s\n", text, strerror(errno));
}
void v_error_exit(const char* format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
std::string sform(const char* format, ...)
{
va_list args;
va_start(args, format);
char *buf;
#if !defined(HAVE_VASPRINTF)
# ifdef HAVE_VSNPRINTF
buf = (char *) malloc(10000);
memset(buf, 0, 10000);
int result = vsnprintf(buf, 10000-1, format, args);
# else
# error need either vasprintf or vsnprintf
# endif
#else
int result = vasprintf(&buf, format, args);
#endif
if (result < 0) {
error_exit("vasprintf failed");
}
std::string outstr(buf, result);
free(buf);
va_end(args);
return outstr;
}
void remove_files_at_exit()
{
remove_fake_krb5_conf();
remove_ccache();
}
void catch_int(int)
{
remove_files_at_exit();
exit(1);
}
void set_supportedEncryptionTypes(msktutil_flags *flags, char * value)
{
flags->enctypes = VALUE_ON;
flags->supportedEncryptionTypes = strtol(value, NULL, 0);
}
/* Parse string representation of enctype into numeric krb5 enctype
* (not to be mistaken with the numeric AD enctype!)
*/
int parse_enctype(const std::string &value)
{
int enctype;
if ("des-cbc-crc" == value)
enctype = 1;
else if ("des-cbc-md5" == value)
enctype = 3;
else if ("arcfour-hmac-md5" == value ||
"arcfour-hmac" == value ||
"arcfour" == value ||
"rc4-hmac-md5" == value ||
"rc4-hmac" == value ||
"rc4" == value)
enctype = 23;
else if ("aes128-cts-hmac-sha1-96" == value ||
"aes128-cts-hmac-sha1" == value ||
"aes128-cts-hmac" == value ||
"aes128-cts" == value ||
"aes128" == value)
enctype = 17;
else if ("aes256-cts-hmac-sha1-96" == value ||
"aes256-cts-hmac-sha1" == value ||
"aes256-cts-hmac" == value ||
"aes256-cts" == value ||
"aes256" == value)
enctype = 18;
else {
fprintf(stderr,
"Error: enctype = %s not supported. "
"Supported enctype strings are\n", value.c_str()
);
fprintf(stderr, " des-cbc-crc\n");
fprintf(stderr, " des-cbc-md5\n");
fprintf(stderr, " arcfour\n");
fprintf(stderr, " aes128\n");
fprintf(stderr, " aes256\n");
exit(1);
}
return enctype;
}
void do_verbose()
{
g_verbose++; /* allow for ldap debuging */
}
void qualify_principal_vec(std::vector<std::string> &principals,
const std::string &hostname)
{
for(size_t i = 0; i < principals.size(); ++i) {
/* If no hostname part, add it: */
if (principals[i].find('/') == std::string::npos) {
if (hostname.empty()) {
fprintf(stderr,
"Error: default hostname unspecified, "
"and service argument missing hostname.\n"
);
exit(1);
}
principals[i].append("/").append(hostname);
}
}
}
int finalize_exec(msktutil_exec *exec, msktutil_flags *flags)
{
int ret;
char *temp_realm;
if (flags->realm_name.empty()) {
if (krb5_get_default_realm(g_context, &temp_realm)) {
fprintf(stderr, "Error: krb5_get_default_realm failed\n");
exit(1);
}
flags->realm_name = std::string(temp_realm);
#ifdef HEIMDAL
krb5_xfree(temp_realm);
#else
krb5_free_default_realm(g_context, temp_realm);
#endif
}
flags->lower_realm_name = flags->realm_name;
for(std::string::iterator it = flags->lower_realm_name.begin();
it != flags->lower_realm_name.end(); ++it) {
*it = std::tolower(*it);
}
if (exec->mode == MODE_CLEANUP) {
VERBOSE("cleanup mode: don't need AD server");
flags->server = "dummy";
} else {
if (flags->server.empty()) {
flags->server = get_dc_host(flags->realm_name,
flags->site,
flags->no_reverse_lookups);
if (flags->server.empty()) {
fprintf(stderr, "Error: get_dc_host failed\n");
exit(1);
}
}
}
get_default_keytab(flags);
signal(SIGINT, catch_int);
atexit(remove_files_at_exit);
create_fake_krb5_conf(flags);
if (exec->mode == MODE_CLEANUP) {
VERBOSE("cleanup mode: nothing more to do");
return (0);
}
if (exec->mode == MODE_PRECREATE && flags->hostname.empty()) {
/* Don't set a default hostname if none provided in pre-create
* mode. */
if (flags->sAMAccountName.empty()) {
fprintf(stderr,
"Error: You must supply either --computer-name "
"or --hostname when using pre-create mode.\n"
);
exit(1);
}
} else if (flags->hostname.empty()) {
/* Canonicalize the hostname if need be */
flags->hostname = get_default_hostname(flags->no_canonical_name);
} else {
flags->hostname = complete_hostname(flags->hostname);
}
/* Determine the sAMAccountName, if not set */
if (flags->sAMAccountName.empty()) {
if (flags->use_service_account) {
fprintf(stderr,
"Error: You must supply --account-name "
"when using --use-service-account.\n"
);
exit(1);
} else {
flags->sAMAccountName = get_default_samaccountname(flags) + "$";
}
}
/* Determine sAMAccountName_nodollar */
flags->sAMAccountName_nodollar = flags->sAMAccountName;
if (flags->sAMAccountName_nodollar[
flags->sAMAccountName_nodollar.size()-1] == '$') {
flags->sAMAccountName_nodollar.erase(
flags->sAMAccountName_nodollar.size()-1);
}
/* Add a "$" to machine accounts */
if ((!flags->use_service_account)
&& (flags->sAMAccountName[flags->sAMAccountName.size()-1] != '$')) {
flags->sAMAccountName += "$";
}
/* Determine uppercase version of sAMAccountName */
flags->sAMAccountName_uppercase = flags->sAMAccountName;
for (std::string::size_type i=0;
i<flags->sAMAccountName_uppercase.length();
++i) {
flags->sAMAccountName_uppercase[i]
= toupper(flags->sAMAccountName_uppercase[i]);
}
/* The sAMAccountName will cause win 9x, NT problems if longer
* than MAX_SAM_ACCOUNT_LEN characters */
if (flags->sAMAccountName.length() > MAX_SAM_ACCOUNT_LEN) {
fprintf(stderr,
"Error: The SAM name (%s) for this host is longer "
"than the maximum of MAX_SAM_ACCOUNT_LEN characters\n",
flags->sAMAccountName.c_str()
);
fprintf(stderr,
"Error: You can specify a shorter name using "
"--computer-name\n"
);
exit(1);
}
VERBOSE("SAM Account Name is: %s", flags->sAMAccountName.c_str());
/* Qualify entries in the principals list */
qualify_principal_vec(exec->add_principals, flags->hostname);
qualify_principal_vec(exec->remove_principals, flags->hostname);
/* Now, try to get kerberos credentials in order to connect to
* LDAP. */
flags->auth_type = find_working_creds(flags);
if (flags->auth_type == AUTH_NONE) {
fprintf(stderr,
"Error: could not find any credentials to authenticate with. "
"Neither keytab,\n"
"default machine password, nor calling user's tickets worked. "
"Try\n\"kinit\"ing yourself some tickets with permission to "
"create computer\nobjects, or pre-creating the computer "
"object in AD and selecting\n'reset account'.\n"
);
exit(1);
}
/* If we didn't get kerberos credentials because the old passord
* has expired we need to change it now */
if (flags->auth_type == AUTH_FROM_SUPPLIED_EXPIRED_PASSWORD) {
VERBOSE("Account password expired, changing it now...");
ret = set_password(flags);
if (ret) {
fprintf(stderr, "Error: failed to change password\n");
exit(1);
}
if (!get_creds(flags)) {
fprintf(stderr, "Error: failed to get kerberos credentials\n");
exit(1);
}
}
VERBOSE("Authenticated using method %d", flags->auth_type);
flags->ldap = new LDAPConnection(flags->server,
flags->sasl_mechanisms,
flags->no_reverse_lookups);
if (!flags->ldap->is_connected()) {
fprintf(stderr, "Error: ldap_connect failed\n");
/* Print a hint as to the likely cause: */
if (flags->auth_type == AUTH_FROM_USER_CREDS) {
fprintf(stderr, "--> Is your kerberos ticket expired? "
"You might try re-\"kinit\"ing.\n"
);
}
if (flags->no_reverse_lookups == false) {
fprintf(stderr, "--> Is DNS configured correctly? ");
fprintf(stderr, "You might try options \"--server\" "
"and \"--no-reverse-lookups\".\n"
);
}
exit(1);
}
ldap_get_base_dn(flags);
get_default_ou(flags);
return 0;
}
int add_and_remove_principals(msktutil_exec *exec)
{
int ret = 0;
std::vector<std::string> &cur_princs(Globals::flags()->ad_principals);
for (size_t i = 0; i < exec->add_principals.size(); ++i) {
std::string principal = exec->add_principals[i];
if (std::find(cur_princs.begin(),
cur_princs.end(),
principal) == cur_princs.end()) {
/* Not already in the list, so add it. */
int loc_ret = ldap_add_principal(principal, Globals::flags());
if (loc_ret) {
fprintf(stderr, "Error: ldap_add_principal failed\n");
ret = 1;
continue;
}
}
}
for (size_t i = 0; i < exec->remove_principals.size(); ++i) {
std::string principal = exec->remove_principals[i];
if (std::find(cur_princs.begin(), cur_princs.end(), principal)
!= cur_princs.end()) {
int loc_ret = ldap_remove_principal(principal, Globals::flags());
if (loc_ret) {
fprintf(stderr, "Error: ldap_remove_principal failed\n");
ret = 1;
continue;
}
} else {
fprintf(stderr,
"Error: principal %s cannot be removed, was not in "
"servicePrincipalName.\n",
principal.c_str()
);
for (size_t i = 0; i < cur_princs.size(); ++i)
fprintf(stderr, " %s\n", cur_princs[i].c_str());
ret = 1;
}
}
return ret;
}
void do_help()
{
fprintf(stdout, "Usage: %s [MODE] [OPTIONS]\n", PACKAGE_NAME);
fprintf(stdout, "\n");
fprintf(stdout, "Modes: \n");
fprintf(stdout, "\n");
fprintf(stdout, " create Creates a keytab for the current host or a given service account.\n");
fprintf(stdout, " (same as update -s host).\n");
fprintf(stdout, "\n");
fprintf(stdout, " update Updates the keytab for the current host or service account. This\n");
fprintf(stdout, " changes the account's password and updates the keytab with entries\n");
fprintf(stdout, " for all principals in servicePrincipalName and userPrincipalName.\n");
fprintf(stdout, " It also updates LDAP attributes for msDS-supportedEncryptionTypes,\n");
fprintf(stdout, " dNSHostName, and applies other options you specify.\n");
fprintf(stdout, "\n");
fprintf(stdout, " auto-update Same as update, but only if keytab fails to authenticate, or\n");
fprintf(stdout, " the last password change was more than 30 days ago\n");
fprintf(stdout, " (see --auto-update-interval). Useful to run from a daily cron job.\n");
fprintf(stdout, "\n");
fprintf(stdout, " pre-create Pre-create an account for the given host with default password\n");
fprintf(stdout, " but do not update local keytab.\n");
fprintf(stdout, " Requires -h or --computer-name argument.\n");
fprintf(stdout, " Implies --user-creds-only.\n");
fprintf(stdout, "\n");
fprintf(stdout, " flush Flushes all principals for the current host or service account\n");
fprintf(stdout, " from the keytab, and deletes servicePrincipalName from AD.\n");
fprintf(stdout, "\n");
fprintf(stdout, " delete Deletes the host or service account from Active Directory.\n");
fprintf(stdout, "\n");
fprintf(stdout, " cleanup Deletes entries from the keytab that are no longer needed.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Common options: \n");
fprintf(stdout, " --help Displays this message\n");
fprintf(stdout, " -v, --version Display the current version\n");
fprintf(stdout, " --verbose Enable verbose messages.\n");
fprintf(stdout, " More then once to get LDAP debugging.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Connection/setup options: \n");
fprintf(stdout, " -b, --base <base ou> Sets the LDAP base OU to use when creating an account.\n");
fprintf(stdout, " The default is read from AD (often CN=computers).\n");
fprintf(stdout, " --computer-name <name>, --account-name <name>\n");
fprintf(stdout, " Sets the computer account name or service account name\n");
fprintf(stdout, " to <name>.\n");
fprintf(stdout, " --old-account-password <password>\n");
fprintf(stdout, " Use supplied computer account password or service\n");
fprintf(stdout, " account password for authentication.\n");
fprintf(stdout, " This option is mutually exclusive with --user-creds-only.\n");
fprintf(stdout, " -h, --hostname <name> Use <name> as current hostname.\n");
fprintf(stdout, " --password <new_password>\n");
fprintf(stdout, " Specify the new account password instead of generating\n");
fprintf(stdout, " a random one. Consider the password policy settings when\n");
fprintf(stdout, " defining the string.\n");
fprintf(stdout, " --dont-change-password Do not create a new password. Try to use existing keys\n");
fprintf(stdout, " when performing keytab updates (update and create mode only).\n");
fprintf(stdout, " -k, --keytab <file> Use <file> for the keytab (both read and write).\n");
fprintf(stdout, " --keytab-auth-as <name>\n");
fprintf(stdout, " First try to authenticate to AD as principal <name>, using\n");
fprintf(stdout, " creds from the keytab, instead of using the account name\n");
fprintf(stdout, " principal or the host principal, etc.\n");
fprintf(stdout, " --server <address> Use a specific domain controller instead of looking\n");
fprintf(stdout, " up in DNS based upon realm.\n");
fprintf(stdout, " --server-behind-nat Ignore server IP validation error caused by NAT.\n");
fprintf(stdout, " --realm <realm> Use a specific kerberos realm instead of using\n");
fprintf(stdout, " default_realm from krb5.conf.\n");
fprintf(stdout, " --site <site> Find and use domain controller in specific AD site.\n");
fprintf(stdout, " This option is ignored if option --server is used.\n");
fprintf(stdout, " -N, --no-reverse-lookups\n");
fprintf(stdout, " Don't reverse-lookup the domain controller.\n");
fprintf(stdout, " -n, --no-canonical-name\n");
fprintf(stdout, " Do not attempt to canonicalize hostname while\n");
fprintf(stdout, " creating Kerberos principal(s).\n");
fprintf(stdout, " --use-what-is-given\n");
fprintf(stdout, " Do not attempt to use other service principals\n");
fprintf(stdout, " then given on the comman line e.g. host, XX$.\n");
fprintf(stdout, " --user-creds-only Don't attempt to authenticate with machine keytab:\n");
fprintf(stdout, " only use user's credentials (from e.g. kinit).\n");
fprintf(stdout, " --auto-update-interval <days>\n");
fprintf(stdout, " Number of <days> when auto-update will change the\n");
fprintf(stdout, " account password. Defaults to 30 days.\n");
fprintf(stdout, " -m, --sasl-mechanisms <mechanisms list>\n");
fprintf(stdout, " Candidate SASL mechanisms to use when performing\n");
fprintf(stdout, " the LDAP bind. Defaults to \"GSS-SPNEGO GSSAPI\".\n");
fprintf(stdout, "\n");
fprintf(stdout, "Object type/attribute-setting options:\n");
fprintf(stdout, " --use-service-account Create and maintain service account instead of\n");
fprintf(stdout, " machine account.\n");
fprintf(stdout, " --enable Enable the account.\n");
fprintf(stdout, " --delegation Set the account to be trusted for delegation.\n");
fprintf(stdout, " --disable-delegation Set the account to not be trusted for\n");
fprintf(stdout, " delegation.\n");
fprintf(stdout, " --description <text> Sets the description field on the account.\n");
fprintf(stdout, " --dont-expire-password Disables password expiration for the account.\n");
fprintf(stdout, " --dont-update-dnshostname\n");
fprintf(stdout, " Do not update dNSHostName attribute.\n");
fprintf(stdout, " --do-expire-password Undisables (puts back to default) password expiration.\n");
fprintf(stdout, " --enctypes <int> Sets msDs-supportedEncryptionTypes\n");
fprintf(stdout, " (OR of: 0x1=des-cbc-crc 0x2=des-cbc-md5\n");
fprintf(stdout, " 0x4=rc4-hmac-md5 0x8=aes128-cts-hmac-sha1\n");
fprintf(stdout, " 0x10=aes256-cts-hmac-sha1)\n");
fprintf(stdout, " Sets des-only in userAccountControl if set to 0x3.\n");
fprintf(stdout, " --allow-weak-crypto Enables the usage of DES keys for authentication\n");
fprintf(stdout, " --no-pac Sets the service principal to not include a PAC.\n");
fprintf(stdout, " --disable-no-pac Sets the service principal to include a PAC.\n");
fprintf(stdout, " -s, --service <name> Adds the service <name> for the current host or the\n");
fprintf(stdout, " given service account. The service is of the form\n");
fprintf(stdout, " <service>/<hostname>.\n");
fprintf(stdout, " If the hostname is omitted, assumes current hostname.\n");
fprintf(stdout, " --remove-service <name> Same, but removes instead of adds.\n");
fprintf(stdout, " --upn <principal> Set the user principal name to be <principal>.\n");
fprintf(stdout, " The realm name will be appended to this principal.\n");
fprintf(stdout, " --set-samba-secret Use the net changesecretpw command to locally set the\n");
fprintf(stdout, " machine account password in samba's secrets.tdb.\n");
fprintf(stdout, " $PATH need to include Samba's net command.\n");
fprintf(stdout, " --use-samba-cmd <command> Use the supplied command instead of samba\n");
fprintf(stdout, " net changesecretpw.\n");
fprintf(stdout, " --check-replication Wait until password change is reflected in LDAP.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Cleanup options:\n");
fprintf(stdout, " --remove-old <number> Removes entries older than <number> days\n");
fprintf(stdout, " --remove-enctype <enctype>\n");
fprintf(stdout, " Removes entries with given <enctype>. Supported enctype\n");
fprintf(stdout, " strings are: des-cbc-crc,des-cbc-md5, arcfour, aes128\n");
fprintf(stdout, " and aes256\n");
}
void do_version()
{
fprintf(stdout, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
}
static int wait_for_new_kvno(msktutil_flags *flags)
{
if (!flags->check_replication) {
return 0;
}
if (flags->auth_type == AUTH_FROM_SUPPLIED_EXPIRED_PASSWORD) {
VERBOSE("Warning: authenticated with expired password -- "
"no way to verify the password change in LDAP.");
return 0;
}
VERBOSE("Checking new kvno via ldap");
/* Loop and wait for the account and password set to replicate */
for (int this_time = 0; ; this_time += 5) {
krb5_kvno current_kvno = ldap_get_kvno(flags);
if (current_kvno == flags->kvno) {
return 0;
}
fprintf(stdout,
"Waiting for account replication (%d seconds past)\n",
this_time);
sleep(5);
}
}
int execute(msktutil_exec *exec, msktutil_flags *flags)
{
int ret = 0;
if (flags->password_from_cmdline) {
VERBOSE("Using password from command line");
} else if (flags->dont_change_password) {
VERBOSE("Skipping creation of new password");
flags->password = flags->old_account_password;
} else if (exec->mode == MODE_CLEANUP) {
VERBOSE("cleanup mode: don't need a new password");
} else if (exec->mode == MODE_DELETE) {
VERBOSE("delete mode: don't need a new password");
} else {
/* Generate a random password and store it. */
ret = generate_new_password(flags);
if (ret) {
fprintf(stderr, "Error: generate_new_password failed\n");
return ret;
}
}
ret = finalize_exec(exec, flags);
if (ret) {
fprintf(stderr, "Error: finalize_exec failed\n");
exit(ret);
}
if (exec->mode == MODE_FLUSH) {
if (flags->use_service_account) {
fprintf(stdout,
"Flushing all entries for service account %s from the keytab %s\n",
flags->sAMAccountName.c_str(),
flags->keytab_writename.c_str());
} else {
fprintf(stdout,
"Flushing all entries for %s from the keytab %s\n",
flags->hostname.c_str(),
flags->keytab_writename.c_str());
}
ret = flush_keytab(flags);
return ret;
} else if (exec->mode == MODE_DELETE) {
ret = ldap_delete_account(flags);
return ret;
} else if (exec->mode == MODE_CREATE ||
exec->mode == MODE_UPDATE ||
exec->mode == MODE_AUTO_UPDATE) {
if (exec->mode == MODE_AUTO_UPDATE) {
if (flags->auth_type == AUTH_FROM_SAM_KEYTAB ||
flags->auth_type == AUTH_FROM_SAM_UPPERCASE_KEYTAB ||
flags->auth_type == AUTH_FROM_EXPLICIT_KEYTAB) {
std::string pwdLastSet = ldap_get_pwdLastSet(flags);
/* Windows timestamp is in
* 100-nanoseconds-since-1601. (or, tenths of
* microseconds) */
long long windows_timestamp = strtoll(pwdLastSet.c_str(),
NULL,
10);
long long epoch_bias_1601_to_1970 = 116444736000000000LL;
/* Unix timestamp is seconds since 1970. */
long long unix_timestamp;
if (windows_timestamp < epoch_bias_1601_to_1970) {
unix_timestamp = 0;
} else {
unix_timestamp = (windows_timestamp -
epoch_bias_1601_to_1970) / 10000000;
}
time_t current_unix_time = time(NULL);
long long days_since_password_change = (current_unix_time -
unix_timestamp) / 86400;
VERBOSE("Password last set %lld days ago.",
days_since_password_change);
if (days_since_password_change < flags->auto_update_interval) {
VERBOSE("Exiting because password was changed recently.");
return 0;
}
}
}
/* Check if computer account exists, update if so, create if
* not. */
if (! ldap_check_account(flags)) {
if (flags->password.empty()) {
fprintf(stderr,
"Error: a new AD account needs to be created "
"but there is no password.");
if (flags->dont_change_password) {
fprintf(stderr,
" Please provide a password with "
"--old-account-password <password>");
}
fprintf(stderr, "\n");
exit(1);
} else {
ldap_create_account(flags);
flags->kvno = ldap_get_kvno(flags);
}
} else {
/* We retrieve the kvno _before_ the password change and
* increment it. */
flags->kvno = ldap_get_kvno(flags);
if ((flags->auth_type != AUTH_FROM_SUPPLIED_EXPIRED_PASSWORD) &&
(!flags->dont_change_password)) {
flags->kvno++;
}
if ((flags->auth_type != AUTH_FROM_SUPPLIED_EXPIRED_PASSWORD) &&
(!flags->dont_change_password)) {
/* Set the password. */
ret = set_password(flags);
if (ret) {
fprintf(stderr, "Error: set_password failed\n");
if (flags->use_service_account) {
fprintf(stderr,
"Hint: Does your password policy allow to "
"change %s's password?\n",
flags->sAMAccountName.c_str()
);
fprintf(stderr, " For example, there could be a "
"\"Minimum password age\" policy preventing\n"
);
fprintf(stderr, " passwords from being changed "
"too frequently. If so, you can reset the\n"
);
fprintf(stderr, " password instead of changing "
"it using the --user-creds-only option.\n"
);
fprintf(stderr, " Be aware that you need a "
"ticket of a user with administrative "
"privileges\n"
);
fprintf(stderr, " for that.\n");
}
return ret;
}
}
}
/* Add and remove principals to servicePrincipalName in LDAP.*/
add_and_remove_principals(exec);
remove_keytab_entries(flags, exec->remove_principals);
/* update keytab */
if (flags->use_service_account) {
VERBOSE("Updating all entries for service account %s in the keytab %s",
flags->sAMAccountName.c_str(),
flags->keytab_writename.c_str());
} else {
VERBOSE("Updating all entries for computer account %s in the keytab %s",
flags->sAMAccountName.c_str(),
flags->keytab_writename.c_str());
}
update_keytab(flags);
add_keytab_entries(flags);
wait_for_new_kvno(flags);
return ret;
} else if (exec->mode == MODE_PRECREATE) {
/* Change account password to default value: */
flags->password = create_default_machine_password(
flags->sAMAccountName);
/* Check if computer account exists, update if so, create if
* not. */
if (! ldap_check_account(flags)) {
ldap_create_account(flags);
}
/* Set the password. */
ret = set_password(flags);
if (ret) {
fprintf(stderr, "Error: set_password failed\n");
return ret;
}
/* And add and remove principals to servicePrincipalName in
* LDAP. */
add_and_remove_principals(exec);
wait_for_new_kvno(flags);
return ret;
} else if (exec->mode == MODE_RESET) {
/* reset mode will only work for machine accounts:*/
if (flags->use_service_account) {
fprintf(stderr, "Error: \"reset\" mode and "
"\"--use-service-account\" are "
"mutually exclusive\n");
return 1;
}
/* Change account password to default value: */
flags->password = create_default_machine_password(
flags->sAMAccountName);
/* Check if computer account exists, update if so, error if
* not. */
if (!ldap_check_account(flags)) {
fprintf(stderr, "Error: the account %s does "
"not exist and cannot be "
"reset\n", flags->sAMAccountName.c_str());
return 1;
}
/* Set the password. */
ret = set_password(flags);
if (ret) {
fprintf(stderr, "Error: set_password failed\n");
return ret;
}
wait_for_new_kvno(flags);
return ret;
} else if (exec->mode == MODE_CLEANUP) {
fprintf(stdout, "Cleaning keytab %s\n",
flags->keytab_writename.c_str());
cleanup_keytab(flags);
return 0;
}
return 0;
}
void msktutil_exec::set_mode(msktutil_mode mode) {
if (this->mode != MODE_NONE) {
fprintf(stderr, "Error: only one mode argument may be provided.\n");
fprintf(stderr, "\nFor help, try running %s --help\n\n", PACKAGE_NAME);
exit(1);
}
this->mode = mode;
}
Globals *Globals::instance;
int main(int argc, char *argv [])
{
/* unbuffer stdout. */
setbuf(stdout, NULL);
initialize_g_context();
int i;
int start_i;
start_i = 2;
msktutil_exec *exec = Globals::exec();
msktutil_flags *flags = Globals::flags();
if (argc > 1) {
/* determine MODE */
if (!strcmp(argv[1], "create")) {
exec->set_mode(MODE_CREATE);
} else if (!strcmp(argv[1], "update")) {
exec->set_mode(MODE_UPDATE);
} else if (!strcmp(argv[1], "auto-update")) {
exec->set_mode(MODE_AUTO_UPDATE);
} else if (!strcmp(argv[1], "pre-create")) {
exec->set_mode(MODE_PRECREATE);
} else if (!strcmp(argv[1], "flush")) {
exec->set_mode(MODE_FLUSH);
} else if (!strcmp(argv[1], "cleanup")) {
exec->set_mode(MODE_CLEANUP);
} else if (!strcmp(argv[1], "delete")) {
exec->set_mode(MODE_DELETE);
} else if (!strcmp(argv[1], "reset")) {
exec->set_mode(MODE_RESET);
}
}
if (exec->mode == MODE_NONE) {
/* compatibility for old command line syntax (e.g. "--create"
* or "-c" instead of "create") */
start_i = 1;
}
for (i = start_i; i < argc; i++) {
/* Display Version Message and exit */
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
do_version();
return 0;
}
/* Display Help Messages and exit */
if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "--usage")) {
do_help();
return 0;
}
/* Flush the keytab */
if (!strcmp(argv[i], "--flush") || !strcmp(argv[i], "-f")) {
exec->set_mode(MODE_FLUSH);
continue;
}
/* Update All Principals */
if (!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
exec->set_mode(MODE_UPDATE);
continue;
}
/* Update All Principals, if needed */
if (!strcmp(argv[i], "--auto-update")) {
exec->set_mode(MODE_AUTO_UPDATE);
continue;
}
/* Create 'Default' Keytab */
if (!strcmp(argv[i], "--create") || !strcmp(argv[i], "-c")) {
exec->set_mode(MODE_CREATE);
continue;
}
/* Pre-create computer account for another host */
if (!strcmp(argv[i], "--precreate")) {
exec->set_mode(MODE_PRECREATE);
flags->user_creds_only = true;
continue;
}
/* Service Principal Name */
if (!strcmp(argv[i], "--service") || !strcmp(argv[i], "-s")) {
if (++i < argc) {
exec->add_principals.push_back(argv[i]);
if (strchr(argv[i],'/')) {
flags->servicePrincipalName = argv[i];
}
} else {
fprintf(stderr,
"Error: No service principal given after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
if (!strcmp(argv[i], "--remove-service")) {
if (++i < argc) {
exec->remove_principals.push_back(argv[i]);
} else {
fprintf(stderr,
"Error: No service principal given after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
/* Host name */
if (!strcmp(argv[i], "--host") ||
!strcmp(argv[i], "--hostname") ||
!strcmp(argv[i], "-h")) {
if (++i < argc) {
flags->hostname = argv[i];
} else {
fprintf(stderr,
"Error: No name given after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
/* no canonical name */
if (!strcmp(argv[i], "--no-canonical-name") ||
!strcmp(argv[i], "-n")) {
flags->no_canonical_name = true;
continue;
}
/* no canonical name */
if (!strcmp(argv[i], "--use-what-is-given")) {
flags->use_what_is_given = true;
continue;
}
/* computer password */
if (!strcmp(argv[i], "--old-account-password")) {
if (++i < argc) {
flags->old_account_password = argv[i];
} else {
fprintf(stderr,
"Error: No password given after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
if (!strcmp(argv[i], "--password")) {
if (++i < argc) {
flags->password_from_cmdline = true;
flags->password = argv[i];
} else {
fprintf(stderr,
"Error: No password given after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
/* do not change the password */
if (!strcmp(argv[i], "--dont-change-password")) {
flags->dont_change_password = true;
continue;
}
/* site */
if (!strcmp(argv[i], "--site")) {
if (++i < argc) {
flags->site = argv[i];
} else {
fprintf(stderr,
"Error: No site given after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
/* W2008 msDs-supportedEncryptionTypes */
if (!strcmp(argv[i], "--enctypes")) {
if (++i < argc) {
set_supportedEncryptionTypes(flags, argv[i]);
} else {
fprintf(stderr,
"Error: No enctype after '%s'\n",
argv[i - 1]
);
goto error;
}
continue;
}
/* Re-activate DES encryption in fake krb5.conf */
if (!strcmp(argv[i], "--allow-weak-crypto")) {
flags->allow_weak_crypto = true;
continue;