Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/kadm5/ad.c
34870 views
1
/*
2
* Copyright (c) 2004 Kungliga Tekniska Högskolan
3
* (Royal Institute of Technology, Stockholm, Sweden).
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
*
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* 3. Neither the name of the Institute nor the names of its contributors
18
* may be used to endorse or promote products derived from this software
19
* without specific prior written permission.
20
*
21
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
* SUCH DAMAGE.
32
*/
33
34
#define HAVE_TSASL 1
35
36
#include "kadm5_locl.h"
37
#if 1
38
#undef OPENLDAP
39
#undef HAVE_TSASL
40
#endif
41
#ifdef OPENLDAP
42
#include <ldap.h>
43
#ifdef HAVE_TSASL
44
#include <tsasl.h>
45
#endif
46
#include <resolve.h>
47
#include <base64.h>
48
#endif
49
50
RCSID("$Id$");
51
52
#ifdef OPENLDAP
53
54
#define CTX2LP(context) ((LDAP *)((context)->ldap_conn))
55
#define CTX2BASE(context) ((context)->base_dn)
56
57
/*
58
* userAccountControl
59
*/
60
61
#define UF_SCRIPT 0x00000001
62
#define UF_ACCOUNTDISABLE 0x00000002
63
#define UF_UNUSED_0 0x00000004
64
#define UF_HOMEDIR_REQUIRED 0x00000008
65
#define UF_LOCKOUT 0x00000010
66
#define UF_PASSWD_NOTREQD 0x00000020
67
#define UF_PASSWD_CANT_CHANGE 0x00000040
68
#define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED 0x00000080
69
#define UF_TEMP_DUPLICATE_ACCOUNT 0x00000100
70
#define UF_NORMAL_ACCOUNT 0x00000200
71
#define UF_UNUSED_1 0x00000400
72
#define UF_INTERDOMAIN_TRUST_ACCOUNT 0x00000800
73
#define UF_WORKSTATION_TRUST_ACCOUNT 0x00001000
74
#define UF_SERVER_TRUST_ACCOUNT 0x00002000
75
#define UF_UNUSED_2 0x00004000
76
#define UF_UNUSED_3 0x00008000
77
#define UF_PASSWD_NOT_EXPIRE 0x00010000
78
#define UF_MNS_LOGON_ACCOUNT 0x00020000
79
#define UF_SMARTCARD_REQUIRED 0x00040000
80
#define UF_TRUSTED_FOR_DELEGATION 0x00080000
81
#define UF_NOT_DELEGATED 0x00100000
82
#define UF_USE_DES_KEY_ONLY 0x00200000
83
#define UF_DONT_REQUIRE_PREAUTH 0x00400000
84
#define UF_UNUSED_4 0x00800000
85
#define UF_UNUSED_5 0x01000000
86
#define UF_UNUSED_6 0x02000000
87
#define UF_UNUSED_7 0x04000000
88
#define UF_UNUSED_8 0x08000000
89
#define UF_UNUSED_9 0x10000000
90
#define UF_UNUSED_10 0x20000000
91
#define UF_UNUSED_11 0x40000000
92
#define UF_UNUSED_12 0x80000000
93
94
/*
95
*
96
*/
97
98
#ifndef HAVE_TSASL
99
static int
100
sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *interact)
101
{
102
return LDAP_SUCCESS;
103
}
104
#endif
105
106
#if 0
107
static Sockbuf_IO ldap_tsasl_io = {
108
NULL, /* sbi_setup */
109
NULL, /* sbi_remove */
110
NULL, /* sbi_ctrl */
111
NULL, /* sbi_read */
112
NULL, /* sbi_write */
113
NULL /* sbi_close */
114
};
115
#endif
116
117
#ifdef HAVE_TSASL
118
static int
119
ldap_tsasl_bind_s(LDAP *ld,
120
LDAP_CONST char *dn,
121
LDAPControl **serverControls,
122
LDAPControl **clientControls,
123
const char *host)
124
{
125
char *attrs[] = { "supportedSASLMechanisms", NULL };
126
struct tsasl_peer *peer = NULL;
127
struct tsasl_buffer in, out;
128
struct berval ccred, *scred;
129
LDAPMessage *m, *m0;
130
const char *mech;
131
char **vals;
132
int ret, rc;
133
134
ret = tsasl_peer_init(TSASL_FLAGS_INITIATOR | TSASL_FLAGS_CLEAR,
135
"ldap", host, &peer);
136
if (ret != TSASL_DONE) {
137
rc = LDAP_LOCAL_ERROR;
138
goto out;
139
}
140
141
rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, &m0);
142
if (rc != LDAP_SUCCESS)
143
goto out;
144
145
m = ldap_first_entry(ld, m0);
146
if (m == NULL) {
147
ldap_msgfree(m0);
148
goto out;
149
}
150
151
vals = ldap_get_values(ld, m, "supportedSASLMechanisms");
152
if (vals == NULL) {
153
ldap_msgfree(m0);
154
goto out;
155
}
156
157
ret = tsasl_find_best_mech(peer, vals, &mech);
158
if (ret) {
159
ldap_msgfree(m0);
160
goto out;
161
}
162
163
ldap_msgfree(m0);
164
165
ret = tsasl_select_mech(peer, mech);
166
if (ret != TSASL_DONE) {
167
rc = LDAP_LOCAL_ERROR;
168
goto out;
169
}
170
171
in.tb_data = NULL;
172
in.tb_size = 0;
173
174
do {
175
ret = tsasl_request(peer, &in, &out);
176
if (in.tb_size != 0) {
177
free(in.tb_data);
178
in.tb_data = NULL;
179
in.tb_size = 0;
180
}
181
if (ret != TSASL_DONE && ret != TSASL_CONTINUE) {
182
rc = LDAP_AUTH_UNKNOWN;
183
goto out;
184
}
185
186
ccred.bv_val = out.tb_data;
187
ccred.bv_len = out.tb_size;
188
189
rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
190
serverControls, clientControls, &scred);
191
tsasl_buffer_free(&out);
192
193
if (rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS) {
194
if(scred && scred->bv_len)
195
ber_bvfree(scred);
196
goto out;
197
}
198
199
in.tb_data = malloc(scred->bv_len);
200
if (in.tb_data == NULL) {
201
rc = LDAP_LOCAL_ERROR;
202
goto out;
203
}
204
memcpy(in.tb_data, scred->bv_val, scred->bv_len);
205
in.tb_size = scred->bv_len;
206
ber_bvfree(scred);
207
208
} while (rc == LDAP_SASL_BIND_IN_PROGRESS);
209
210
out:
211
if (rc == LDAP_SUCCESS) {
212
#if 0
213
ber_sockbuf_add_io(ld->ld_conns->lconn_sb, &ldap_tsasl_io,
214
LBER_SBIOD_LEVEL_APPLICATION, peer);
215
216
#endif
217
} else if (peer != NULL)
218
tsasl_peer_free(peer);
219
220
return rc;
221
}
222
#endif /* HAVE_TSASL */
223
224
225
static int
226
check_ldap(kadm5_ad_context *context, int ret)
227
{
228
switch (ret) {
229
case LDAP_SUCCESS:
230
return 0;
231
case LDAP_SERVER_DOWN: {
232
LDAP *lp = CTX2LP(context);
233
ldap_unbind(lp);
234
context->ldap_conn = NULL;
235
free(context->base_dn);
236
context->base_dn = NULL;
237
return 1;
238
}
239
default:
240
return 1;
241
}
242
}
243
244
/*
245
*
246
*/
247
248
static void
249
laddattr(char ***al, int *attrlen, char *attr)
250
{
251
char **a;
252
a = realloc(*al, (*attrlen + 2) * sizeof(**al));
253
if (a == NULL)
254
return;
255
a[*attrlen] = attr;
256
a[*attrlen + 1] = NULL;
257
(*attrlen)++;
258
*al = a;
259
}
260
261
static kadm5_ret_t
262
_kadm5_ad_connect(void *server_handle)
263
{
264
kadm5_ad_context *context = server_handle;
265
struct {
266
char *server;
267
int port;
268
} *s, *servers = NULL;
269
int i, num_servers = 0;
270
271
if (context->ldap_conn)
272
return 0;
273
274
{
275
struct dns_reply *r;
276
struct resource_record *rr;
277
char *domain;
278
279
asprintf(&domain, "_ldap._tcp.%s", context->realm);
280
if (domain == NULL) {
281
krb5_set_error_message(context->context, KADM5_NO_SRV, "malloc");
282
return KADM5_NO_SRV;
283
}
284
285
r = dns_lookup(domain, "SRV");
286
free(domain);
287
if (r == NULL) {
288
krb5_set_error_message(context->context, KADM5_NO_SRV, "Didn't find ldap dns");
289
return KADM5_NO_SRV;
290
}
291
292
for (rr = r->head ; rr != NULL; rr = rr->next) {
293
if (rr->type != rk_ns_t_srv)
294
continue;
295
s = realloc(servers, sizeof(*servers) * (num_servers + 1));
296
if (s == NULL) {
297
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "malloc");
298
dns_free_data(r);
299
goto fail;
300
}
301
servers = s;
302
num_servers++;
303
servers[num_servers - 1].port = rr->u.srv->port;
304
servers[num_servers - 1].server = strdup(rr->u.srv->target);
305
}
306
dns_free_data(r);
307
}
308
309
if (num_servers == 0) {
310
krb5_set_error_message(context->context, KADM5_NO_SRV, "No AD server found in DNS");
311
return KADM5_NO_SRV;
312
}
313
314
for (i = 0; i < num_servers; i++) {
315
int lret, version = LDAP_VERSION3;
316
LDAP *lp;
317
318
lp = ldap_init(servers[i].server, servers[i].port);
319
if (lp == NULL)
320
continue;
321
322
if (ldap_set_option(lp, LDAP_OPT_PROTOCOL_VERSION, &version)) {
323
ldap_unbind(lp);
324
continue;
325
}
326
327
if (ldap_set_option(lp, LDAP_OPT_REFERRALS, LDAP_OPT_OFF)) {
328
ldap_unbind(lp);
329
continue;
330
}
331
332
#ifdef HAVE_TSASL
333
lret = ldap_tsasl_bind_s(lp, NULL, NULL, NULL, servers[i].server);
334
335
#else
336
lret = ldap_sasl_interactive_bind_s(lp, NULL, NULL, NULL, NULL,
337
LDAP_SASL_QUIET,
338
sasl_interact, NULL);
339
#endif
340
if (lret != LDAP_SUCCESS) {
341
krb5_set_error_message(context->context, 0,
342
"Couldn't contact any AD servers: %s",
343
ldap_err2string(lret));
344
ldap_unbind(lp);
345
continue;
346
}
347
348
context->ldap_conn = lp;
349
break;
350
}
351
if (i >= num_servers) {
352
goto fail;
353
}
354
355
{
356
LDAPMessage *m, *m0;
357
char **attr = NULL;
358
int attrlen = 0;
359
char **vals;
360
int ret;
361
362
laddattr(&attr, &attrlen, "defaultNamingContext");
363
364
ret = ldap_search_s(CTX2LP(context), "", LDAP_SCOPE_BASE,
365
"objectclass=*", attr, 0, &m);
366
free(attr);
367
if (check_ldap(context, ret))
368
goto fail;
369
370
if (ldap_count_entries(CTX2LP(context), m) > 0) {
371
m0 = ldap_first_entry(CTX2LP(context), m);
372
if (m0 == NULL) {
373
krb5_set_error_message(context->context, KADM5_RPC_ERROR,
374
"Error in AD ldap responce");
375
ldap_msgfree(m);
376
goto fail;
377
}
378
vals = ldap_get_values(CTX2LP(context),
379
m0, "defaultNamingContext");
380
if (vals == NULL) {
381
krb5_set_error_message(context->context, KADM5_RPC_ERROR,
382
"No naming context found");
383
goto fail;
384
}
385
context->base_dn = strdup(vals[0]);
386
} else
387
goto fail;
388
ldap_msgfree(m);
389
}
390
391
for (i = 0; i < num_servers; i++)
392
free(servers[i].server);
393
free(servers);
394
395
return 0;
396
397
fail:
398
for (i = 0; i < num_servers; i++)
399
free(servers[i].server);
400
free(servers);
401
402
if (context->ldap_conn) {
403
ldap_unbind(CTX2LP(context));
404
context->ldap_conn = NULL;
405
}
406
return KADM5_RPC_ERROR;
407
}
408
409
#define NTTIME_EPOCH 0x019DB1DED53E8000LL
410
411
static time_t
412
nt2unixtime(const char *str)
413
{
414
unsigned long long t;
415
t = strtoll(str, NULL, 10);
416
t = ((t - NTTIME_EPOCH) / (long long)10000000);
417
if (t > (((time_t)(~(long long)0)) >> 1))
418
return 0;
419
return (time_t)t;
420
}
421
422
static long long
423
unix2nttime(time_t unix_time)
424
{
425
long long wt;
426
wt = unix_time * (long long)10000000 + (long long)NTTIME_EPOCH;
427
return wt;
428
}
429
430
/* XXX create filter in a better way */
431
432
static int
433
ad_find_entry(kadm5_ad_context *context,
434
const char *fqdn,
435
const char *pn,
436
char **name)
437
{
438
LDAPMessage *m, *m0;
439
char *attr[] = { "distinguishedName", NULL };
440
char *filter;
441
int ret;
442
443
if (name)
444
*name = NULL;
445
446
if (fqdn)
447
asprintf(&filter,
448
"(&(objectClass=computer)(|(dNSHostName=%s)(servicePrincipalName=%s)))",
449
fqdn, pn);
450
else if(pn)
451
asprintf(&filter, "(&(objectClass=account)(userPrincipalName=%s))", pn);
452
else
453
return KADM5_RPC_ERROR;
454
455
ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
456
LDAP_SCOPE_SUBTREE,
457
filter, attr, 0, &m);
458
free(filter);
459
if (check_ldap(context, ret))
460
return KADM5_RPC_ERROR;
461
462
if (ldap_count_entries(CTX2LP(context), m) > 0) {
463
char **vals;
464
m0 = ldap_first_entry(CTX2LP(context), m);
465
vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
466
if (vals == NULL || vals[0] == NULL) {
467
ldap_msgfree(m);
468
return KADM5_RPC_ERROR;
469
}
470
if (name)
471
*name = strdup(vals[0]);
472
ldap_msgfree(m);
473
} else
474
return KADM5_UNK_PRINC;
475
476
return 0;
477
}
478
479
#endif /* OPENLDAP */
480
481
static kadm5_ret_t
482
ad_get_cred(kadm5_ad_context *context, const char *password)
483
{
484
kadm5_ret_t ret;
485
krb5_ccache cc;
486
char *service;
487
488
if (context->ccache)
489
return 0;
490
491
asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME,
492
context->realm, context->realm);
493
if (service == NULL)
494
return ENOMEM;
495
496
ret = _kadm5_c_get_cred_cache(context->context,
497
context->client_name,
498
service,
499
password, krb5_prompter_posix,
500
NULL, NULL, &cc);
501
free(service);
502
if(ret)
503
return ret; /* XXX */
504
context->ccache = cc;
505
return 0;
506
}
507
508
static kadm5_ret_t
509
kadm5_ad_chpass_principal(void *server_handle,
510
krb5_principal principal,
511
const char *password)
512
{
513
kadm5_ad_context *context = server_handle;
514
krb5_data result_code_string, result_string;
515
int result_code;
516
kadm5_ret_t ret;
517
518
ret = ad_get_cred(context, NULL);
519
if (ret)
520
return ret;
521
522
krb5_data_zero (&result_code_string);
523
krb5_data_zero (&result_string);
524
525
ret = krb5_set_password_using_ccache (context->context,
526
context->ccache,
527
password,
528
principal,
529
&result_code,
530
&result_code_string,
531
&result_string);
532
533
krb5_data_free (&result_code_string);
534
krb5_data_free (&result_string);
535
536
/* XXX do mapping here on error codes */
537
538
return ret;
539
}
540
541
#ifdef OPENLDAP
542
static const char *
543
get_fqdn(krb5_context context, const krb5_principal p)
544
{
545
const char *s, *hosttypes[] = { "host", "ldap", "gc", "cifs", "dns" };
546
int i;
547
548
s = krb5_principal_get_comp_string(context, p, 0);
549
if (p == NULL)
550
return NULL;
551
552
for (i = 0; i < sizeof(hosttypes)/sizeof(hosttypes[0]); i++) {
553
if (strcasecmp(s, hosttypes[i]) == 0)
554
return krb5_principal_get_comp_string(context, p, 1);
555
}
556
return 0;
557
}
558
#endif
559
560
561
static kadm5_ret_t
562
kadm5_ad_create_principal(void *server_handle,
563
kadm5_principal_ent_t entry,
564
uint32_t mask,
565
const char *password)
566
{
567
kadm5_ad_context *context = server_handle;
568
569
/*
570
* KADM5_PRINC_EXPIRE_TIME
571
*
572
* return 0 || KADM5_DUP;
573
*/
574
575
#ifdef OPENLDAP
576
LDAPMod *attrs[8], rattrs[7], *a;
577
char *useraccvals[2] = { NULL, NULL },
578
*samvals[2], *dnsvals[2], *spnvals[5], *upnvals[2], *tv[2];
579
char *ocvals_spn[] = { "top", "person", "organizationalPerson",
580
"user", "computer", NULL};
581
char *p, *realmless_p, *p_msrealm = NULL, *dn = NULL;
582
const char *fqdn;
583
char *s, *samname = NULL, *short_spn = NULL;
584
int ret, i;
585
int32_t uf_flags = 0;
586
587
if ((mask & KADM5_PRINCIPAL) == 0)
588
return KADM5_BAD_MASK;
589
590
for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
591
attrs[i] = &rattrs[i];
592
attrs[i] = NULL;
593
594
ret = ad_get_cred(context, NULL);
595
if (ret)
596
return ret;
597
598
ret = _kadm5_ad_connect(server_handle);
599
if (ret)
600
return ret;
601
602
fqdn = get_fqdn(context->context, entry->principal);
603
604
ret = krb5_unparse_name(context->context, entry->principal, &p);
605
if (ret)
606
return ret;
607
608
if (ad_find_entry(context, fqdn, p, NULL) == 0) {
609
free(p);
610
return KADM5_DUP;
611
}
612
613
if (mask & KADM5_ATTRIBUTES) {
614
if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
615
uf_flags |= UF_ACCOUNTDISABLE|UF_LOCKOUT;
616
if ((entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) == 0)
617
uf_flags |= UF_DONT_REQUIRE_PREAUTH;
618
if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
619
uf_flags |= UF_SMARTCARD_REQUIRED;
620
}
621
622
realmless_p = strdup(p);
623
if (realmless_p == NULL) {
624
ret = ENOMEM;
625
goto out;
626
}
627
s = strrchr(realmless_p, '@');
628
if (s)
629
*s = '\0';
630
631
if (fqdn) {
632
/* create computer account */
633
asprintf(&samname, "%s$", fqdn);
634
if (samname == NULL) {
635
ret = ENOMEM;
636
goto out;
637
}
638
s = strchr(samname, '.');
639
if (s) {
640
s[0] = '$';
641
s[1] = '\0';
642
}
643
644
short_spn = strdup(p);
645
if (short_spn == NULL) {
646
errno = ENOMEM;
647
goto out;
648
}
649
s = strchr(short_spn, '.');
650
if (s) {
651
*s = '\0';
652
} else {
653
free(short_spn);
654
short_spn = NULL;
655
}
656
657
p_msrealm = strdup(p);
658
if (p_msrealm == NULL) {
659
errno = ENOMEM;
660
goto out;
661
}
662
s = strrchr(p_msrealm, '@');
663
if (s) {
664
*s = '/';
665
} else {
666
free(p_msrealm);
667
p_msrealm = NULL;
668
}
669
670
asprintf(&dn, "cn=%s, cn=Computers, %s", fqdn, CTX2BASE(context));
671
if (dn == NULL) {
672
ret = ENOMEM;
673
goto out;
674
}
675
676
a = &rattrs[0];
677
a->mod_op = LDAP_MOD_ADD;
678
a->mod_type = "objectClass";
679
a->mod_values = ocvals_spn;
680
a++;
681
682
a->mod_op = LDAP_MOD_ADD;
683
a->mod_type = "userAccountControl";
684
a->mod_values = useraccvals;
685
asprintf(&useraccvals[0], "%d",
686
uf_flags |
687
UF_PASSWD_NOT_EXPIRE |
688
UF_WORKSTATION_TRUST_ACCOUNT);
689
useraccvals[1] = NULL;
690
a++;
691
692
a->mod_op = LDAP_MOD_ADD;
693
a->mod_type = "sAMAccountName";
694
a->mod_values = samvals;
695
samvals[0] = samname;
696
samvals[1] = NULL;
697
a++;
698
699
a->mod_op = LDAP_MOD_ADD;
700
a->mod_type = "dNSHostName";
701
a->mod_values = dnsvals;
702
dnsvals[0] = (char *)fqdn;
703
dnsvals[1] = NULL;
704
a++;
705
706
/* XXX add even more spn's */
707
a->mod_op = LDAP_MOD_ADD;
708
a->mod_type = "servicePrincipalName";
709
a->mod_values = spnvals;
710
i = 0;
711
spnvals[i++] = p;
712
spnvals[i++] = realmless_p;
713
if (short_spn)
714
spnvals[i++] = short_spn;
715
if (p_msrealm)
716
spnvals[i++] = p_msrealm;
717
spnvals[i++] = NULL;
718
a++;
719
720
a->mod_op = LDAP_MOD_ADD;
721
a->mod_type = "userPrincipalName";
722
a->mod_values = upnvals;
723
upnvals[0] = p;
724
upnvals[1] = NULL;
725
a++;
726
727
a->mod_op = LDAP_MOD_ADD;
728
a->mod_type = "accountExpires";
729
a->mod_values = tv;
730
tv[0] = "9223372036854775807"; /* "never" */
731
tv[1] = NULL;
732
a++;
733
734
} else {
735
/* create user account */
736
737
a = &rattrs[0];
738
a->mod_op = LDAP_MOD_ADD;
739
a->mod_type = "userAccountControl";
740
a->mod_values = useraccvals;
741
asprintf(&useraccvals[0], "%d",
742
uf_flags |
743
UF_PASSWD_NOT_EXPIRE);
744
useraccvals[1] = NULL;
745
a++;
746
747
a->mod_op = LDAP_MOD_ADD;
748
a->mod_type = "sAMAccountName";
749
a->mod_values = samvals;
750
samvals[0] = realmless_p;
751
samvals[1] = NULL;
752
a++;
753
754
a->mod_op = LDAP_MOD_ADD;
755
a->mod_type = "userPrincipalName";
756
a->mod_values = upnvals;
757
upnvals[0] = p;
758
upnvals[1] = NULL;
759
a++;
760
761
a->mod_op = LDAP_MOD_ADD;
762
a->mod_type = "accountExpires";
763
a->mod_values = tv;
764
tv[0] = "9223372036854775807"; /* "never" */
765
tv[1] = NULL;
766
a++;
767
}
768
769
attrs[a - &rattrs[0]] = NULL;
770
771
ret = ldap_add_s(CTX2LP(context), dn, attrs);
772
773
out:
774
if (useraccvals[0])
775
free(useraccvals[0]);
776
if (realmless_p)
777
free(realmless_p);
778
if (samname)
779
free(samname);
780
if (short_spn)
781
free(short_spn);
782
if (p_msrealm)
783
free(p_msrealm);
784
free(p);
785
786
if (check_ldap(context, ret))
787
return KADM5_RPC_ERROR;
788
789
return 0;
790
#else
791
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
792
return KADM5_RPC_ERROR;
793
#endif
794
}
795
796
static kadm5_ret_t
797
kadm5_ad_delete_principal(void *server_handle, krb5_principal principal)
798
{
799
kadm5_ad_context *context = server_handle;
800
#ifdef OPENLDAP
801
char *p, *dn = NULL;
802
const char *fqdn;
803
int ret;
804
805
ret = ad_get_cred(context, NULL);
806
if (ret)
807
return ret;
808
809
ret = _kadm5_ad_connect(server_handle);
810
if (ret)
811
return ret;
812
813
fqdn = get_fqdn(context->context, principal);
814
815
ret = krb5_unparse_name(context->context, principal, &p);
816
if (ret)
817
return ret;
818
819
if (ad_find_entry(context, fqdn, p, &dn) != 0) {
820
free(p);
821
return KADM5_UNK_PRINC;
822
}
823
824
ret = ldap_delete_s(CTX2LP(context), dn);
825
826
free(dn);
827
free(p);
828
829
if (check_ldap(context, ret))
830
return KADM5_RPC_ERROR;
831
return 0;
832
#else
833
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
834
return KADM5_RPC_ERROR;
835
#endif
836
}
837
838
static kadm5_ret_t
839
kadm5_ad_destroy(void *server_handle)
840
{
841
kadm5_ad_context *context = server_handle;
842
843
if (context->ccache)
844
krb5_cc_destroy(context->context, context->ccache);
845
846
#ifdef OPENLDAP
847
{
848
LDAP *lp = CTX2LP(context);
849
if (lp)
850
ldap_unbind(lp);
851
if (context->base_dn)
852
free(context->base_dn);
853
}
854
#endif
855
free(context->realm);
856
free(context->client_name);
857
krb5_free_principal(context->context, context->caller);
858
if(context->my_context)
859
krb5_free_context(context->context);
860
return 0;
861
}
862
863
static kadm5_ret_t
864
kadm5_ad_flush(void *server_handle)
865
{
866
kadm5_ad_context *context = server_handle;
867
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
868
return KADM5_RPC_ERROR;
869
}
870
871
static kadm5_ret_t
872
kadm5_ad_get_principal(void *server_handle,
873
krb5_principal principal,
874
kadm5_principal_ent_t entry,
875
uint32_t mask)
876
{
877
kadm5_ad_context *context = server_handle;
878
#ifdef OPENLDAP
879
LDAPMessage *m, *m0;
880
char **attr = NULL;
881
int attrlen = 0;
882
char *filter, *p, *q, *u;
883
int ret;
884
885
/*
886
* principal
887
* KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
888
*/
889
890
/*
891
* return 0 || KADM5_DUP;
892
*/
893
894
memset(entry, 0, sizeof(*entry));
895
896
if (mask & KADM5_KVNO)
897
laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
898
899
if (mask & KADM5_PRINCIPAL) {
900
laddattr(&attr, &attrlen, "userPrincipalName");
901
laddattr(&attr, &attrlen, "servicePrincipalName");
902
}
903
laddattr(&attr, &attrlen, "objectClass");
904
laddattr(&attr, &attrlen, "lastLogon");
905
laddattr(&attr, &attrlen, "badPwdCount");
906
laddattr(&attr, &attrlen, "badPasswordTime");
907
laddattr(&attr, &attrlen, "pwdLastSet");
908
laddattr(&attr, &attrlen, "accountExpires");
909
laddattr(&attr, &attrlen, "userAccountControl");
910
911
krb5_unparse_name_short(context->context, principal, &p);
912
krb5_unparse_name(context->context, principal, &u);
913
914
/* replace @ in domain part with a / */
915
q = strrchr(p, '@');
916
if (q && (p != q && *(q - 1) != '\\'))
917
*q = '/';
918
919
asprintf(&filter,
920
"(|(userPrincipalName=%s)(servicePrincipalName=%s)(servicePrincipalName=%s))",
921
u, p, u);
922
free(p);
923
free(u);
924
925
ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
926
LDAP_SCOPE_SUBTREE,
927
filter, attr, 0, &m);
928
free(attr);
929
if (check_ldap(context, ret))
930
return KADM5_RPC_ERROR;
931
932
if (ldap_count_entries(CTX2LP(context), m) > 0) {
933
char **vals;
934
m0 = ldap_first_entry(CTX2LP(context), m);
935
if (m0 == NULL) {
936
ldap_msgfree(m);
937
goto fail;
938
}
939
#if 0
940
vals = ldap_get_values(CTX2LP(context), m0, "servicePrincipalName");
941
if (vals)
942
printf("servicePrincipalName %s\n", vals[0]);
943
vals = ldap_get_values(CTX2LP(context), m0, "userPrincipalName");
944
if (vals)
945
printf("userPrincipalName %s\n", vals[0]);
946
vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
947
if (vals)
948
printf("userAccountControl %s\n", vals[0]);
949
#endif
950
entry->princ_expire_time = 0;
951
if (mask & KADM5_PRINC_EXPIRE_TIME) {
952
vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
953
if (vals)
954
entry->princ_expire_time = nt2unixtime(vals[0]);
955
}
956
entry->last_success = 0;
957
if (mask & KADM5_LAST_SUCCESS) {
958
vals = ldap_get_values(CTX2LP(context), m0, "lastLogon");
959
if (vals)
960
entry->last_success = nt2unixtime(vals[0]);
961
}
962
if (mask & KADM5_LAST_FAILED) {
963
vals = ldap_get_values(CTX2LP(context), m0, "badPasswordTime");
964
if (vals)
965
entry->last_failed = nt2unixtime(vals[0]);
966
}
967
if (mask & KADM5_LAST_PWD_CHANGE) {
968
vals = ldap_get_values(CTX2LP(context), m0, "pwdLastSet");
969
if (vals)
970
entry->last_pwd_change = nt2unixtime(vals[0]);
971
}
972
if (mask & KADM5_FAIL_AUTH_COUNT) {
973
vals = ldap_get_values(CTX2LP(context), m0, "badPwdCount");
974
if (vals)
975
entry->fail_auth_count = atoi(vals[0]);
976
}
977
if (mask & KADM5_ATTRIBUTES) {
978
vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
979
if (vals) {
980
uint32_t i;
981
i = atoi(vals[0]);
982
if (i & (UF_ACCOUNTDISABLE|UF_LOCKOUT))
983
entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
984
if ((i & UF_DONT_REQUIRE_PREAUTH) == 0)
985
entry->attributes |= KRB5_KDB_REQUIRES_PRE_AUTH;
986
if (i & UF_SMARTCARD_REQUIRED)
987
entry->attributes |= KRB5_KDB_REQUIRES_HW_AUTH;
988
if ((i & UF_WORKSTATION_TRUST_ACCOUNT) == 0)
989
entry->attributes |= KRB5_KDB_DISALLOW_SVR;
990
}
991
}
992
if (mask & KADM5_KVNO) {
993
vals = ldap_get_values(CTX2LP(context), m0,
994
"msDS-KeyVersionNumber");
995
if (vals)
996
entry->kvno = atoi(vals[0]);
997
else
998
entry->kvno = 0;
999
}
1000
ldap_msgfree(m);
1001
} else {
1002
return KADM5_UNK_PRINC;
1003
}
1004
1005
if (mask & KADM5_PRINCIPAL)
1006
krb5_copy_principal(context->context, principal, &entry->principal);
1007
1008
return 0;
1009
fail:
1010
return KADM5_RPC_ERROR;
1011
#else
1012
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1013
return KADM5_RPC_ERROR;
1014
#endif
1015
}
1016
1017
static kadm5_ret_t
1018
kadm5_ad_get_principals(void *server_handle,
1019
const char *expression,
1020
char ***principals,
1021
int *count)
1022
{
1023
kadm5_ad_context *context = server_handle;
1024
1025
/*
1026
* KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
1027
*/
1028
1029
#ifdef OPENLDAP
1030
kadm5_ret_t ret;
1031
1032
ret = ad_get_cred(context, NULL);
1033
if (ret)
1034
return ret;
1035
1036
ret = _kadm5_ad_connect(server_handle);
1037
if (ret)
1038
return ret;
1039
1040
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1041
return KADM5_RPC_ERROR;
1042
#else
1043
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1044
return KADM5_RPC_ERROR;
1045
#endif
1046
}
1047
1048
static kadm5_ret_t
1049
kadm5_ad_get_privs(void *server_handle, uint32_t*privs)
1050
{
1051
kadm5_ad_context *context = server_handle;
1052
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1053
return KADM5_RPC_ERROR;
1054
}
1055
1056
static kadm5_ret_t
1057
kadm5_ad_modify_principal(void *server_handle,
1058
kadm5_principal_ent_t entry,
1059
uint32_t mask)
1060
{
1061
kadm5_ad_context *context = server_handle;
1062
1063
/*
1064
* KADM5_ATTRIBUTES
1065
* KRB5_KDB_DISALLOW_ALL_TIX (| KADM5_KVNO)
1066
*/
1067
1068
#ifdef OPENLDAP
1069
LDAPMessage *m = NULL, *m0;
1070
kadm5_ret_t ret;
1071
char **attr = NULL;
1072
int attrlen = 0;
1073
char *p = NULL, *s = NULL, *q;
1074
char **vals;
1075
LDAPMod *attrs[4], rattrs[3], *a;
1076
char *uaf[2] = { NULL, NULL };
1077
char *kvno[2] = { NULL, NULL };
1078
char *tv[2] = { NULL, NULL };
1079
char *filter, *dn;
1080
int i;
1081
1082
for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
1083
attrs[i] = &rattrs[i];
1084
attrs[i] = NULL;
1085
a = &rattrs[0];
1086
1087
ret = _kadm5_ad_connect(server_handle);
1088
if (ret)
1089
return ret;
1090
1091
if (mask & KADM5_KVNO)
1092
laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
1093
if (mask & KADM5_PRINC_EXPIRE_TIME)
1094
laddattr(&attr, &attrlen, "accountExpires");
1095
if (mask & KADM5_ATTRIBUTES)
1096
laddattr(&attr, &attrlen, "userAccountControl");
1097
laddattr(&attr, &attrlen, "distinguishedName");
1098
1099
krb5_unparse_name(context->context, entry->principal, &p);
1100
1101
s = strdup(p);
1102
1103
q = strrchr(s, '@');
1104
if (q && (p != q && *(q - 1) != '\\'))
1105
*q = '\0';
1106
1107
asprintf(&filter,
1108
"(|(userPrincipalName=%s)(servicePrincipalName=%s))",
1109
s, s);
1110
free(p);
1111
free(s);
1112
1113
ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
1114
LDAP_SCOPE_SUBTREE,
1115
filter, attr, 0, &m);
1116
free(attr);
1117
free(filter);
1118
if (check_ldap(context, ret))
1119
return KADM5_RPC_ERROR;
1120
1121
if (ldap_count_entries(CTX2LP(context), m) <= 0) {
1122
ret = KADM5_RPC_ERROR;
1123
goto out;
1124
}
1125
1126
m0 = ldap_first_entry(CTX2LP(context), m);
1127
1128
if (mask & KADM5_ATTRIBUTES) {
1129
int32_t i;
1130
1131
vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
1132
if (vals == NULL) {
1133
ret = KADM5_RPC_ERROR;
1134
goto out;
1135
}
1136
1137
i = atoi(vals[0]);
1138
if (i == 0)
1139
return KADM5_RPC_ERROR;
1140
1141
if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
1142
i |= (UF_ACCOUNTDISABLE|UF_LOCKOUT);
1143
else
1144
i &= ~(UF_ACCOUNTDISABLE|UF_LOCKOUT);
1145
if (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)
1146
i &= ~UF_DONT_REQUIRE_PREAUTH;
1147
else
1148
i |= UF_DONT_REQUIRE_PREAUTH;
1149
if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
1150
i |= UF_SMARTCARD_REQUIRED;
1151
else
1152
i &= UF_SMARTCARD_REQUIRED;
1153
if (entry->attributes & KRB5_KDB_DISALLOW_SVR)
1154
i &= ~UF_WORKSTATION_TRUST_ACCOUNT;
1155
else
1156
i |= UF_WORKSTATION_TRUST_ACCOUNT;
1157
1158
asprintf(&uaf[0], "%d", i);
1159
1160
a->mod_op = LDAP_MOD_REPLACE;
1161
a->mod_type = "userAccountControl";
1162
a->mod_values = uaf;
1163
a++;
1164
}
1165
1166
if (mask & KADM5_KVNO) {
1167
vals = ldap_get_values(CTX2LP(context), m0, "msDS-KeyVersionNumber");
1168
if (vals == NULL) {
1169
entry->kvno = 0;
1170
} else {
1171
asprintf(&kvno[0], "%d", entry->kvno);
1172
1173
a->mod_op = LDAP_MOD_REPLACE;
1174
a->mod_type = "msDS-KeyVersionNumber";
1175
a->mod_values = kvno;
1176
a++;
1177
}
1178
}
1179
1180
if (mask & KADM5_PRINC_EXPIRE_TIME) {
1181
long long wt;
1182
vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
1183
if (vals == NULL) {
1184
ret = KADM5_RPC_ERROR;
1185
goto out;
1186
}
1187
1188
wt = unix2nttime(entry->princ_expire_time);
1189
1190
asprintf(&tv[0], "%llu", wt);
1191
1192
a->mod_op = LDAP_MOD_REPLACE;
1193
a->mod_type = "accountExpires";
1194
a->mod_values = tv;
1195
a++;
1196
}
1197
1198
vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
1199
if (vals == NULL) {
1200
ret = KADM5_RPC_ERROR;
1201
goto out;
1202
}
1203
dn = vals[0];
1204
1205
attrs[a - &rattrs[0]] = NULL;
1206
1207
ret = ldap_modify_s(CTX2LP(context), dn, attrs);
1208
if (check_ldap(context, ret))
1209
return KADM5_RPC_ERROR;
1210
1211
out:
1212
if (m)
1213
ldap_msgfree(m);
1214
if (uaf[0])
1215
free(uaf[0]);
1216
if (kvno[0])
1217
free(kvno[0]);
1218
if (tv[0])
1219
free(tv[0]);
1220
return ret;
1221
#else
1222
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1223
return KADM5_RPC_ERROR;
1224
#endif
1225
}
1226
1227
static kadm5_ret_t
1228
kadm5_ad_randkey_principal(void *server_handle,
1229
krb5_principal principal,
1230
krb5_keyblock **keys,
1231
int *n_keys)
1232
{
1233
kadm5_ad_context *context = server_handle;
1234
1235
/*
1236
* random key
1237
*/
1238
1239
#ifdef OPENLDAP
1240
krb5_data result_code_string, result_string;
1241
int result_code, plen;
1242
kadm5_ret_t ret;
1243
char *password;
1244
1245
*keys = NULL;
1246
*n_keys = 0;
1247
1248
{
1249
char p[64];
1250
krb5_generate_random_block(p, sizeof(p));
1251
plen = base64_encode(p, sizeof(p), &password);
1252
if (plen < 0)
1253
return ENOMEM;
1254
}
1255
1256
ret = ad_get_cred(context, NULL);
1257
if (ret) {
1258
free(password);
1259
return ret;
1260
}
1261
1262
krb5_data_zero (&result_code_string);
1263
krb5_data_zero (&result_string);
1264
1265
ret = krb5_set_password_using_ccache (context->context,
1266
context->ccache,
1267
password,
1268
principal,
1269
&result_code,
1270
&result_code_string,
1271
&result_string);
1272
1273
krb5_data_free (&result_code_string);
1274
krb5_data_free (&result_string);
1275
1276
if (ret == 0) {
1277
1278
*keys = malloc(sizeof(**keys) * 1);
1279
if (*keys == NULL) {
1280
ret = ENOMEM;
1281
goto out;
1282
}
1283
*n_keys = 1;
1284
1285
ret = krb5_string_to_key(context->context,
1286
ENCTYPE_ARCFOUR_HMAC_MD5,
1287
password,
1288
principal,
1289
&(*keys)[0]);
1290
memset(password, 0, sizeof(password));
1291
if (ret) {
1292
free(*keys);
1293
*keys = NULL;
1294
*n_keys = 0;
1295
goto out;
1296
}
1297
}
1298
memset(password, 0, plen);
1299
free(password);
1300
out:
1301
return ret;
1302
#else
1303
*keys = NULL;
1304
*n_keys = 0;
1305
1306
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1307
return KADM5_RPC_ERROR;
1308
#endif
1309
}
1310
1311
static kadm5_ret_t
1312
kadm5_ad_rename_principal(void *server_handle,
1313
krb5_principal from,
1314
krb5_principal to)
1315
{
1316
kadm5_ad_context *context = server_handle;
1317
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1318
return KADM5_RPC_ERROR;
1319
}
1320
1321
static kadm5_ret_t
1322
kadm5_ad_chpass_principal_with_key(void *server_handle,
1323
krb5_principal princ,
1324
int n_key_data,
1325
krb5_key_data *key_data)
1326
{
1327
kadm5_ad_context *context = server_handle;
1328
krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
1329
return KADM5_RPC_ERROR;
1330
}
1331
1332
static void
1333
set_funcs(kadm5_ad_context *c)
1334
{
1335
#define SET(C, F) (C)->funcs.F = kadm5_ad_ ## F
1336
SET(c, chpass_principal);
1337
SET(c, chpass_principal_with_key);
1338
SET(c, create_principal);
1339
SET(c, delete_principal);
1340
SET(c, destroy);
1341
SET(c, flush);
1342
SET(c, get_principal);
1343
SET(c, get_principals);
1344
SET(c, get_privs);
1345
SET(c, modify_principal);
1346
SET(c, randkey_principal);
1347
SET(c, rename_principal);
1348
}
1349
1350
kadm5_ret_t
1351
kadm5_ad_init_with_password_ctx(krb5_context context,
1352
const char *client_name,
1353
const char *password,
1354
const char *service_name,
1355
kadm5_config_params *realm_params,
1356
unsigned long struct_version,
1357
unsigned long api_version,
1358
void **server_handle)
1359
{
1360
kadm5_ret_t ret;
1361
kadm5_ad_context *ctx;
1362
1363
ctx = malloc(sizeof(*ctx));
1364
if(ctx == NULL)
1365
return ENOMEM;
1366
memset(ctx, 0, sizeof(*ctx));
1367
set_funcs(ctx);
1368
1369
ctx->context = context;
1370
krb5_add_et_list (context, initialize_kadm5_error_table_r);
1371
1372
ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
1373
if(ret) {
1374
free(ctx);
1375
return ret;
1376
}
1377
1378
if(realm_params->mask & KADM5_CONFIG_REALM) {
1379
ret = 0;
1380
ctx->realm = strdup(realm_params->realm);
1381
if (ctx->realm == NULL)
1382
ret = ENOMEM;
1383
} else
1384
ret = krb5_get_default_realm(ctx->context, &ctx->realm);
1385
if (ret) {
1386
free(ctx);
1387
return ret;
1388
}
1389
1390
ctx->client_name = strdup(client_name);
1391
1392
if(password != NULL && *password != '\0')
1393
ret = ad_get_cred(ctx, password);
1394
else
1395
ret = ad_get_cred(ctx, NULL);
1396
if(ret) {
1397
kadm5_ad_destroy(ctx);
1398
return ret;
1399
}
1400
1401
#ifdef OPENLDAP
1402
ret = _kadm5_ad_connect(ctx);
1403
if (ret) {
1404
kadm5_ad_destroy(ctx);
1405
return ret;
1406
}
1407
#endif
1408
1409
*server_handle = ctx;
1410
return 0;
1411
}
1412
1413
kadm5_ret_t
1414
kadm5_ad_init_with_password(const char *client_name,
1415
const char *password,
1416
const char *service_name,
1417
kadm5_config_params *realm_params,
1418
unsigned long struct_version,
1419
unsigned long api_version,
1420
void **server_handle)
1421
{
1422
krb5_context context;
1423
kadm5_ret_t ret;
1424
kadm5_ad_context *ctx;
1425
1426
ret = krb5_init_context(&context);
1427
if (ret)
1428
return ret;
1429
ret = kadm5_ad_init_with_password_ctx(context,
1430
client_name,
1431
password,
1432
service_name,
1433
realm_params,
1434
struct_version,
1435
api_version,
1436
server_handle);
1437
if(ret) {
1438
krb5_free_context(context);
1439
return ret;
1440
}
1441
ctx = *server_handle;
1442
ctx->my_context = 1;
1443
return 0;
1444
}
1445
1446