remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / kpasswd / kpasswdd.c
1 /*
2  * Copyright (c) 1997-2002 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: kpasswdd.c,v 1.54 2002/12/02 14:31:52 joda Exp $");
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 sig_atomic_t exit_flag = 0;
48
49 static void
50 send_reply (int s,
51             struct sockaddr *sa,
52             int sa_size,
53             krb5_data *ap_rep,
54             krb5_data *rest)
55 {
56     struct msghdr msghdr;
57     struct iovec iov[3];
58     u_int16_t len, ap_rep_len;
59     u_char header[6];
60     u_char *p;
61
62     if (ap_rep)
63         ap_rep_len = ap_rep->length;
64     else
65         ap_rep_len = 0;
66
67     len = 6 + ap_rep_len + rest->length;
68     p = header;
69     *p++ = (len >> 8) & 0xFF;
70     *p++ = (len >> 0) & 0xFF;
71     *p++ = 0;
72     *p++ = 1;
73     *p++ = (ap_rep_len >> 8) & 0xFF;
74     *p++ = (ap_rep_len >> 0) & 0xFF;
75
76     memset (&msghdr, 0, sizeof(msghdr));
77     msghdr.msg_name       = (void *)sa;
78     msghdr.msg_namelen    = sa_size;
79     msghdr.msg_iov        = iov;
80     msghdr.msg_iovlen     = sizeof(iov)/sizeof(*iov);
81 #if 0
82     msghdr.msg_control    = NULL;
83     msghdr.msg_controllen = 0;
84 #endif
85
86     iov[0].iov_base       = (char *)header;
87     iov[0].iov_len        = 6;
88     if (ap_rep_len) {
89         iov[1].iov_base   = ap_rep->data;
90         iov[1].iov_len    = ap_rep->length;
91     } else {
92         iov[1].iov_base   = NULL;
93         iov[1].iov_len    = 0;
94     }
95     iov[2].iov_base       = rest->data;
96     iov[2].iov_len        = rest->length;
97
98     if (sendmsg (s, &msghdr, 0) < 0)
99         krb5_warn (context, errno, "sendmsg");
100 }
101
102 static int
103 make_result (krb5_data *data,
104              u_int16_t result_code,
105              const char *expl)
106 {
107     krb5_data_zero (data);
108
109     data->length = asprintf ((char **)&data->data,
110                              "%c%c%s",
111                              (result_code >> 8) & 0xFF,
112                              result_code & 0xFF,
113                              expl);
114
115     if (data->data == NULL) {
116         krb5_warnx (context, "Out of memory generating error reply");
117         return 1;
118     }
119     return 0;
120 }
121
122 static void
123 reply_error (krb5_principal server,
124              int s,
125              struct sockaddr *sa,
126              int sa_size,
127              krb5_error_code error_code,
128              u_int16_t result_code,
129              const char *expl)
130 {
131     krb5_error_code ret;
132     krb5_data error_data;
133     krb5_data e_data;
134
135     if (make_result(&e_data, result_code, expl))
136         return;
137
138     ret = krb5_mk_error (context,
139                          error_code,
140                          NULL,
141                          &e_data,
142                          NULL,
143                          server,
144                          NULL,
145                          NULL,
146                          &error_data);
147     krb5_data_free (&e_data);
148     if (ret) {
149         krb5_warn (context, ret, "Could not even generate error reply");
150         return;
151     }
152     send_reply (s, sa, sa_size, NULL, &error_data);
153     krb5_data_free (&error_data);
154 }
155
156 static void
157 reply_priv (krb5_auth_context auth_context,
158             int s,
159             struct sockaddr *sa,
160             int sa_size,
161             u_int16_t result_code,
162             const char *expl)
163 {
164     krb5_error_code ret;
165     krb5_data krb_priv_data;
166     krb5_data ap_rep_data;
167     krb5_data e_data;
168
169     ret = krb5_mk_rep (context,
170                        auth_context,
171                        &ap_rep_data);
172     if (ret) {
173         krb5_warn (context, ret, "Could not even generate error reply");
174         return;
175     }
176
177     if (make_result(&e_data, result_code, expl))
178         return;
179
180     ret = krb5_mk_priv (context,
181                         auth_context,
182                         &e_data,
183                         &krb_priv_data,
184                         NULL);
185     krb5_data_free (&e_data);
186     if (ret) {
187         krb5_warn (context, ret, "Could not even generate error reply");
188         return;
189     }
190     send_reply (s, sa, sa_size, &ap_rep_data, &krb_priv_data);
191     krb5_data_free (&ap_rep_data);
192     krb5_data_free (&krb_priv_data);
193 }
194
195 /*
196  * Change the password for `principal', sending the reply back on `s'
197  * (`sa', `sa_size') to `pwd_data'.
198  */
199
200 static void
201 change (krb5_auth_context auth_context,
202         krb5_principal principal,
203         int s,
204         struct sockaddr *sa,
205         int sa_size,
206         krb5_data *pwd_data)
207 {
208     krb5_error_code ret;
209     char *client;
210     const char *pwd_reason;
211     kadm5_config_params conf;
212     void *kadm5_handle;
213     char *tmp;
214
215     memset (&conf, 0, sizeof(conf));
216     
217     krb5_unparse_name (context, principal, &client);
218
219     ret = kadm5_init_with_password_ctx(context, 
220                                        client,
221                                        NULL,
222                                        KADM5_ADMIN_SERVICE,
223                                        &conf, 0, 0, 
224                                        &kadm5_handle);
225     if (ret) {
226         free (client);
227         krb5_warn (context, ret, "kadm5_init_with_password_ctx");
228         reply_priv (auth_context, s, sa, sa_size, 2,
229                     "Internal error");
230         return;
231     }
232
233     krb5_warnx (context, "Changing password for %s", client);
234     free (client);
235
236     pwd_reason = kadm5_check_password_quality (context, principal, pwd_data);
237     if (pwd_reason != NULL ) {
238         krb5_warnx (context, "%s", pwd_reason);
239         reply_priv (auth_context, s, sa, sa_size, 4, pwd_reason);
240         kadm5_destroy (kadm5_handle);
241         return;
242     }
243
244     tmp = malloc (pwd_data->length + 1);
245     if (tmp == NULL) {
246         krb5_warnx (context, "malloc: out of memory");
247         reply_priv (auth_context, s, sa, sa_size, 2,
248                     "Internal error");
249         goto out;
250     }
251     memcpy (tmp, pwd_data->data, pwd_data->length);
252     tmp[pwd_data->length] = '\0';
253
254     ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
255     memset (tmp, 0, pwd_data->length);
256     free (tmp);
257     if (ret) {
258         krb5_warn (context, ret, "kadm5_s_chpass_principal_cond");
259         reply_priv (auth_context, s, sa, sa_size, 2,
260                     "Internal error");
261         goto out;
262     }
263     reply_priv (auth_context, s, sa, sa_size, 0, "Password changed");
264 out:
265     kadm5_destroy (kadm5_handle);
266 }
267
268 static int
269 verify (krb5_auth_context *auth_context,
270         krb5_principal server,
271         krb5_keytab keytab,
272         krb5_ticket **ticket,
273         krb5_data *out_data,
274         int s,
275         struct sockaddr *sa,
276         int sa_size,
277         u_char *msg,
278         size_t len)
279 {
280     krb5_error_code ret;
281     u_int16_t pkt_len, pkt_ver, ap_req_len;
282     krb5_data ap_req_data;
283     krb5_data krb_priv_data;
284
285     pkt_len = (msg[0] << 8) | (msg[1]);
286     pkt_ver = (msg[2] << 8) | (msg[3]);
287     ap_req_len = (msg[4] << 8) | (msg[5]);
288     if (pkt_len != len) {
289         krb5_warnx (context, "Strange len: %ld != %ld", 
290                     (long)pkt_len, (long)len);
291         reply_error (server, s, sa, sa_size, 0, 1, "Bad request");
292         return 1;
293     }
294     if (pkt_ver != 0x0001) {
295         krb5_warnx (context, "Bad version (%d)", pkt_ver);
296         reply_error (server, s, sa, sa_size, 0, 1, "Wrong program version");
297         return 1;
298     }
299
300     ap_req_data.data   = msg + 6;
301     ap_req_data.length = ap_req_len;
302
303     ret = krb5_rd_req (context,
304                        auth_context,
305                        &ap_req_data,
306                        server,
307                        keytab,
308                        NULL,
309                        ticket);
310     if (ret) {
311         if(ret == KRB5_KT_NOTFOUND) {
312             char *name;
313             krb5_unparse_name(context, server, &name);
314             krb5_warnx (context, "krb5_rd_req: %s (%s)", 
315                         krb5_get_err_text(context, ret), name);
316             free(name);
317         } else
318             krb5_warn (context, ret, "krb5_rd_req");
319         reply_error (server, s, sa, sa_size, ret, 3, "Authentication failed");
320         return 1;
321     }
322
323     if (!(*ticket)->ticket.flags.initial) {
324         krb5_warnx (context, "initial flag not set");
325         reply_error (server, s, sa, sa_size, ret, 1,
326                      "Bad request");
327         goto out;
328     }
329     krb_priv_data.data   = msg + 6 + ap_req_len;
330     krb_priv_data.length = len - 6 - ap_req_len;
331
332     ret = krb5_rd_priv (context,
333                         *auth_context,
334                         &krb_priv_data,
335                         out_data,
336                         NULL);
337     
338     if (ret) {
339         krb5_warn (context, ret, "krb5_rd_priv");
340         reply_error (server, s, sa, sa_size, ret, 3, "Bad request");
341         goto out;
342     }
343     return 0;
344 out:
345     krb5_free_ticket (context, *ticket);
346     return 1;
347 }
348
349 static void
350 process (krb5_principal server,
351          krb5_keytab keytab,
352          int s,
353          krb5_address *this_addr,
354          struct sockaddr *sa,
355          int sa_size,
356          u_char *msg,
357          int len)
358 {
359     krb5_error_code ret;
360     krb5_auth_context auth_context = NULL;
361     krb5_data out_data;
362     krb5_ticket *ticket;
363     krb5_address other_addr;
364
365     krb5_data_zero (&out_data);
366
367     ret = krb5_auth_con_init (context, &auth_context);
368     if (ret) {
369         krb5_warn (context, ret, "krb5_auth_con_init");
370         return;
371     }
372
373     krb5_auth_con_setflags (context, auth_context,
374                             KRB5_AUTH_CONTEXT_DO_SEQUENCE);
375
376     ret = krb5_sockaddr2address (context, sa, &other_addr);
377     if (ret) {
378         krb5_warn (context, ret, "krb5_sockaddr2address");
379         goto out;
380     }
381
382     ret = krb5_auth_con_setaddrs (context,
383                                   auth_context,
384                                   this_addr,
385                                   &other_addr);
386     krb5_free_address (context, &other_addr);
387     if (ret) {
388         krb5_warn (context, ret, "krb5_auth_con_setaddr");
389         goto out;
390     }
391
392     if (verify (&auth_context, server, keytab, &ticket, &out_data,
393                 s, sa, sa_size, msg, len) == 0) {
394         change (auth_context,
395                 ticket->client,
396                 s,
397                 sa, sa_size,
398                 &out_data);
399         memset (out_data.data, 0, out_data.length);
400         krb5_free_ticket (context, ticket);
401         free (ticket);
402     }
403
404 out:
405     krb5_data_free (&out_data);
406     krb5_auth_con_free (context, auth_context);
407 }
408
409 static int
410 doit (krb5_keytab keytab, int port)
411 {
412     krb5_error_code ret;
413     krb5_principal server;
414     int *sockets;
415     int maxfd;
416     char *realm;
417     krb5_addresses addrs;
418     unsigned n, i;
419     fd_set real_fdset;
420     struct sockaddr_storage __ss;
421     struct sockaddr *sa = (struct sockaddr *)&__ss;
422
423     ret = krb5_get_default_realm (context, &realm);
424     if (ret)
425         krb5_err (context, 1, ret, "krb5_get_default_realm");
426
427     ret = krb5_build_principal (context,
428                                 &server,
429                                 strlen(realm),
430                                 realm,
431                                 "kadmin",
432                                 "changepw",
433                                 NULL);
434     if (ret)
435         krb5_err (context, 1, ret, "krb5_build_principal");
436
437     free (realm);
438
439     ret = krb5_get_all_server_addrs (context, &addrs);
440     if (ret)
441         krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
442
443     n = addrs.len;
444
445     sockets = malloc (n * sizeof(*sockets));
446     if (sockets == NULL)
447         krb5_errx (context, 1, "out of memory");
448     maxfd = -1;
449     FD_ZERO(&real_fdset);
450     for (i = 0; i < n; ++i) {
451         int sa_size = sizeof(__ss);
452
453         krb5_addr2sockaddr (context, &addrs.val[i], sa, &sa_size, port);
454         
455         sockets[i] = socket (sa->sa_family, SOCK_DGRAM, 0);
456         if (sockets[i] < 0)
457             krb5_err (context, 1, errno, "socket");
458         if (bind (sockets[i], sa, sa_size) < 0) {
459             char str[128];
460             size_t len;
461             int save_errno = errno;
462
463             ret = krb5_print_address (&addrs.val[i], str, sizeof(str), &len);
464             if (ret)
465                 strlcpy(str, "unknown address", sizeof(str));
466             krb5_warn (context, save_errno, "bind(%s)", str);
467             continue;
468         }
469         maxfd = max (maxfd, sockets[i]);
470         if (maxfd >= FD_SETSIZE)
471             krb5_errx (context, 1, "fd too large");
472         FD_SET(sockets[i], &real_fdset);
473     }
474     if (maxfd == -1)
475         krb5_errx (context, 1, "No sockets!");
476
477     while(exit_flag == 0) {
478         int ret;
479         fd_set fdset = real_fdset;
480
481         ret = select (maxfd + 1, &fdset, NULL, NULL, NULL);
482         if (ret < 0) {
483             if (errno == EINTR)
484                 continue;
485             else
486                 krb5_err (context, 1, errno, "select");
487         }
488         for (i = 0; i < n; ++i)
489             if (FD_ISSET(sockets[i], &fdset)) {
490                 u_char buf[BUFSIZ];
491                 socklen_t addrlen = sizeof(__ss);
492
493                 ret = recvfrom (sockets[i], buf, sizeof(buf), 0,
494                                 sa, &addrlen);
495                 if (ret < 0) {
496                     if(errno == EINTR)
497                         break;
498                     else
499                         krb5_err (context, 1, errno, "recvfrom");
500                 }
501
502                 process (server, keytab, sockets[i],
503                          &addrs.val[i],
504                          sa, addrlen,
505                          buf, ret);
506             }
507     }
508     krb5_free_addresses (context, &addrs);
509     krb5_free_principal (context, server);
510     krb5_free_context (context);
511     return 0;
512 }
513
514 static RETSIGTYPE
515 sigterm(int sig)
516 {
517     exit_flag = 1;
518 }
519
520 const char *check_library  = NULL;
521 const char *check_function = NULL;
522 char *keytab_str = "HDB:";
523 char *realm_str;
524 int version_flag;
525 int help_flag;
526 char *port_str;
527
528 struct getargs args[] = {
529 #ifdef HAVE_DLOPEN
530     { "check-library", 0, arg_string, &check_library, 
531       "library to load password check function from", "library" },
532     { "check-function", 0, arg_string, &check_function,
533       "password check function to load", "function" },
534 #endif
535     { "keytab", 'k', arg_string, &keytab_str, 
536       "keytab to get authentication key from", "kspec" },
537     { "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
538     { "port",  'p', arg_string, &port_str, "port" },
539     { "version", 0, arg_flag, &version_flag },
540     { "help", 0, arg_flag, &help_flag }
541 };
542 int num_args = sizeof(args) / sizeof(args[0]);
543
544 int
545 main (int argc, char **argv)
546 {
547     int optind;
548     krb5_keytab keytab;
549     krb5_error_code ret;
550     int port;
551     
552     optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
553     
554     if(help_flag)
555         krb5_std_usage(0, args, num_args);
556     if(version_flag) {
557         print_version(NULL);
558         exit(0);
559     }
560
561     if(realm_str)
562         krb5_set_default_realm(context, realm_str);
563     
564     krb5_openlog (context, "kpasswdd", &log_facility);
565     krb5_set_warn_dest(context, log_facility);
566
567     if (port_str != NULL) {
568         struct servent *s = roken_getservbyname (port_str, "udp");
569
570         if (s != NULL)
571             port = s->s_port;
572         else {
573             char *ptr;
574
575             port = strtol (port_str, &ptr, 10);
576             if (port == 0 && ptr == port_str)
577                 krb5_errx (context, 1, "bad port `%s'", port_str);
578             port = htons(port);
579         }
580     } else
581         port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT);
582
583     ret = krb5_kt_register(context, &hdb_kt_ops);
584     if(ret)
585         krb5_err(context, 1, ret, "krb5_kt_register");
586
587     ret = krb5_kt_resolve(context, keytab_str, &keytab);
588     if(ret)
589         krb5_err(context, 1, ret, "%s", keytab_str);
590     
591     kadm5_setup_passwd_quality_check (context, check_library, check_function);
592
593 #ifdef HAVE_SIGACTION
594     {
595         struct sigaction sa;
596
597         sa.sa_flags = 0;
598         sa.sa_handler = sigterm;
599         sigemptyset(&sa.sa_mask);
600
601         sigaction(SIGINT,  &sa, NULL);
602         sigaction(SIGTERM, &sa, NULL);
603     }
604 #else
605     signal(SIGINT,  sigterm);
606     signal(SIGTERM, sigterm);
607 #endif
608
609     pidfile(NULL);
610
611     return doit (keytab, port);
612 }