Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/kpasswd/kpasswdd.c
34860 views
1
/*
2
* Copyright (c) 1997-2005 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
#include "kpasswd_locl.h"
35
RCSID("$Id$");
36
37
#include <kadm5/admin.h>
38
#ifdef HAVE_SYS_UN_H
39
#include <sys/un.h>
40
#endif
41
#include <hdb.h>
42
#include <kadm5/private.h>
43
44
static krb5_context context;
45
static krb5_log_facility *log_facility;
46
47
static struct getarg_strings addresses_str;
48
krb5_addresses explicit_addresses;
49
50
static sig_atomic_t exit_flag = 0;
51
52
static void
53
add_one_address (const char *str, int first)
54
{
55
krb5_error_code ret;
56
krb5_addresses tmp;
57
58
ret = krb5_parse_address (context, str, &tmp);
59
if (ret)
60
krb5_err (context, 1, ret, "parse_address `%s'", str);
61
if (first)
62
krb5_copy_addresses(context, &tmp, &explicit_addresses);
63
else
64
krb5_append_addresses(context, &explicit_addresses, &tmp);
65
krb5_free_addresses (context, &tmp);
66
}
67
68
static void
69
send_reply (int s,
70
struct sockaddr *sa,
71
int sa_size,
72
krb5_data *ap_rep,
73
krb5_data *rest)
74
{
75
struct msghdr msghdr;
76
struct iovec iov[3];
77
uint16_t len, ap_rep_len;
78
u_char header[6];
79
u_char *p;
80
81
if (ap_rep)
82
ap_rep_len = ap_rep->length;
83
else
84
ap_rep_len = 0;
85
86
len = 6 + ap_rep_len + rest->length;
87
p = header;
88
*p++ = (len >> 8) & 0xFF;
89
*p++ = (len >> 0) & 0xFF;
90
*p++ = 0;
91
*p++ = 1;
92
*p++ = (ap_rep_len >> 8) & 0xFF;
93
*p++ = (ap_rep_len >> 0) & 0xFF;
94
95
memset (&msghdr, 0, sizeof(msghdr));
96
msghdr.msg_name = (void *)sa;
97
msghdr.msg_namelen = sa_size;
98
msghdr.msg_iov = iov;
99
msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
100
#if 0
101
msghdr.msg_control = NULL;
102
msghdr.msg_controllen = 0;
103
#endif
104
105
iov[0].iov_base = (char *)header;
106
iov[0].iov_len = 6;
107
if (ap_rep_len) {
108
iov[1].iov_base = ap_rep->data;
109
iov[1].iov_len = ap_rep->length;
110
} else {
111
iov[1].iov_base = NULL;
112
iov[1].iov_len = 0;
113
}
114
iov[2].iov_base = rest->data;
115
iov[2].iov_len = rest->length;
116
117
if (sendmsg (s, &msghdr, 0) < 0)
118
krb5_warn (context, errno, "sendmsg");
119
}
120
121
static int
122
make_result (krb5_data *data,
123
uint16_t result_code,
124
const char *expl)
125
{
126
char *str;
127
krb5_data_zero (data);
128
129
data->length = asprintf (&str,
130
"%c%c%s",
131
(result_code >> 8) & 0xFF,
132
result_code & 0xFF,
133
expl);
134
135
if (str == NULL) {
136
krb5_warnx (context, "Out of memory generating error reply");
137
return 1;
138
}
139
data->data = str;
140
return 0;
141
}
142
143
static void
144
reply_error (krb5_realm realm,
145
int s,
146
struct sockaddr *sa,
147
int sa_size,
148
krb5_error_code error_code,
149
uint16_t result_code,
150
const char *expl)
151
{
152
krb5_error_code ret;
153
krb5_data error_data;
154
krb5_data e_data;
155
krb5_principal server = NULL;
156
157
if (make_result(&e_data, result_code, expl))
158
return;
159
160
if (realm) {
161
ret = krb5_make_principal (context, &server, realm,
162
"kadmin", "changepw", NULL);
163
if (ret) {
164
krb5_data_free (&e_data);
165
return;
166
}
167
}
168
169
ret = krb5_mk_error (context,
170
error_code,
171
NULL,
172
&e_data,
173
NULL,
174
server,
175
NULL,
176
NULL,
177
&error_data);
178
if (server)
179
krb5_free_principal(context, server);
180
krb5_data_free (&e_data);
181
if (ret) {
182
krb5_warn (context, ret, "Could not even generate error reply");
183
return;
184
}
185
send_reply (s, sa, sa_size, NULL, &error_data);
186
krb5_data_free (&error_data);
187
}
188
189
static void
190
reply_priv (krb5_auth_context auth_context,
191
int s,
192
struct sockaddr *sa,
193
int sa_size,
194
uint16_t result_code,
195
const char *expl)
196
{
197
krb5_error_code ret;
198
krb5_data krb_priv_data;
199
krb5_data ap_rep_data;
200
krb5_data e_data;
201
202
ret = krb5_mk_rep (context,
203
auth_context,
204
&ap_rep_data);
205
if (ret) {
206
krb5_warn (context, ret, "Could not even generate error reply");
207
return;
208
}
209
210
if (make_result(&e_data, result_code, expl))
211
return;
212
213
ret = krb5_mk_priv (context,
214
auth_context,
215
&e_data,
216
&krb_priv_data,
217
NULL);
218
krb5_data_free (&e_data);
219
if (ret) {
220
krb5_warn (context, ret, "Could not even generate error reply");
221
return;
222
}
223
send_reply (s, sa, sa_size, &ap_rep_data, &krb_priv_data);
224
krb5_data_free (&ap_rep_data);
225
krb5_data_free (&krb_priv_data);
226
}
227
228
/*
229
* Change the password for `principal', sending the reply back on `s'
230
* (`sa', `sa_size') to `pwd_data'.
231
*/
232
233
static void
234
change (krb5_auth_context auth_context,
235
krb5_principal admin_principal,
236
uint16_t version,
237
int s,
238
struct sockaddr *sa,
239
int sa_size,
240
krb5_data *in_data)
241
{
242
krb5_error_code ret;
243
char *client = NULL, *admin = NULL;
244
const char *pwd_reason;
245
kadm5_config_params conf;
246
void *kadm5_handle = NULL;
247
krb5_principal principal = NULL;
248
krb5_data *pwd_data = NULL;
249
char *tmp;
250
ChangePasswdDataMS chpw;
251
252
memset (&conf, 0, sizeof(conf));
253
memset(&chpw, 0, sizeof(chpw));
254
255
if (version == KRB5_KPASSWD_VERS_CHANGEPW) {
256
ret = krb5_copy_data(context, in_data, &pwd_data);
257
if (ret) {
258
krb5_warn (context, ret, "krb5_copy_data");
259
reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
260
"out out memory copying password");
261
return;
262
}
263
principal = admin_principal;
264
} else if (version == KRB5_KPASSWD_VERS_SETPW) {
265
size_t len;
266
267
ret = decode_ChangePasswdDataMS(in_data->data, in_data->length,
268
&chpw, &len);
269
if (ret) {
270
krb5_warn (context, ret, "decode_ChangePasswdDataMS");
271
reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
272
"malformed ChangePasswdData");
273
return;
274
}
275
276
277
ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data);
278
if (ret) {
279
krb5_warn (context, ret, "krb5_copy_data");
280
reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
281
"out out memory copying password");
282
goto out;
283
}
284
285
if (chpw.targname == NULL && chpw.targrealm != NULL) {
286
krb5_warn (context, ret, "kadm5_init_with_password_ctx");
287
reply_priv (auth_context, s, sa, sa_size,
288
KRB5_KPASSWD_MALFORMED,
289
"targrealm but not targname");
290
goto out;
291
}
292
293
if (chpw.targname) {
294
krb5_principal_data princ;
295
296
princ.name = *chpw.targname;
297
princ.realm = *chpw.targrealm;
298
if (princ.realm == NULL) {
299
ret = krb5_get_default_realm(context, &princ.realm);
300
301
if (ret) {
302
krb5_warnx (context,
303
"kadm5_init_with_password_ctx: "
304
"failed to allocate realm");
305
reply_priv (auth_context, s, sa, sa_size,
306
KRB5_KPASSWD_SOFTERROR,
307
"failed to allocate realm");
308
goto out;
309
}
310
}
311
ret = krb5_copy_principal(context, &princ, &principal);
312
if (*chpw.targrealm == NULL)
313
free(princ.realm);
314
if (ret) {
315
krb5_warn(context, ret, "krb5_copy_principal");
316
reply_priv(auth_context, s, sa, sa_size,
317
KRB5_KPASSWD_HARDERROR,
318
"failed to allocate principal");
319
goto out;
320
}
321
} else
322
principal = admin_principal;
323
} else {
324
krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto");
325
reply_priv (auth_context, s, sa, sa_size,
326
KRB5_KPASSWD_HARDERROR,
327
"Unknown protocol used");
328
return;
329
}
330
331
ret = krb5_unparse_name (context, admin_principal, &admin);
332
if (ret) {
333
krb5_warn (context, ret, "unparse_name failed");
334
reply_priv (auth_context, s, sa, sa_size,
335
KRB5_KPASSWD_HARDERROR, "out of memory error");
336
goto out;
337
}
338
339
conf.realm = principal->realm;
340
conf.mask |= KADM5_CONFIG_REALM;
341
342
ret = kadm5_init_with_password_ctx(context,
343
admin,
344
NULL,
345
KADM5_ADMIN_SERVICE,
346
&conf, 0, 0,
347
&kadm5_handle);
348
if (ret) {
349
krb5_warn (context, ret, "kadm5_init_with_password_ctx");
350
reply_priv (auth_context, s, sa, sa_size, 2,
351
"Internal error");
352
goto out;
353
}
354
355
ret = krb5_unparse_name(context, principal, &client);
356
if (ret) {
357
krb5_warn (context, ret, "unparse_name failed");
358
reply_priv (auth_context, s, sa, sa_size,
359
KRB5_KPASSWD_HARDERROR, "out of memory error");
360
goto out;
361
}
362
363
/*
364
* Check password quality if not changing as administrator
365
*/
366
367
if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {
368
369
pwd_reason = kadm5_check_password_quality (context, principal,
370
pwd_data);
371
if (pwd_reason != NULL ) {
372
krb5_warnx (context,
373
"%s didn't pass password quality check with error: %s",
374
client, pwd_reason);
375
reply_priv (auth_context, s, sa, sa_size,
376
KRB5_KPASSWD_SOFTERROR, pwd_reason);
377
goto out;
378
}
379
krb5_warnx (context, "Changing password for %s", client);
380
} else {
381
ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
382
principal);
383
if (ret) {
384
krb5_warn (context, ret,
385
"Check ACL failed for %s for changing %s password",
386
admin, client);
387
reply_priv (auth_context, s, sa, sa_size,
388
KRB5_KPASSWD_HARDERROR, "permission denied");
389
goto out;
390
}
391
krb5_warnx (context, "%s is changing password for %s", admin, client);
392
}
393
394
ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
395
if (ret) {
396
krb5_warn (context, ret, "malloc: out of memory");
397
reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR,
398
"Internal error");
399
goto out;
400
}
401
tmp = pwd_data->data;
402
tmp[pwd_data->length - 1] = '\0';
403
404
ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
405
krb5_free_data (context, pwd_data);
406
pwd_data = NULL;
407
if (ret) {
408
const char *str = krb5_get_error_message(context, ret);
409
krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str);
410
reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR,
411
str ? str : "Internal error");
412
krb5_free_error_message(context, str);
413
goto out;
414
}
415
reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS,
416
"Password changed");
417
out:
418
free_ChangePasswdDataMS(&chpw);
419
if (principal != admin_principal)
420
krb5_free_principal(context, principal);
421
if (admin)
422
free(admin);
423
if (client)
424
free(client);
425
if (pwd_data)
426
krb5_free_data(context, pwd_data);
427
if (kadm5_handle)
428
kadm5_destroy (kadm5_handle);
429
}
430
431
static int
432
verify (krb5_auth_context *auth_context,
433
krb5_realm *realms,
434
krb5_keytab keytab,
435
krb5_ticket **ticket,
436
krb5_data *out_data,
437
uint16_t *version,
438
int s,
439
struct sockaddr *sa,
440
int sa_size,
441
u_char *msg,
442
size_t len,
443
krb5_address *client_addr)
444
{
445
krb5_error_code ret;
446
uint16_t pkt_len, pkt_ver, ap_req_len;
447
krb5_data ap_req_data;
448
krb5_data krb_priv_data;
449
krb5_realm *r;
450
451
/*
452
* Only send an error reply if the request passes basic length
453
* verification. Otherwise, kpasswdd would reply to every UDP packet,
454
* allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP
455
* packet with a source address of another UDP service that also replies
456
* to every packet.
457
*
458
* Also suppress the error reply if ap_req_len is 0, which indicates
459
* either an invalid request or an error packet. An error packet may be
460
* the result of a ping-pong attacker pointing us at another kpasswdd.
461
*/
462
pkt_len = (msg[0] << 8) | (msg[1]);
463
pkt_ver = (msg[2] << 8) | (msg[3]);
464
ap_req_len = (msg[4] << 8) | (msg[5]);
465
if (pkt_len != len) {
466
krb5_warnx (context, "Strange len: %ld != %ld",
467
(long)pkt_len, (long)len);
468
return 1;
469
}
470
if (ap_req_len == 0) {
471
krb5_warnx (context, "Request is error packet (ap_req_len == 0)");
472
return 1;
473
}
474
if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW &&
475
pkt_ver != KRB5_KPASSWD_VERS_SETPW) {
476
krb5_warnx (context, "Bad version (%d)", pkt_ver);
477
reply_error (NULL, s, sa, sa_size, 0, 1, "Wrong program version");
478
return 1;
479
}
480
*version = pkt_ver;
481
482
ap_req_data.data = msg + 6;
483
ap_req_data.length = ap_req_len;
484
485
ret = krb5_rd_req (context,
486
auth_context,
487
&ap_req_data,
488
NULL,
489
keytab,
490
NULL,
491
ticket);
492
if (ret) {
493
krb5_warn (context, ret, "krb5_rd_req");
494
reply_error (NULL, s, sa, sa_size, ret, 3, "Authentication failed");
495
return 1;
496
}
497
498
/* verify realm and principal */
499
for (r = realms; *r != NULL; r++) {
500
krb5_principal principal;
501
krb5_boolean same;
502
503
ret = krb5_make_principal (context,
504
&principal,
505
*r,
506
"kadmin",
507
"changepw",
508
NULL);
509
if (ret)
510
krb5_err (context, 1, ret, "krb5_make_principal");
511
512
same = krb5_principal_compare(context, principal, (*ticket)->server);
513
krb5_free_principal(context, principal);
514
if (same == TRUE)
515
break;
516
}
517
if (*r == NULL) {
518
char *str;
519
krb5_unparse_name(context, (*ticket)->server, &str);
520
krb5_warnx (context, "client used not valid principal %s", str);
521
free(str);
522
reply_error (NULL, s, sa, sa_size, ret, 1,
523
"Bad request");
524
goto out;
525
}
526
527
if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) {
528
krb5_warnx (context, "server realm (%s) not same a client realm (%s)",
529
(*ticket)->server->realm, (*ticket)->client->realm);
530
reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
531
"Bad request");
532
goto out;
533
}
534
535
if (!(*ticket)->ticket.flags.initial) {
536
krb5_warnx (context, "initial flag not set");
537
reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
538
"Bad request");
539
goto out;
540
}
541
krb_priv_data.data = msg + 6 + ap_req_len;
542
krb_priv_data.length = len - 6 - ap_req_len;
543
544
/*
545
* Only enforce client addresses on on tickets with addresses. If
546
* its addressless, we are guessing its behind NAT and really
547
* can't know this information.
548
*/
549
550
if ((*ticket)->ticket.caddr && (*ticket)->ticket.caddr->len > 0) {
551
ret = krb5_auth_con_setaddrs (context, *auth_context,
552
NULL, client_addr);
553
if (ret) {
554
krb5_warn (context, ret, "krb5_auth_con_setaddr(this)");
555
goto out;
556
}
557
}
558
559
ret = krb5_rd_priv (context,
560
*auth_context,
561
&krb_priv_data,
562
out_data,
563
NULL);
564
565
if (ret) {
566
krb5_warn (context, ret, "krb5_rd_priv");
567
reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 3,
568
"Bad request");
569
goto out;
570
}
571
return 0;
572
out:
573
krb5_free_ticket (context, *ticket);
574
ticket = NULL;
575
return 1;
576
}
577
578
static void
579
process (krb5_realm *realms,
580
krb5_keytab keytab,
581
int s,
582
krb5_address *this_addr,
583
struct sockaddr *sa,
584
int sa_size,
585
u_char *msg,
586
int len)
587
{
588
krb5_error_code ret;
589
krb5_auth_context auth_context = NULL;
590
krb5_data out_data;
591
krb5_ticket *ticket;
592
krb5_address other_addr;
593
uint16_t version;
594
595
memset(&other_addr, 0, sizeof(other_addr));
596
krb5_data_zero (&out_data);
597
598
ret = krb5_auth_con_init (context, &auth_context);
599
if (ret) {
600
krb5_warn (context, ret, "krb5_auth_con_init");
601
return;
602
}
603
604
krb5_auth_con_setflags (context, auth_context,
605
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
606
607
ret = krb5_sockaddr2address (context, sa, &other_addr);
608
if (ret) {
609
krb5_warn (context, ret, "krb5_sockaddr2address");
610
goto out;
611
}
612
613
ret = krb5_auth_con_setaddrs (context, auth_context, this_addr, NULL);
614
if (ret) {
615
krb5_warn (context, ret, "krb5_auth_con_setaddr(this)");
616
goto out;
617
}
618
619
if (verify (&auth_context, realms, keytab, &ticket, &out_data,
620
&version, s, sa, sa_size, msg, len, &other_addr) == 0)
621
{
622
/*
623
* We always set the client_addr, to assume that the client
624
* can ignore it if it choose to do so (just the server does
625
* so for addressless tickets).
626
*/
627
ret = krb5_auth_con_setaddrs (context, auth_context,
628
this_addr, &other_addr);
629
if (ret) {
630
krb5_warn (context, ret, "krb5_auth_con_setaddr(other)");
631
goto out;
632
}
633
634
change (auth_context,
635
ticket->client,
636
version,
637
s,
638
sa, sa_size,
639
&out_data);
640
memset (out_data.data, 0, out_data.length);
641
krb5_free_ticket (context, ticket);
642
}
643
644
out:
645
krb5_free_address(context, &other_addr);
646
krb5_data_free(&out_data);
647
krb5_auth_con_free(context, auth_context);
648
}
649
650
static int
651
doit (krb5_keytab keytab, int port)
652
{
653
krb5_error_code ret;
654
int *sockets;
655
int maxfd;
656
krb5_realm *realms;
657
krb5_addresses addrs;
658
unsigned n, i;
659
fd_set real_fdset;
660
struct sockaddr_storage __ss;
661
struct sockaddr *sa = (struct sockaddr *)&__ss;
662
663
ret = krb5_get_default_realms(context, &realms);
664
if (ret)
665
krb5_err (context, 1, ret, "krb5_get_default_realms");
666
667
if (explicit_addresses.len) {
668
addrs = explicit_addresses;
669
} else {
670
ret = krb5_get_all_server_addrs (context, &addrs);
671
if (ret)
672
krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
673
}
674
n = addrs.len;
675
676
sockets = malloc (n * sizeof(*sockets));
677
if (sockets == NULL)
678
krb5_errx (context, 1, "out of memory");
679
maxfd = -1;
680
FD_ZERO(&real_fdset);
681
for (i = 0; i < n; ++i) {
682
krb5_socklen_t sa_size = sizeof(__ss);
683
684
krb5_addr2sockaddr (context, &addrs.val[i], sa, &sa_size, port);
685
686
sockets[i] = socket (sa->sa_family, SOCK_DGRAM, 0);
687
if (sockets[i] < 0)
688
krb5_err (context, 1, errno, "socket");
689
if (bind (sockets[i], sa, sa_size) < 0) {
690
char str[128];
691
size_t len;
692
int save_errno = errno;
693
694
ret = krb5_print_address (&addrs.val[i], str, sizeof(str), &len);
695
if (ret)
696
strlcpy(str, "unknown address", sizeof(str));
697
krb5_warn (context, save_errno, "bind(%s)", str);
698
continue;
699
}
700
maxfd = max (maxfd, sockets[i]);
701
if (maxfd >= FD_SETSIZE)
702
krb5_errx (context, 1, "fd too large");
703
FD_SET(sockets[i], &real_fdset);
704
}
705
if (maxfd == -1)
706
krb5_errx (context, 1, "No sockets!");
707
708
while(exit_flag == 0) {
709
krb5_ssize_t retx;
710
fd_set fdset = real_fdset;
711
712
retx = select (maxfd + 1, &fdset, NULL, NULL, NULL);
713
if (retx < 0) {
714
if (errno == EINTR)
715
continue;
716
else
717
krb5_err (context, 1, errno, "select");
718
}
719
for (i = 0; i < n; ++i)
720
if (FD_ISSET(sockets[i], &fdset)) {
721
u_char buf[BUFSIZ];
722
socklen_t addrlen = sizeof(__ss);
723
724
retx = recvfrom(sockets[i], buf, sizeof(buf), 0,
725
sa, &addrlen);
726
if (retx < 0) {
727
if(errno == EINTR)
728
break;
729
else
730
krb5_err (context, 1, errno, "recvfrom");
731
}
732
733
process (realms, keytab, sockets[i],
734
&addrs.val[i],
735
sa, addrlen,
736
buf, retx);
737
}
738
}
739
740
for (i = 0; i < n; ++i)
741
close(sockets[i]);
742
free(sockets);
743
744
krb5_free_addresses (context, &addrs);
745
krb5_free_host_realm (context, realms);
746
krb5_free_context (context);
747
return 0;
748
}
749
750
static RETSIGTYPE
751
sigterm(int sig)
752
{
753
exit_flag = 1;
754
}
755
756
static const char *check_library = NULL;
757
static const char *check_function = NULL;
758
static getarg_strings policy_libraries = { 0, NULL };
759
static char sHDB[] = "HDB:";
760
static char *keytab_str = sHDB;
761
static char *realm_str;
762
static int version_flag;
763
static int help_flag;
764
static char *port_str;
765
static char *config_file;
766
767
struct getargs args[] = {
768
#ifdef HAVE_DLOPEN
769
{ "check-library", 0, arg_string, &check_library,
770
"library to load password check function from", "library" },
771
{ "check-function", 0, arg_string, &check_function,
772
"password check function to load", "function" },
773
{ "policy-libraries", 0, arg_strings, &policy_libraries,
774
"password check function to load", "function" },
775
#endif
776
{ "addresses", 0, arg_strings, &addresses_str,
777
"addresses to listen on", "list of addresses" },
778
{ "keytab", 'k', arg_string, &keytab_str,
779
"keytab to get authentication key from", "kspec" },
780
{ "config-file", 'c', arg_string, &config_file, NULL, NULL },
781
{ "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
782
{ "port", 'p', arg_string, &port_str, "port", NULL },
783
{ "version", 0, arg_flag, &version_flag, NULL, NULL },
784
{ "help", 0, arg_flag, &help_flag, NULL, NULL }
785
};
786
int num_args = sizeof(args) / sizeof(args[0]);
787
788
int
789
main (int argc, char **argv)
790
{
791
krb5_keytab keytab;
792
krb5_error_code ret;
793
char **files;
794
int port, i;
795
796
krb5_program_setup(&context, argc, argv, args, num_args, NULL);
797
798
if(help_flag)
799
krb5_std_usage(0, args, num_args);
800
if(version_flag) {
801
print_version(NULL);
802
exit(0);
803
}
804
805
if (config_file == NULL) {
806
asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
807
if (config_file == NULL)
808
errx(1, "out of memory");
809
}
810
811
ret = krb5_prepend_config_files_default(config_file, &files);
812
if (ret)
813
krb5_err(context, 1, ret, "getting configuration files");
814
815
ret = krb5_set_config_files(context, files);
816
krb5_free_config_files(files);
817
if (ret)
818
krb5_err(context, 1, ret, "reading configuration files");
819
820
if(realm_str)
821
krb5_set_default_realm(context, realm_str);
822
823
krb5_openlog (context, "kpasswdd", &log_facility);
824
krb5_set_warn_dest(context, log_facility);
825
826
if (port_str != NULL) {
827
struct servent *s = roken_getservbyname (port_str, "udp");
828
829
if (s != NULL)
830
port = s->s_port;
831
else {
832
char *ptr;
833
834
port = strtol (port_str, &ptr, 10);
835
if (port == 0 && ptr == port_str)
836
krb5_errx (context, 1, "bad port `%s'", port_str);
837
port = htons(port);
838
}
839
} else
840
port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT);
841
842
ret = krb5_kt_register(context, &hdb_kt_ops);
843
if(ret)
844
krb5_err(context, 1, ret, "krb5_kt_register");
845
846
ret = krb5_kt_resolve(context, keytab_str, &keytab);
847
if(ret)
848
krb5_err(context, 1, ret, "%s", keytab_str);
849
850
kadm5_setup_passwd_quality_check (context, check_library, check_function);
851
852
for (i = 0; i < policy_libraries.num_strings; i++) {
853
ret = kadm5_add_passwd_quality_verifier(context,
854
policy_libraries.strings[i]);
855
if (ret)
856
krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
857
}
858
ret = kadm5_add_passwd_quality_verifier(context, NULL);
859
if (ret)
860
krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
861
862
863
explicit_addresses.len = 0;
864
865
if (addresses_str.num_strings) {
866
int j;
867
868
for (j = 0; j < addresses_str.num_strings; ++j)
869
add_one_address (addresses_str.strings[j], j == 0);
870
free_getarg_strings (&addresses_str);
871
} else {
872
char **foo = krb5_config_get_strings (context, NULL,
873
"kdc", "addresses", NULL);
874
875
if (foo != NULL) {
876
add_one_address (*foo++, TRUE);
877
while (*foo)
878
add_one_address (*foo++, FALSE);
879
}
880
}
881
882
#ifdef HAVE_SIGACTION
883
{
884
struct sigaction sa;
885
886
sa.sa_flags = 0;
887
sa.sa_handler = sigterm;
888
sigemptyset(&sa.sa_mask);
889
890
sigaction(SIGINT, &sa, NULL);
891
sigaction(SIGTERM, &sa, NULL);
892
}
893
#else
894
signal(SIGINT, sigterm);
895
signal(SIGTERM, sigterm);
896
#endif
897
898
pidfile(NULL);
899
900
return doit (keytab, port);
901
}
902
903