2 * Copyright (c) 1997 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Kungliga Tekniska
20 * Högskolan and its contributors.
22 * 4. Neither the name of the Institute nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include "kpasswd_locl.h"
43 static krb5_context context
;
44 static krb5_log_facility
*log_facility
;
46 static sig_atomic_t exit_flag
= 0;
48 static krb5_data msater_key
;
49 static int master_key_set
= 0;
51 #define KPASSWDD_LOG_ERR 0
52 #define KPASSWDD_LOG_INFO 1
55 syslog_and_die (const char *m
, ...)
60 krb5_vlog (context
, log_facility
, KPASSWDD_LOG_ERR
, m
, args
);
65 static char *database
= HDB_DEFAULT_DB
;
77 u_int16_t len
, ap_rep_len
;
82 ap_rep_len
= ap_rep
->length
;
86 len
= 6 + ap_rep_len
+ rest
->length
;
88 *p
++ = (len
>> 8) & 0xFF;
89 *p
++ = (len
>> 0) & 0xFF;
92 *p
++ = (ap_rep_len
>> 8) & 0xFF;
93 *p
++ = (ap_rep_len
>> 0) & 0xFF;
95 memset (&msghdr
, 0, sizeof(msghdr
));
96 msghdr
.msg_name
= (void *)sa
;
97 msghdr
.msg_namelen
= sa_size
;
99 msghdr
.msg_iovlen
= sizeof(iov
)/sizeof(*iov
);
101 msghdr
.msg_control
= NULL
;
102 msghdr
.msg_controllen
= 0;
105 iov
[0].iov_base
= (char *)header
;
108 iov
[1].iov_base
= ap_rep
->data
;
109 iov
[1].iov_len
= ap_rep
->length
;
111 iov
[1].iov_base
= NULL
;
114 iov
[2].iov_base
= rest
->data
;
115 iov
[2].iov_len
= rest
->length
;
117 if (sendmsg (s
, &msghdr
, 0) < 0)
118 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
124 make_result (krb5_data
*data
,
125 u_int16_t result_code
,
128 krb5_data_zero (data
);
130 data
->length
= asprintf ((char **)&data
->data
,
132 (result_code
>> 8) & 0xFF,
136 if (data
->data
== NULL
) {
137 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
138 "Out of memory generating error reply");
145 reply_error (krb5_principal server
,
149 krb5_error_code error_code
,
150 u_int16_t result_code
,
154 krb5_data error_data
;
157 if (make_result(&e_data
, result_code
, expl
))
160 ret
= krb5_mk_error (context
,
168 krb5_data_free (&e_data
);
170 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
171 "Could not even generate error reply: %s",
172 krb5_get_err_text (context
, ret
));
175 send_reply (s
, sa
, sa_size
, NULL
, &error_data
);
176 krb5_data_free (&error_data
);
180 reply_priv (krb5_auth_context auth_context
,
184 u_int16_t result_code
,
188 krb5_data krb_priv_data
;
189 krb5_data ap_rep_data
;
192 ret
= krb5_mk_rep (context
,
196 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
197 "Could not even generate error reply: %s",
198 krb5_get_err_text (context
, ret
));
202 if (make_result(&e_data
, result_code
, expl
))
205 ret
= krb5_mk_priv (context
,
210 krb5_data_free (&e_data
);
212 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
213 "Could not even generate error reply: %s",
214 krb5_get_err_text (context
, ret
));
217 send_reply (s
, sa
, sa_size
, &ap_rep_data
, &krb_priv_data
);
218 krb5_data_free (&ap_rep_data
);
219 krb5_data_free (&krb_priv_data
);
223 passwd_quality_check (krb5_data
*pwd
)
226 return "Password too short";
232 change (krb5_auth_context auth_context
,
233 krb5_principal principal
,
243 krb5_keyblock new_keyblock
, *old_keyblock
;
246 krb5_unparse_name (context
, principal
, &c
);
248 krb5_log (context
, log_facility
, KPASSWDD_LOG_INFO
,
249 "Changing password for %s", c
);
252 pwd_reason
= passwd_quality_check (pwd_data
);
253 if (pwd_reason
!= NULL
) {
254 krb5_log (context
, log_facility
,
255 KPASSWDD_LOG_ERR
, pwd_reason
);
256 reply_priv (auth_context
, s
, sa
, sa_size
, 4, pwd_reason
);
260 ret
= db
->open(context
, db
, O_RDWR
, 0600);
262 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
263 "hdb_open: %s", krb5_get_err_text(context
, ret
));
264 reply_priv (auth_context
, s
, sa
, sa_size
, 2, "hdb_open failed");
268 krb5_copy_principal (context
, principal
, &ent
.principal
);
270 ret
= db
->fetch (context
, db
, &ent
);
273 case HDB_ERR_NOENTRY
:
274 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
275 "not found in database");
276 reply_priv (auth_context
, s
, sa
, sa_size
, 2,
277 "entry not found in database");
282 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
283 "dbfetch: %s", krb5_get_err_text(context
, ret
));
284 reply_priv (auth_context
, s
, sa
, sa_size
, 2,
290 * Compare with the first key to see if it already has been
291 * changed. If it hasn't, store the new key in the database and
292 * string2key all the rest of them.
295 krb5_data_zero (&salt
);
296 krb5_get_salt (principal
, &salt
);
297 memset (&new_keyblock
, 0, sizeof(new_keyblock
));
298 old_keyblock
= &ent
.keys
.val
[0].key
;
299 krb5_string_to_key_data (pwd_data
, &salt
, old_keyblock
->keytype
, /* XXX */
302 if (new_keyblock
.keytype
== old_keyblock
->keytype
303 && new_keyblock
.keyvalue
.length
== old_keyblock
->keyvalue
.length
304 && memcmp (new_keyblock
.keyvalue
.data
,
305 old_keyblock
->keyvalue
.data
,
306 new_keyblock
.keyvalue
.length
) == 0) {
312 free_EncryptionKey (old_keyblock
);
313 memset (old_keyblock
, 0, sizeof(*old_keyblock
));
314 old_keyblock
->keytype
= new_keyblock
.keytype
;
315 krb5_data_copy (&old_keyblock
->keyvalue
,
316 new_keyblock
.keyvalue
.data
,
317 new_keyblock
.keyvalue
.length
);
319 for(i
= 1; i
< ent
.keys
.len
; ++i
) {
320 free_Key (&ent
.keys
.val
[i
]);
321 krb5_string_to_key_data (pwd_data
,
323 ent
.keys
.val
[i
].key
.keytype
,
324 &ent
.keys
.val
[i
].key
);
328 e
= malloc(sizeof(*e
));
329 e
->time
= time(NULL
);
330 krb5_copy_principal (context
, principal
, &e
->principal
);
331 if (ent
.modified_by
) {
332 free_Event (ent
.modified_by
);
333 free (ent
.modified_by
);
337 *ent
.pw_end
= e
->time
+ 3600; /* XXX - Change here! */
338 ret
= db
->store (context
, db
, 1, &ent
);
340 krb5_data_free (&salt
);
341 krb5_free_keyblock (context
, &new_keyblock
);
344 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
345 "dbstore: %s", krb5_get_err_text (context
, ret
));
346 reply_priv (auth_context
, s
, sa
, sa_size
, 2,
349 reply_priv (auth_context
, s
, sa
, sa_size
, 0, "password changed");
352 hdb_free_entry (context
, &ent
);
353 db
->close (context
, db
);
357 verify (krb5_auth_context
*auth_context
,
358 krb5_principal server
,
359 krb5_ticket
**ticket
,
368 u_int16_t pkt_len
, pkt_ver
, ap_req_len
;
369 krb5_data ap_req_data
;
370 krb5_data krb_priv_data
;
372 pkt_len
= (msg
[0] << 8) | (msg
[1]);
373 pkt_ver
= (msg
[2] << 8) | (msg
[3]);
374 ap_req_len
= (msg
[4] << 8) | (msg
[5]);
375 if (pkt_len
!= len
) {
376 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
377 "Strange len: %d != %d", pkt_len
, len
);
378 reply_error (server
, s
, sa
, sa_size
, 0, 1, "bad length");
381 if (pkt_ver
!= 0x0001) {
382 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
383 "Bad version (%d)", pkt_ver
);
384 reply_error (server
, s
, sa
, sa_size
, 0, 1, "bad version");
388 ap_req_data
.data
= msg
+ 6;
389 ap_req_data
.length
= ap_req_len
;
391 ret
= krb5_rd_req (context
,
399 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
, "krb5_rd_req: %s",
400 krb5_get_err_text(context
, ret
));
401 reply_error (server
, s
, sa
, sa_size
, ret
, 3, "rd_req failed");
405 if (!(*ticket
)->ticket
.flags
.initial
) {
406 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
407 "initial flag not set");
408 reply_error (server
, s
, sa
, sa_size
, ret
, 1,
409 "initial flag not set");
412 krb_priv_data
.data
= msg
+ 6 + ap_req_len
;
413 krb_priv_data
.length
= len
- 6 - ap_req_len
;
415 ret
= krb5_rd_priv (context
,
422 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
, "krb5_rd_priv: %s",
423 krb5_get_err_text(context
, ret
));
424 reply_error (server
, s
, sa
, sa_size
, ret
, 3, "rd_priv failed");
429 krb5_free_ticket (context
, *ticket
);
434 process (krb5_principal server
,
436 krb5_address
*this_addr
,
443 krb5_auth_context auth_context
= NULL
;
446 krb5_address other_addr
;
448 krb5_data_zero (&out_data
);
450 ret
= krb5_auth_con_init (context
, &auth_context
);
452 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
453 "krb5_auth_con_init: %s",
454 krb5_get_err_text(context
, ret
));
458 krb5_auth_con_setflags (context
, auth_context
,
459 KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
461 ret
= krb5_sockaddr2address (sa
, &other_addr
);
463 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
464 "krb5_sockaddr2address: %s",
465 krb5_get_err_text(context
, ret
));
469 ret
= krb5_auth_con_setaddrs (context
,
473 krb5_free_address (context
, &other_addr
);
475 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
476 "krb5_auth_con_setaddr: %s",
477 krb5_get_err_text(context
, ret
));
481 if (verify (&auth_context
, server
, &ticket
, &out_data
,
482 s
, sa
, sa_size
, msg
, len
) == 0) {
483 change (auth_context
,
488 krb5_free_ticket (context
, ticket
);
493 krb5_data_free (&out_data
);
494 krb5_auth_con_free (context
, auth_context
);
501 krb5_principal server
;
505 krb5_addresses addrs
;
512 sa_max_size
= krb5_max_sockaddr_size ();
513 sa_buf
= malloc (sa_max_size
);
515 syslog_and_die ("out of memory");
516 sa
= (struct sockaddr
*)sa_buf
;
518 ret
= krb5_get_default_realm (context
, &realm
);
520 syslog_and_die ("krb5_get_default_realm: %s",
521 krb5_get_err_text(context
, ret
));
523 ret
= krb5_build_principal (context
,
531 syslog_and_die ("krb5_build_principal_ext: %s",
532 krb5_get_err_text(context
, ret
));
536 ret
= krb5_get_all_client_addrs (&addrs
);
538 syslog_and_die ("krb5_get_all_clients_addrs: %s",
539 krb5_get_err_text(context
, ret
));
543 sockets
= malloc (n
* sizeof(*sockets
));
545 FD_ZERO(&real_fdset
);
546 for (i
= 0; i
< n
; ++i
) {
549 krb5_addr2sockaddr (&addrs
.val
[i
], sa
, &sa_size
, port
);
551 sockets
[i
] = socket (sa
->sa_family
, SOCK_DGRAM
, 0);
553 syslog_and_die ("socket: %m");
555 if (bind (sockets
[i
], sa
, sa_size
) < 0)
556 syslog_and_die ("bind: %m");
557 maxfd
= max (maxfd
, sockets
[i
]);
558 FD_SET(sockets
[i
], &real_fdset
);
561 while(exit_flag
== 0) {
563 struct fd_set fdset
= real_fdset
;
565 ret
= select (maxfd
+ 1, &fdset
, NULL
, NULL
, NULL
);
570 syslog_and_die ("select: %m");
571 for (i
= 0; i
< n
; ++i
)
572 if (FD_ISSET(sockets
[i
], &fdset
)) {
574 int addrlen
= sa_max_size
;
576 ret
= recvfrom (sockets
[i
], buf
, sizeof(buf
), 0,
582 syslog_and_die ("recvfrom: %m");
584 process (server
, sockets
[i
],
590 krb5_free_addresses (context
, &addrs
);
591 krb5_free_principal (context
, server
);
592 krb5_free_context (context
);
604 main (int argc
, char **argv
)
607 char *keyfile
= NULL
;
609 krb5_init_context (&context
);
611 set_progname (argv
[0]);
612 krb5_openlog (context
, "kpasswdd", &log_facility
);
614 ret
= hdb_create (context
, &db
, database
);
616 syslog_and_die ("Failed to open database %s: %s",
617 database
, krb5_get_err_text(context
, ret
));
618 ret
= hdb_set_master_key(context
, db
, keyfile
);
620 syslog_and_die ("Failed to set master key: %s",
621 krb5_get_err_text(context
, ret
));
623 #ifdef HAVE_SIGACTION
628 sa
.sa_handler
= sigterm
;
629 sigemptyset(&sa
.sa_mask
);
631 sigaction(SIGINT
, &sa
, NULL
);
634 signal(SIGINT
, sigterm
);
637 return doit (krb5_getportbyname (context
, "kpasswd", "udp", KPASSWD_PORT
));