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 #define KPASSWDD_LOG_ERR 0
49 #define KPASSWDD_LOG_INFO 1
52 syslog_and_die (const char *m
, ...)
57 krb5_vlog (context
, log_facility
, KPASSWDD_LOG_ERR
, m
, args
);
62 static char *database
= HDB_DEFAULT_DB
;
66 struct sockaddr_in
*addr
,
72 u_int16_t len
, ap_rep_len
;
77 ap_rep_len
= ap_rep
->length
;
81 len
= 6 + ap_rep_len
+ rest
->length
;
83 *p
++ = (len
>> 8) & 0xFF;
84 *p
++ = (len
>> 0) & 0xFF;
87 *p
++ = (ap_rep_len
>> 8) & 0xFF;
88 *p
++ = (ap_rep_len
>> 0) & 0xFF;
90 memset (&msghdr
, 0, sizeof(msghdr
));
91 msghdr
.msg_name
= (void *)addr
;
92 msghdr
.msg_namelen
= sizeof(*addr
);
94 msghdr
.msg_iovlen
= sizeof(iov
)/sizeof(*iov
);
96 msghdr
.msg_control
= NULL
;
97 msghdr
.msg_controllen
= 0;
100 iov
[0].iov_base
= header
;
103 iov
[1].iov_base
= ap_rep
->data
;
104 iov
[1].iov_len
= ap_rep
->length
;
106 iov
[1].iov_base
= NULL
;
109 iov
[2].iov_base
= rest
->data
;
110 iov
[2].iov_len
= rest
->length
;
112 if (sendmsg (s
, &msghdr
, 0) < 0)
113 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
119 make_result (krb5_data
*data
,
120 u_int16_t result_code
,
123 krb5_data_zero (data
);
125 data
->length
= asprintf ((char **)&data
->data
,
127 (result_code
>> 8) & 0xFF,
131 if (data
->data
== NULL
) {
132 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
133 "Out of memory generating error reply");
140 reply_error (krb5_principal server
,
142 struct sockaddr_in
*addr
,
143 krb5_error_code error_code
,
144 u_int16_t result_code
,
148 krb5_data error_data
;
151 if (make_result(&e_data
, result_code
, expl
))
154 ret
= krb5_mk_error (context
,
162 krb5_data_free (&e_data
);
164 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
165 "Could not even generate error reply: %s",
166 krb5_get_err_text (context
, ret
));
169 send_reply (s
, addr
, NULL
, &error_data
);
170 krb5_data_free (&error_data
);
174 reply_priv (krb5_auth_context auth_context
,
176 struct sockaddr_in
*addr
,
177 u_int16_t result_code
,
181 krb5_data krb_priv_data
;
182 krb5_data ap_rep_data
;
185 ret
= krb5_mk_rep (context
,
189 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
190 "Could not even generate error reply: %s",
191 krb5_get_err_text (context
, ret
));
195 if (make_result(&e_data
, result_code
, expl
))
198 ret
= krb5_mk_priv (context
,
203 krb5_data_free (&e_data
);
205 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
206 "Could not even generate error reply: %s",
207 krb5_get_err_text (context
, ret
));
210 send_reply (s
, addr
, &ap_rep_data
, &krb_priv_data
);
211 krb5_data_free (&ap_rep_data
);
212 krb5_data_free (&krb_priv_data
);
216 change (krb5_auth_context auth_context
,
217 krb5_principal principal
,
219 struct sockaddr_in
*addr
,
227 krb5_keyblock new_keyblock
, *old_keyblock
;
229 krb5_unparse_name (context
, principal
, &c
);
231 krb5_log (context
, log_facility
, KPASSWDD_LOG_INFO
,
232 "Changing password for %s", c
);
235 if (pwd_data
->length
< 6) { /* XXX */
236 krb5_log (context
, log_facility
,
237 KPASSWDD_LOG_ERR
, "Password too short");
238 reply_priv (auth_context
, s
, addr
, 4, "password too short");
242 ret
= hdb_open (context
, &db
, database
, O_RDWR
, 0600);
244 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
245 "hdb_open: %s", krb5_get_err_text(context
, ret
));
246 reply_priv (auth_context
, s
, addr
, 2, "hdb_open failed");
250 krb5_copy_principal (context
, principal
, &ent
.principal
);
252 ret
= db
->fetch (context
, db
, &ent
);
255 case HDB_ERR_NOENTRY
:
256 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
257 "not found in database");
258 reply_priv (auth_context
, s
, addr
, 2,
259 "entry not found in database");
264 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
265 "dbfetch: %s", krb5_get_err_text(context
, ret
));
266 reply_priv (auth_context
, s
, addr
, 2,
271 krb5_data_zero (&salt
);
272 krb5_get_salt (principal
, &salt
);
273 memset (&new_keyblock
, 0, sizeof(new_keyblock
));
274 krb5_string_to_key_data (pwd_data
, &salt
, &new_keyblock
);
275 krb5_data_free (&salt
);
276 old_keyblock
= &ent
.keys
.val
[0].key
;
278 if (new_keyblock
.keytype
== old_keyblock
->keytype
279 && new_keyblock
.keyvalue
.length
== old_keyblock
->keyvalue
.length
280 && memcmp (new_keyblock
.keyvalue
.data
,
281 old_keyblock
->keyvalue
.data
,
282 new_keyblock
.keyvalue
.length
) == 0) {
287 free_EncryptionKey (old_keyblock
);
288 memset (old_keyblock
, 0, sizeof(*old_keyblock
));
289 old_keyblock
->keytype
= new_keyblock
.keytype
;
290 krb5_data_copy (&old_keyblock
->keyvalue
,
291 new_keyblock
.keyvalue
.data
,
292 new_keyblock
.keyvalue
.length
);
294 e
= malloc(sizeof(*e
));
295 e
->time
= time(NULL
);
296 krb5_copy_principal (context
, principal
, &e
->principal
);
297 if (ent
.modified_by
) {
298 free_Event (ent
.modified_by
);
299 free (ent
.modified_by
);
303 *ent
.pw_end
= e
->time
+ 3600; /* XXX - Change here! */
304 ret
= db
->store (context
, db
, &ent
);
306 krb5_free_keyblock (context
, &new_keyblock
);
309 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
310 "dbstore: %s", krb5_get_err_text (context
, ret
));
311 reply_priv (auth_context
, s
, addr
, 2,
314 reply_priv (auth_context
, s
, addr
, 0, "password changed");
317 hdb_free_entry (context
, &ent
);
318 db
->close (context
, db
);
322 verify (krb5_auth_context
*auth_context
,
323 krb5_principal server
,
324 krb5_ticket
**ticket
,
327 struct sockaddr_in
*addr
,
332 u_int16_t pkt_len
, pkt_ver
, ap_req_len
;
333 krb5_data ap_req_data
;
334 krb5_data krb_priv_data
;
336 pkt_len
= (msg
[0] << 8) | (msg
[1]);
337 pkt_ver
= (msg
[2] << 8) | (msg
[3]);
338 ap_req_len
= (msg
[4] << 8) | (msg
[5]);
339 if (pkt_len
!= len
) {
340 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
341 "Strange len: %d != %d", pkt_len
, len
);
342 reply_error (server
, s
, addr
, 0, 1, "bad length");
345 if (pkt_ver
!= 0x0001) {
346 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
347 "Bad version (%d)", pkt_ver
);
348 reply_error (server
, s
, addr
, 0, 1, "bad version");
352 ap_req_data
.data
= msg
+ 6;
353 ap_req_data
.length
= ap_req_len
;
355 ret
= krb5_rd_req (context
,
363 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
, "krb5_rd_req: %s",
364 krb5_get_err_text(context
, ret
));
365 reply_error (server
, s
, addr
, ret
, 3, "rd_req failed");
369 if (!(*ticket
)->ticket
.flags
.initial
) {
370 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
371 "initial flag not set");
372 reply_error (server
, s
, addr
, ret
, 1,
373 "initial flag not set");
376 krb_priv_data
.data
= msg
+ 6 + ap_req_len
;
377 krb_priv_data
.length
= len
- 6 - ap_req_len
;
379 ret
= krb5_rd_priv (context
,
386 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
, "krb5_rd_priv: %s",
387 krb5_get_err_text(context
, ret
));
388 reply_error (server
, s
, addr
, ret
, 3, "rd_priv failed");
393 krb5_free_ticket (context
, *ticket
);
398 process (krb5_principal server
,
401 struct sockaddr_in
*other_addr
,
406 krb5_auth_context auth_context
= NULL
;
409 krb5_address remote_addr
, local_addr
;
411 krb5_data_zero (&out_data
);
413 ret
= krb5_auth_con_init (context
, &auth_context
);
415 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
416 "krb5_auth_con_init: %s",
417 krb5_get_err_text(context
, ret
));
421 krb5_auth_con_setflags (context
, auth_context
,
422 KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
424 local_addr
.addr_type
= AF_INET
;
425 local_addr
.address
.length
= sizeof(other_addr
->sin_addr
);
426 local_addr
.address
.data
= this_addr
;
428 remote_addr
.addr_type
= AF_INET
;
429 remote_addr
.address
.length
= sizeof(other_addr
->sin_addr
);
430 remote_addr
.address
.data
= &other_addr
->sin_addr
;
432 ret
= krb5_auth_con_setaddrs (context
,
437 krb5_log (context
, log_facility
, KPASSWDD_LOG_ERR
,
438 "krb5_auth_con_setaddr: %s",
439 krb5_get_err_text(context
, ret
));
443 if (verify (&auth_context
, server
, &ticket
, &out_data
,
444 s
, other_addr
, msg
, len
) == 0) {
445 change (auth_context
,
450 krb5_free_ticket (context
, ticket
);
455 krb5_data_free (&out_data
);
456 krb5_auth_con_free (context
, auth_context
);
463 krb5_principal server
;
467 krb5_addresses addrs
;
471 ret
= krb5_get_default_realm (context
, &realm
);
473 syslog_and_die ("krb5_get_default_realm: %s",
474 krb5_get_err_text(context
, ret
));
476 ret
= krb5_build_principal (context
,
484 syslog_and_die ("krb5_build_principal_ext: %s",
485 krb5_get_err_text(context
, ret
));
489 ret
= krb5_get_all_client_addrs (&addrs
);
491 syslog_and_die ("krb5_get_all_clients_addrs: %s",
492 krb5_get_err_text(context
, ret
));
496 sockets
= malloc (n
* sizeof(*sockets
));
498 FD_ZERO(&real_fdset
);
499 for (i
= 0; i
< n
; ++i
) {
500 struct sockaddr_in addr
;
502 sockets
[i
] = socket (AF_INET
, SOCK_DGRAM
, 0);
504 syslog_and_die ("socket: %m");
505 memset (&addr
, 0, sizeof(addr
));
506 addr
.sin_family
= AF_INET
;
507 memcpy (&addr
.sin_addr
, addrs
.val
[i
].address
.data
,
508 sizeof(addr
.sin_addr
));
509 addr
.sin_port
= port
;
511 if (bind (sockets
[i
], (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
512 syslog_and_die ("bind: %m");
513 maxfd
= max (maxfd
, sockets
[i
]);
514 FD_SET(sockets
[i
], &real_fdset
);
517 while(exit_flag
== 0) {
519 struct fd_set fdset
= real_fdset
;
521 ret
= select (maxfd
+ 1, &fdset
, NULL
, NULL
, NULL
);
526 syslog_and_die ("select: %m");
527 for (i
= 0; i
< n
; ++i
)
528 if (FD_ISSET(sockets
[i
], &fdset
)) {
529 struct sockaddr_in other_addr
;
531 int addrlen
= sizeof(other_addr
);
533 ret
= recvfrom (sockets
[i
], buf
, sizeof(buf
), 0,
534 (struct sockaddr
*)&other_addr
,
540 syslog_and_die ("recvfrom: %m");
541 process (server
, sockets
[i
],
542 addrs
.val
[i
].address
.data
, &other_addr
, buf
, ret
);
545 krb5_free_addresses (context
, &addrs
);
546 krb5_free_principal (context
, server
);
547 krb5_free_context (context
);
558 main (int argc
, char **argv
)
560 krb5_init_context (&context
);
562 set_progname (argv
[0]);
563 krb5_openlog (context
, "kpasswdd", &log_facility
);
565 signal (SIGINT
, sigterm
);
567 return doit (krb5_getportbyname ("kpasswd", "udp", htons(KPASSWD_PORT
)));