xref: /freebsd/crypto/krb5/src/kprop/kprop.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* kprop/kprop.c */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert  * All Rights Reserved.
6*7f2fe78bSCy Schubert  *
7*7f2fe78bSCy Schubert  * Export of this software from the United States of America may
8*7f2fe78bSCy Schubert  *   require a specific license from the United States Government.
9*7f2fe78bSCy Schubert  *   It is the responsibility of any person or organization contemplating
10*7f2fe78bSCy Schubert  *   export to obtain such a license before exporting.
11*7f2fe78bSCy Schubert  *
12*7f2fe78bSCy Schubert  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13*7f2fe78bSCy Schubert  * distribute this software and its documentation for any purpose and
14*7f2fe78bSCy Schubert  * without fee is hereby granted, provided that the above copyright
15*7f2fe78bSCy Schubert  * notice appear in all copies and that both that copyright notice and
16*7f2fe78bSCy Schubert  * this permission notice appear in supporting documentation, and that
17*7f2fe78bSCy Schubert  * the name of M.I.T. not be used in advertising or publicity pertaining
18*7f2fe78bSCy Schubert  * to distribution of the software without specific, written prior
19*7f2fe78bSCy Schubert  * permission.  Furthermore if you modify this software you must label
20*7f2fe78bSCy Schubert  * your software as modified software and not distribute it in such a
21*7f2fe78bSCy Schubert  * fashion that it might be confused with the original M.I.T. software.
22*7f2fe78bSCy Schubert  * M.I.T. makes no representations about the suitability of
23*7f2fe78bSCy Schubert  * this software for any purpose.  It is provided "as is" without express
24*7f2fe78bSCy Schubert  * or implied warranty.
25*7f2fe78bSCy Schubert  */
26*7f2fe78bSCy Schubert 
27*7f2fe78bSCy Schubert #include "k5-int.h"
28*7f2fe78bSCy Schubert #include <inttypes.h>
29*7f2fe78bSCy Schubert #include <locale.h>
30*7f2fe78bSCy Schubert #include <sys/file.h>
31*7f2fe78bSCy Schubert #include <signal.h>
32*7f2fe78bSCy Schubert #include <sys/types.h>
33*7f2fe78bSCy Schubert #include <sys/time.h>
34*7f2fe78bSCy Schubert #include <sys/stat.h>
35*7f2fe78bSCy Schubert #include <sys/socket.h>
36*7f2fe78bSCy Schubert #include <netinet/in.h>
37*7f2fe78bSCy Schubert #include <sys/param.h>
38*7f2fe78bSCy Schubert #include <netdb.h>
39*7f2fe78bSCy Schubert #include <fcntl.h>
40*7f2fe78bSCy Schubert 
41*7f2fe78bSCy Schubert #include "com_err.h"
42*7f2fe78bSCy Schubert #include "fake-addrinfo.h"
43*7f2fe78bSCy Schubert #include "kprop.h"
44*7f2fe78bSCy Schubert 
45*7f2fe78bSCy Schubert #ifndef GETSOCKNAME_ARG3_TYPE
46*7f2fe78bSCy Schubert #define GETSOCKNAME_ARG3_TYPE unsigned int
47*7f2fe78bSCy Schubert #endif
48*7f2fe78bSCy Schubert 
49*7f2fe78bSCy Schubert static char *kprop_version = KPROP_PROT_VERSION;
50*7f2fe78bSCy Schubert 
51*7f2fe78bSCy Schubert static char *progname = NULL;
52*7f2fe78bSCy Schubert static int debug = 0;
53*7f2fe78bSCy Schubert static char *keytab_path = NULL;
54*7f2fe78bSCy Schubert static char *replica_host;
55*7f2fe78bSCy Schubert static char *realm = NULL;
56*7f2fe78bSCy Schubert static char *def_realm = NULL;
57*7f2fe78bSCy Schubert static char *file = KPROP_DEFAULT_FILE;
58*7f2fe78bSCy Schubert 
59*7f2fe78bSCy Schubert /* The Kerberos principal we'll be sending as, initialized in get_tickets. */
60*7f2fe78bSCy Schubert static krb5_principal my_principal;
61*7f2fe78bSCy Schubert 
62*7f2fe78bSCy Schubert static krb5_creds creds;
63*7f2fe78bSCy Schubert static krb5_address *sender_addr;
64*7f2fe78bSCy Schubert static const char *port = KPROP_SERVICE;
65*7f2fe78bSCy Schubert static char *dbpathname;
66*7f2fe78bSCy Schubert 
67*7f2fe78bSCy Schubert static void parse_args(krb5_context context, int argc, char **argv);
68*7f2fe78bSCy Schubert static void get_tickets(krb5_context context);
69*7f2fe78bSCy Schubert static void usage(void);
70*7f2fe78bSCy Schubert static void open_connection(krb5_context context, char *host, int *fd_out);
71*7f2fe78bSCy Schubert static void kerberos_authenticate(krb5_context context,
72*7f2fe78bSCy Schubert                                   krb5_auth_context *auth_context, int fd,
73*7f2fe78bSCy Schubert                                   krb5_principal me, krb5_creds **new_creds);
74*7f2fe78bSCy Schubert static int open_database(krb5_context context, char *data_fn, off_t *size);
75*7f2fe78bSCy Schubert static void close_database(krb5_context context, int fd);
76*7f2fe78bSCy Schubert static void xmit_database(krb5_context context,
77*7f2fe78bSCy Schubert                           krb5_auth_context auth_context, krb5_creds *my_creds,
78*7f2fe78bSCy Schubert                           int fd, int database_fd, off_t in_database_size);
79*7f2fe78bSCy Schubert static void send_error(krb5_context context, krb5_creds *my_creds, int fd,
80*7f2fe78bSCy Schubert                        char *err_text, krb5_error_code err_code);
81*7f2fe78bSCy Schubert static void update_last_prop_file(char *hostname, char *file_name);
82*7f2fe78bSCy Schubert 
usage()83*7f2fe78bSCy Schubert static void usage()
84*7f2fe78bSCy Schubert {
85*7f2fe78bSCy Schubert     fprintf(stderr, _("\nUsage: %s [-r realm] [-f file] [-d] [-P port] "
86*7f2fe78bSCy Schubert                       "[-s keytab] replica_host\n\n"), progname);
87*7f2fe78bSCy Schubert     exit(1);
88*7f2fe78bSCy Schubert }
89*7f2fe78bSCy Schubert 
90*7f2fe78bSCy Schubert int
main(int argc,char ** argv)91*7f2fe78bSCy Schubert main(int argc, char **argv)
92*7f2fe78bSCy Schubert {
93*7f2fe78bSCy Schubert     int fd, database_fd;
94*7f2fe78bSCy Schubert     off_t database_size;
95*7f2fe78bSCy Schubert     krb5_error_code retval;
96*7f2fe78bSCy Schubert     krb5_context context;
97*7f2fe78bSCy Schubert     krb5_creds *my_creds;
98*7f2fe78bSCy Schubert     krb5_auth_context auth_context;
99*7f2fe78bSCy Schubert 
100*7f2fe78bSCy Schubert     setlocale(LC_ALL, "");
101*7f2fe78bSCy Schubert     retval = krb5_init_context(&context);
102*7f2fe78bSCy Schubert     if (retval) {
103*7f2fe78bSCy Schubert         com_err(argv[0], retval, _("while initializing krb5"));
104*7f2fe78bSCy Schubert         exit(1);
105*7f2fe78bSCy Schubert     }
106*7f2fe78bSCy Schubert     parse_args(context, argc, argv);
107*7f2fe78bSCy Schubert     get_tickets(context);
108*7f2fe78bSCy Schubert 
109*7f2fe78bSCy Schubert     database_fd = open_database(context, file, &database_size);
110*7f2fe78bSCy Schubert     open_connection(context, replica_host, &fd);
111*7f2fe78bSCy Schubert     kerberos_authenticate(context, &auth_context, fd, my_principal, &my_creds);
112*7f2fe78bSCy Schubert     xmit_database(context, auth_context, my_creds, fd, database_fd,
113*7f2fe78bSCy Schubert                   database_size);
114*7f2fe78bSCy Schubert     update_last_prop_file(replica_host, file);
115*7f2fe78bSCy Schubert     printf(_("Database propagation to %s: SUCCEEDED\n"), replica_host);
116*7f2fe78bSCy Schubert     krb5_free_cred_contents(context, my_creds);
117*7f2fe78bSCy Schubert     close_database(context, database_fd);
118*7f2fe78bSCy Schubert     krb5_free_default_realm(context, def_realm);
119*7f2fe78bSCy Schubert     exit(0);
120*7f2fe78bSCy Schubert }
121*7f2fe78bSCy Schubert 
122*7f2fe78bSCy Schubert static void
parse_args(krb5_context context,int argc,char ** argv)123*7f2fe78bSCy Schubert parse_args(krb5_context context, int argc, char **argv)
124*7f2fe78bSCy Schubert {
125*7f2fe78bSCy Schubert     int c;
126*7f2fe78bSCy Schubert     krb5_error_code ret;
127*7f2fe78bSCy Schubert 
128*7f2fe78bSCy Schubert     progname = argv[0];
129*7f2fe78bSCy Schubert     while ((c = getopt(argc, argv, "r:f:dP:s:")) != -1) {
130*7f2fe78bSCy Schubert         switch (c) {
131*7f2fe78bSCy Schubert         case 'r':
132*7f2fe78bSCy Schubert             realm = optarg;
133*7f2fe78bSCy Schubert             break;
134*7f2fe78bSCy Schubert         case 'f':
135*7f2fe78bSCy Schubert             file = optarg;
136*7f2fe78bSCy Schubert             break;
137*7f2fe78bSCy Schubert         case 'd':
138*7f2fe78bSCy Schubert             debug++;
139*7f2fe78bSCy Schubert             break;
140*7f2fe78bSCy Schubert         case 'P':
141*7f2fe78bSCy Schubert             port = optarg;
142*7f2fe78bSCy Schubert             break;
143*7f2fe78bSCy Schubert         case 's':
144*7f2fe78bSCy Schubert             keytab_path = optarg;
145*7f2fe78bSCy Schubert             break;
146*7f2fe78bSCy Schubert         default:
147*7f2fe78bSCy Schubert             usage();
148*7f2fe78bSCy Schubert         }
149*7f2fe78bSCy Schubert     }
150*7f2fe78bSCy Schubert     if (argc - optind != 1)
151*7f2fe78bSCy Schubert         usage();
152*7f2fe78bSCy Schubert     replica_host = argv[optind];
153*7f2fe78bSCy Schubert 
154*7f2fe78bSCy Schubert     if (realm == NULL) {
155*7f2fe78bSCy Schubert         ret = krb5_get_default_realm(context, &def_realm);
156*7f2fe78bSCy Schubert         if (ret) {
157*7f2fe78bSCy Schubert             com_err(progname, errno, _("while getting default realm"));
158*7f2fe78bSCy Schubert             exit(1);
159*7f2fe78bSCy Schubert         }
160*7f2fe78bSCy Schubert         realm = def_realm;
161*7f2fe78bSCy Schubert     }
162*7f2fe78bSCy Schubert }
163*7f2fe78bSCy Schubert 
164*7f2fe78bSCy Schubert static void
get_tickets(krb5_context context)165*7f2fe78bSCy Schubert get_tickets(krb5_context context)
166*7f2fe78bSCy Schubert {
167*7f2fe78bSCy Schubert     char *server;
168*7f2fe78bSCy Schubert     krb5_error_code retval;
169*7f2fe78bSCy Schubert     krb5_keytab keytab = NULL;
170*7f2fe78bSCy Schubert     krb5_principal server_princ = NULL;
171*7f2fe78bSCy Schubert 
172*7f2fe78bSCy Schubert     /* Figure out what tickets we'll be using to send. */
173*7f2fe78bSCy Schubert     retval = sn2princ_realm(context, NULL, KPROP_SERVICE_NAME, realm,
174*7f2fe78bSCy Schubert                             &my_principal);
175*7f2fe78bSCy Schubert     if (retval) {
176*7f2fe78bSCy Schubert         com_err(progname, errno, _("while setting client principal name"));
177*7f2fe78bSCy Schubert         exit(1);
178*7f2fe78bSCy Schubert     }
179*7f2fe78bSCy Schubert 
180*7f2fe78bSCy Schubert     /* Construct the principal name for the replica host. */
181*7f2fe78bSCy Schubert     memset(&creds, 0, sizeof(creds));
182*7f2fe78bSCy Schubert     retval = sn2princ_realm(context, replica_host, KPROP_SERVICE_NAME, realm,
183*7f2fe78bSCy Schubert                             &server_princ);
184*7f2fe78bSCy Schubert     if (retval) {
185*7f2fe78bSCy Schubert         com_err(progname, errno, _("while setting server principal name"));
186*7f2fe78bSCy Schubert         exit(1);
187*7f2fe78bSCy Schubert     }
188*7f2fe78bSCy Schubert     retval = krb5_unparse_name_flags(context, server_princ,
189*7f2fe78bSCy Schubert                                      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &server);
190*7f2fe78bSCy Schubert     if (retval) {
191*7f2fe78bSCy Schubert         com_err(progname, retval, _("while unparsing server name"));
192*7f2fe78bSCy Schubert         exit(1);
193*7f2fe78bSCy Schubert     }
194*7f2fe78bSCy Schubert 
195*7f2fe78bSCy Schubert     if (keytab_path != NULL) {
196*7f2fe78bSCy Schubert         retval = krb5_kt_resolve(context, keytab_path, &keytab);
197*7f2fe78bSCy Schubert         if (retval) {
198*7f2fe78bSCy Schubert             com_err(progname, retval, _("while resolving keytab"));
199*7f2fe78bSCy Schubert             exit(1);
200*7f2fe78bSCy Schubert         }
201*7f2fe78bSCy Schubert     }
202*7f2fe78bSCy Schubert 
203*7f2fe78bSCy Schubert     retval = krb5_get_init_creds_keytab(context, &creds, my_principal, keytab,
204*7f2fe78bSCy Schubert                                         0, server, NULL);
205*7f2fe78bSCy Schubert     if (retval) {
206*7f2fe78bSCy Schubert         com_err(progname, retval, _("while getting initial credentials\n"));
207*7f2fe78bSCy Schubert         exit(1);
208*7f2fe78bSCy Schubert     }
209*7f2fe78bSCy Schubert 
210*7f2fe78bSCy Schubert     if (keytab != NULL)
211*7f2fe78bSCy Schubert         krb5_kt_close(context, keytab);
212*7f2fe78bSCy Schubert     krb5_free_unparsed_name(context, server);
213*7f2fe78bSCy Schubert     krb5_free_principal(context, server_princ);
214*7f2fe78bSCy Schubert }
215*7f2fe78bSCy Schubert 
216*7f2fe78bSCy Schubert static void
open_connection(krb5_context context,char * host,int * fd_out)217*7f2fe78bSCy Schubert open_connection(krb5_context context, char *host, int *fd_out)
218*7f2fe78bSCy Schubert {
219*7f2fe78bSCy Schubert     krb5_error_code retval;
220*7f2fe78bSCy Schubert     GETSOCKNAME_ARG3_TYPE socket_length;
221*7f2fe78bSCy Schubert     struct addrinfo hints, *res, *answers;
222*7f2fe78bSCy Schubert     struct sockaddr *sa;
223*7f2fe78bSCy Schubert     struct sockaddr_storage my_sin;
224*7f2fe78bSCy Schubert     int s, error;
225*7f2fe78bSCy Schubert 
226*7f2fe78bSCy Schubert     *fd_out = -1;
227*7f2fe78bSCy Schubert     memset(&hints, 0, sizeof(hints));
228*7f2fe78bSCy Schubert     hints.ai_family = PF_UNSPEC;
229*7f2fe78bSCy Schubert     hints.ai_socktype = SOCK_STREAM;
230*7f2fe78bSCy Schubert     hints.ai_flags = AI_ADDRCONFIG;
231*7f2fe78bSCy Schubert     error = getaddrinfo(host, port, &hints, &answers);
232*7f2fe78bSCy Schubert     if (error != 0) {
233*7f2fe78bSCy Schubert         com_err(progname, 0, "%s: %s", host, gai_strerror(error));
234*7f2fe78bSCy Schubert         exit(1);
235*7f2fe78bSCy Schubert     }
236*7f2fe78bSCy Schubert 
237*7f2fe78bSCy Schubert     s = -1;
238*7f2fe78bSCy Schubert     retval = EINVAL;
239*7f2fe78bSCy Schubert     for (res = answers; res != NULL; res = res->ai_next) {
240*7f2fe78bSCy Schubert         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
241*7f2fe78bSCy Schubert         if (s < 0) {
242*7f2fe78bSCy Schubert             com_err(progname, errno, _("while creating socket"));
243*7f2fe78bSCy Schubert             exit(1);
244*7f2fe78bSCy Schubert         }
245*7f2fe78bSCy Schubert 
246*7f2fe78bSCy Schubert         if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
247*7f2fe78bSCy Schubert             retval = errno;
248*7f2fe78bSCy Schubert             close(s);
249*7f2fe78bSCy Schubert             s = -1;
250*7f2fe78bSCy Schubert             continue;
251*7f2fe78bSCy Schubert         }
252*7f2fe78bSCy Schubert 
253*7f2fe78bSCy Schubert         /* We successfully connect()ed */
254*7f2fe78bSCy Schubert         *fd_out = s;
255*7f2fe78bSCy Schubert 
256*7f2fe78bSCy Schubert         break;
257*7f2fe78bSCy Schubert     }
258*7f2fe78bSCy Schubert 
259*7f2fe78bSCy Schubert     freeaddrinfo(answers);
260*7f2fe78bSCy Schubert 
261*7f2fe78bSCy Schubert     if (s == -1) {
262*7f2fe78bSCy Schubert         com_err(progname, retval, _("while connecting to server"));
263*7f2fe78bSCy Schubert         exit(1);
264*7f2fe78bSCy Schubert     }
265*7f2fe78bSCy Schubert 
266*7f2fe78bSCy Schubert     /* Set sender_addr. */
267*7f2fe78bSCy Schubert     socket_length = sizeof(my_sin);
268*7f2fe78bSCy Schubert     if (getsockname(s, (struct sockaddr *)&my_sin, &socket_length) < 0) {
269*7f2fe78bSCy Schubert         com_err(progname, errno, _("while getting local socket address"));
270*7f2fe78bSCy Schubert         exit(1);
271*7f2fe78bSCy Schubert     }
272*7f2fe78bSCy Schubert     sa = (struct sockaddr *)&my_sin;
273*7f2fe78bSCy Schubert     if (sockaddr2krbaddr(context, sa->sa_family, sa, &sender_addr) != 0) {
274*7f2fe78bSCy Schubert         com_err(progname, errno, _("while converting local address"));
275*7f2fe78bSCy Schubert         exit(1);
276*7f2fe78bSCy Schubert     }
277*7f2fe78bSCy Schubert }
278*7f2fe78bSCy Schubert 
279*7f2fe78bSCy Schubert static void
kerberos_authenticate(krb5_context context,krb5_auth_context * auth_context,int fd,krb5_principal me,krb5_creds ** new_creds)280*7f2fe78bSCy Schubert kerberos_authenticate(krb5_context context, krb5_auth_context *auth_context,
281*7f2fe78bSCy Schubert                       int fd, krb5_principal me, krb5_creds **new_creds)
282*7f2fe78bSCy Schubert {
283*7f2fe78bSCy Schubert     krb5_error_code retval;
284*7f2fe78bSCy Schubert     krb5_error *error = NULL;
285*7f2fe78bSCy Schubert     krb5_ap_rep_enc_part *rep_result;
286*7f2fe78bSCy Schubert 
287*7f2fe78bSCy Schubert     retval = krb5_auth_con_init(context, auth_context);
288*7f2fe78bSCy Schubert     if (retval)
289*7f2fe78bSCy Schubert         exit(1);
290*7f2fe78bSCy Schubert 
291*7f2fe78bSCy Schubert     krb5_auth_con_setflags(context, *auth_context,
292*7f2fe78bSCy Schubert                            KRB5_AUTH_CONTEXT_DO_SEQUENCE);
293*7f2fe78bSCy Schubert 
294*7f2fe78bSCy Schubert     retval = krb5_auth_con_setaddrs(context, *auth_context, sender_addr, NULL);
295*7f2fe78bSCy Schubert     if (retval) {
296*7f2fe78bSCy Schubert         com_err(progname, retval, _("in krb5_auth_con_setaddrs"));
297*7f2fe78bSCy Schubert         exit(1);
298*7f2fe78bSCy Schubert     }
299*7f2fe78bSCy Schubert 
300*7f2fe78bSCy Schubert     retval = krb5_sendauth(context, auth_context, &fd, kprop_version,
301*7f2fe78bSCy Schubert                            me, creds.server, AP_OPTS_MUTUAL_REQUIRED, NULL,
302*7f2fe78bSCy Schubert                            &creds, NULL, &error, &rep_result, new_creds);
303*7f2fe78bSCy Schubert     if (retval) {
304*7f2fe78bSCy Schubert         com_err(progname, retval, _("while authenticating to server"));
305*7f2fe78bSCy Schubert         if (error != NULL) {
306*7f2fe78bSCy Schubert             if (error->error == KRB_ERR_GENERIC) {
307*7f2fe78bSCy Schubert                 if (error->text.data) {
308*7f2fe78bSCy Schubert                     fprintf(stderr, _("Generic remote error: %s\n"),
309*7f2fe78bSCy Schubert                             error->text.data);
310*7f2fe78bSCy Schubert                 }
311*7f2fe78bSCy Schubert             } else if (error->error) {
312*7f2fe78bSCy Schubert                 com_err(progname,
313*7f2fe78bSCy Schubert                         (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5,
314*7f2fe78bSCy Schubert                         _("signalled from server"));
315*7f2fe78bSCy Schubert                 if (error->text.data) {
316*7f2fe78bSCy Schubert                     fprintf(stderr, _("Error text from server: %s\n"),
317*7f2fe78bSCy Schubert                             error->text.data);
318*7f2fe78bSCy Schubert                 }
319*7f2fe78bSCy Schubert             }
320*7f2fe78bSCy Schubert             krb5_free_error(context, error);
321*7f2fe78bSCy Schubert         }
322*7f2fe78bSCy Schubert         exit(1);
323*7f2fe78bSCy Schubert     }
324*7f2fe78bSCy Schubert     krb5_free_ap_rep_enc_part(context, rep_result);
325*7f2fe78bSCy Schubert }
326*7f2fe78bSCy Schubert 
327*7f2fe78bSCy Schubert /*
328*7f2fe78bSCy Schubert  * Open the Kerberos database dump file.  Takes care of locking it
329*7f2fe78bSCy Schubert  * and making sure that the .ok file is more recent that the database
330*7f2fe78bSCy Schubert  * dump file itself.
331*7f2fe78bSCy Schubert  *
332*7f2fe78bSCy Schubert  * Returns the file descriptor of the database dump file.  Also fills
333*7f2fe78bSCy Schubert  * in the size of the database file.
334*7f2fe78bSCy Schubert  */
335*7f2fe78bSCy Schubert static int
open_database(krb5_context context,char * data_fn,off_t * size)336*7f2fe78bSCy Schubert open_database(krb5_context context, char *data_fn, off_t *size)
337*7f2fe78bSCy Schubert {
338*7f2fe78bSCy Schubert     struct stat stbuf, stbuf_ok;
339*7f2fe78bSCy Schubert     char *data_ok_fn;
340*7f2fe78bSCy Schubert     int fd, err;
341*7f2fe78bSCy Schubert 
342*7f2fe78bSCy Schubert     dbpathname = strdup(data_fn);
343*7f2fe78bSCy Schubert     if (dbpathname == NULL) {
344*7f2fe78bSCy Schubert         com_err(progname, ENOMEM, _("allocating database file name '%s'"),
345*7f2fe78bSCy Schubert                 data_fn);
346*7f2fe78bSCy Schubert         exit(1);
347*7f2fe78bSCy Schubert     }
348*7f2fe78bSCy Schubert     fd = open(dbpathname, O_RDONLY);
349*7f2fe78bSCy Schubert     if (fd < 0) {
350*7f2fe78bSCy Schubert         com_err(progname, errno, _("while trying to open %s"), dbpathname);
351*7f2fe78bSCy Schubert         exit(1);
352*7f2fe78bSCy Schubert     }
353*7f2fe78bSCy Schubert 
354*7f2fe78bSCy Schubert     err = krb5_lock_file(context, fd,
355*7f2fe78bSCy Schubert                          KRB5_LOCKMODE_SHARED | KRB5_LOCKMODE_DONTBLOCK);
356*7f2fe78bSCy Schubert     if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) {
357*7f2fe78bSCy Schubert         com_err(progname, 0, _("database locked"));
358*7f2fe78bSCy Schubert         exit(1);
359*7f2fe78bSCy Schubert     } else if (err) {
360*7f2fe78bSCy Schubert         com_err(progname, err, _("while trying to lock '%s'"), dbpathname);
361*7f2fe78bSCy Schubert         exit(1);
362*7f2fe78bSCy Schubert     }
363*7f2fe78bSCy Schubert     if (fstat(fd, &stbuf)) {
364*7f2fe78bSCy Schubert         com_err(progname, errno, _("while trying to stat %s"), data_fn);
365*7f2fe78bSCy Schubert         exit(1);
366*7f2fe78bSCy Schubert     }
367*7f2fe78bSCy Schubert     if (asprintf(&data_ok_fn, "%s.dump_ok", data_fn) < 0) {
368*7f2fe78bSCy Schubert         com_err(progname, ENOMEM, _("while trying to malloc data_ok_fn"));
369*7f2fe78bSCy Schubert         exit(1);
370*7f2fe78bSCy Schubert     }
371*7f2fe78bSCy Schubert     if (stat(data_ok_fn, &stbuf_ok)) {
372*7f2fe78bSCy Schubert         com_err(progname, errno, _("while trying to stat %s"), data_ok_fn);
373*7f2fe78bSCy Schubert         free(data_ok_fn);
374*7f2fe78bSCy Schubert         exit(1);
375*7f2fe78bSCy Schubert     }
376*7f2fe78bSCy Schubert     if (stbuf.st_mtime > stbuf_ok.st_mtime) {
377*7f2fe78bSCy Schubert         com_err(progname, 0, _("'%s' more recent than '%s'."), data_fn,
378*7f2fe78bSCy Schubert                 data_ok_fn);
379*7f2fe78bSCy Schubert         exit(1);
380*7f2fe78bSCy Schubert     }
381*7f2fe78bSCy Schubert     free(data_ok_fn);
382*7f2fe78bSCy Schubert     *size = stbuf.st_size;
383*7f2fe78bSCy Schubert     return fd;
384*7f2fe78bSCy Schubert }
385*7f2fe78bSCy Schubert 
386*7f2fe78bSCy Schubert static void
close_database(krb5_context context,int fd)387*7f2fe78bSCy Schubert close_database(krb5_context context, int fd)
388*7f2fe78bSCy Schubert {
389*7f2fe78bSCy Schubert     int err;
390*7f2fe78bSCy Schubert 
391*7f2fe78bSCy Schubert     err = krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);
392*7f2fe78bSCy Schubert     if (err)
393*7f2fe78bSCy Schubert         com_err(progname, err, _("while unlocking database '%s'"), dbpathname);
394*7f2fe78bSCy Schubert     free(dbpathname);
395*7f2fe78bSCy Schubert     close(fd);
396*7f2fe78bSCy Schubert }
397*7f2fe78bSCy Schubert 
398*7f2fe78bSCy Schubert /*
399*7f2fe78bSCy Schubert  * Now we send over the database.  We use the following protocol:
400*7f2fe78bSCy Schubert  * Send over a KRB_SAFE message with the size.  Then we send over the
401*7f2fe78bSCy Schubert  * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
402*7f2fe78bSCy Schubert  * Then we expect to see a KRB_SAFE message with the size sent back.
403*7f2fe78bSCy Schubert  *
404*7f2fe78bSCy Schubert  * At any point in the protocol, we may send a KRB_ERROR message; this
405*7f2fe78bSCy Schubert  * will abort the entire operation.
406*7f2fe78bSCy Schubert  */
407*7f2fe78bSCy Schubert static void
xmit_database(krb5_context context,krb5_auth_context auth_context,krb5_creds * my_creds,int fd,int database_fd,off_t in_database_size)408*7f2fe78bSCy Schubert xmit_database(krb5_context context, krb5_auth_context auth_context,
409*7f2fe78bSCy Schubert               krb5_creds *my_creds, int fd, int database_fd,
410*7f2fe78bSCy Schubert               off_t in_database_size)
411*7f2fe78bSCy Schubert {
412*7f2fe78bSCy Schubert     krb5_int32 n;
413*7f2fe78bSCy Schubert     krb5_data inbuf, outbuf;
414*7f2fe78bSCy Schubert     char buf[KPROP_BUFSIZ], dbsize_buf[KPROP_DBSIZE_MAX_BUFSIZ];
415*7f2fe78bSCy Schubert     krb5_error_code retval;
416*7f2fe78bSCy Schubert     krb5_error *error;
417*7f2fe78bSCy Schubert     uint64_t database_size = in_database_size, send_size, sent_size;
418*7f2fe78bSCy Schubert 
419*7f2fe78bSCy Schubert     /* Send over the size. */
420*7f2fe78bSCy Schubert     inbuf = make_data(dbsize_buf, sizeof(dbsize_buf));
421*7f2fe78bSCy Schubert     encode_database_size(database_size, &inbuf);
422*7f2fe78bSCy Schubert     /* KPROP_CKSUMTYPE */
423*7f2fe78bSCy Schubert     retval = krb5_mk_safe(context, auth_context, &inbuf, &outbuf, NULL);
424*7f2fe78bSCy Schubert     if (retval) {
425*7f2fe78bSCy Schubert         com_err(progname, retval, _("while encoding database size"));
426*7f2fe78bSCy Schubert         send_error(context, my_creds, fd, _("while encoding database size"),
427*7f2fe78bSCy Schubert                    retval);
428*7f2fe78bSCy Schubert         exit(1);
429*7f2fe78bSCy Schubert     }
430*7f2fe78bSCy Schubert 
431*7f2fe78bSCy Schubert     retval = krb5_write_message(context, &fd, &outbuf);
432*7f2fe78bSCy Schubert     if (retval) {
433*7f2fe78bSCy Schubert         krb5_free_data_contents(context, &outbuf);
434*7f2fe78bSCy Schubert         com_err(progname, retval, _("while sending database size"));
435*7f2fe78bSCy Schubert         exit(1);
436*7f2fe78bSCy Schubert     }
437*7f2fe78bSCy Schubert     krb5_free_data_contents(context, &outbuf);
438*7f2fe78bSCy Schubert 
439*7f2fe78bSCy Schubert     /* Initialize the initial vector. */
440*7f2fe78bSCy Schubert     retval = krb5_auth_con_initivector(context, auth_context);
441*7f2fe78bSCy Schubert     if (retval) {
442*7f2fe78bSCy Schubert         send_error(context, my_creds, fd,
443*7f2fe78bSCy Schubert                    "failed while initializing i_vector", retval);
444*7f2fe78bSCy Schubert         com_err(progname, retval, _("while allocating i_vector"));
445*7f2fe78bSCy Schubert         exit(1);
446*7f2fe78bSCy Schubert     }
447*7f2fe78bSCy Schubert 
448*7f2fe78bSCy Schubert     /* Send over the file, block by block. */
449*7f2fe78bSCy Schubert     inbuf.data = buf;
450*7f2fe78bSCy Schubert     sent_size = 0;
451*7f2fe78bSCy Schubert     while ((n = read(database_fd, buf, sizeof(buf)))) {
452*7f2fe78bSCy Schubert         inbuf.length = n;
453*7f2fe78bSCy Schubert         retval = krb5_mk_priv(context, auth_context, &inbuf, &outbuf, NULL);
454*7f2fe78bSCy Schubert         if (retval) {
455*7f2fe78bSCy Schubert             snprintf(buf, sizeof(buf),
456*7f2fe78bSCy Schubert                      "while encoding database block starting at %"PRIu64,
457*7f2fe78bSCy Schubert                      sent_size);
458*7f2fe78bSCy Schubert             com_err(progname, retval, "%s", buf);
459*7f2fe78bSCy Schubert             send_error(context, my_creds, fd, buf, retval);
460*7f2fe78bSCy Schubert             exit(1);
461*7f2fe78bSCy Schubert         }
462*7f2fe78bSCy Schubert 
463*7f2fe78bSCy Schubert         retval = krb5_write_message(context, &fd, &outbuf);
464*7f2fe78bSCy Schubert         if (retval) {
465*7f2fe78bSCy Schubert             krb5_free_data_contents(context, &outbuf);
466*7f2fe78bSCy Schubert             com_err(progname, retval,
467*7f2fe78bSCy Schubert                     _("while sending database block starting at %"PRIu64),
468*7f2fe78bSCy Schubert                     sent_size);
469*7f2fe78bSCy Schubert             exit(1);
470*7f2fe78bSCy Schubert         }
471*7f2fe78bSCy Schubert         krb5_free_data_contents(context, &outbuf);
472*7f2fe78bSCy Schubert         sent_size += n;
473*7f2fe78bSCy Schubert         if (debug)
474*7f2fe78bSCy Schubert             printf("%"PRIu64" bytes sent.\n", sent_size);
475*7f2fe78bSCy Schubert     }
476*7f2fe78bSCy Schubert     if (sent_size != database_size) {
477*7f2fe78bSCy Schubert         com_err(progname, 0, _("Premature EOF found for database file!"));
478*7f2fe78bSCy Schubert         send_error(context, my_creds, fd,
479*7f2fe78bSCy Schubert                    "Premature EOF found for database file!",
480*7f2fe78bSCy Schubert                    KRB5KRB_ERR_GENERIC);
481*7f2fe78bSCy Schubert         exit(1);
482*7f2fe78bSCy Schubert     }
483*7f2fe78bSCy Schubert 
484*7f2fe78bSCy Schubert     /*
485*7f2fe78bSCy Schubert      * OK, we've sent the database; now let's wait for a success
486*7f2fe78bSCy Schubert      * indication from the remote end.
487*7f2fe78bSCy Schubert      */
488*7f2fe78bSCy Schubert     retval = krb5_read_message(context, &fd, &inbuf);
489*7f2fe78bSCy Schubert     if (retval) {
490*7f2fe78bSCy Schubert         com_err(progname, retval, _("while reading response from server"));
491*7f2fe78bSCy Schubert         exit(1);
492*7f2fe78bSCy Schubert     }
493*7f2fe78bSCy Schubert     /*
494*7f2fe78bSCy Schubert      * If we got an error response back from the server, display
495*7f2fe78bSCy Schubert      * the error message
496*7f2fe78bSCy Schubert      */
497*7f2fe78bSCy Schubert     if (krb5_is_krb_error(&inbuf)) {
498*7f2fe78bSCy Schubert         retval = krb5_rd_error(context, &inbuf, &error);
499*7f2fe78bSCy Schubert         if (retval) {
500*7f2fe78bSCy Schubert             com_err(progname, retval,
501*7f2fe78bSCy Schubert                     _("while decoding error response from server"));
502*7f2fe78bSCy Schubert             exit(1);
503*7f2fe78bSCy Schubert         }
504*7f2fe78bSCy Schubert         if (error->error == KRB_ERR_GENERIC) {
505*7f2fe78bSCy Schubert             if (error->text.data) {
506*7f2fe78bSCy Schubert                 fprintf(stderr, _("Generic remote error: %s\n"),
507*7f2fe78bSCy Schubert                         error->text.data);
508*7f2fe78bSCy Schubert             }
509*7f2fe78bSCy Schubert         } else if (error->error) {
510*7f2fe78bSCy Schubert             com_err(progname,
511*7f2fe78bSCy Schubert                     (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5,
512*7f2fe78bSCy Schubert                     _("signalled from server"));
513*7f2fe78bSCy Schubert             if (error->text.data) {
514*7f2fe78bSCy Schubert                 fprintf(stderr, _("Error text from server: %s\n"),
515*7f2fe78bSCy Schubert                         error->text.data);
516*7f2fe78bSCy Schubert             }
517*7f2fe78bSCy Schubert         }
518*7f2fe78bSCy Schubert         krb5_free_error(context, error);
519*7f2fe78bSCy Schubert         exit(1);
520*7f2fe78bSCy Schubert     }
521*7f2fe78bSCy Schubert 
522*7f2fe78bSCy Schubert     retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL);
523*7f2fe78bSCy Schubert     if (retval) {
524*7f2fe78bSCy Schubert         com_err(progname, retval,
525*7f2fe78bSCy Schubert                 "while decoding final size packet from server");
526*7f2fe78bSCy Schubert         exit(1);
527*7f2fe78bSCy Schubert     }
528*7f2fe78bSCy Schubert 
529*7f2fe78bSCy Schubert     retval = decode_database_size(&outbuf, &send_size);
530*7f2fe78bSCy Schubert     if (retval) {
531*7f2fe78bSCy Schubert         com_err(progname, retval, _("malformed sent database size message"));
532*7f2fe78bSCy Schubert         exit(1);
533*7f2fe78bSCy Schubert     }
534*7f2fe78bSCy Schubert     if (send_size != database_size) {
535*7f2fe78bSCy Schubert         com_err(progname, 0, _("Kpropd sent database size %"PRIu64
536*7f2fe78bSCy Schubert                                ", expecting %"PRIu64),
537*7f2fe78bSCy Schubert                 send_size, database_size);
538*7f2fe78bSCy Schubert         exit(1);
539*7f2fe78bSCy Schubert     }
540*7f2fe78bSCy Schubert     free(inbuf.data);
541*7f2fe78bSCy Schubert     free(outbuf.data);
542*7f2fe78bSCy Schubert }
543*7f2fe78bSCy Schubert 
544*7f2fe78bSCy Schubert static void
send_error(krb5_context context,krb5_creds * my_creds,int fd,char * err_text,krb5_error_code err_code)545*7f2fe78bSCy Schubert send_error(krb5_context context, krb5_creds *my_creds, int fd, char *err_text,
546*7f2fe78bSCy Schubert            krb5_error_code err_code)
547*7f2fe78bSCy Schubert {
548*7f2fe78bSCy Schubert     krb5_error error;
549*7f2fe78bSCy Schubert     const char *text;
550*7f2fe78bSCy Schubert     krb5_data outbuf;
551*7f2fe78bSCy Schubert 
552*7f2fe78bSCy Schubert     memset(&error, 0, sizeof(error));
553*7f2fe78bSCy Schubert     krb5_us_timeofday(context, &error.ctime, &error.cusec);
554*7f2fe78bSCy Schubert     error.server = my_creds->server;
555*7f2fe78bSCy Schubert     error.client = my_principal;
556*7f2fe78bSCy Schubert     error.error = err_code - ERROR_TABLE_BASE_krb5;
557*7f2fe78bSCy Schubert     if (error.error > 127)
558*7f2fe78bSCy Schubert         error.error = KRB_ERR_GENERIC;
559*7f2fe78bSCy Schubert     text = (err_text != NULL) ? err_text : error_message(err_code);
560*7f2fe78bSCy Schubert     error.text.length = strlen(text) + 1;
561*7f2fe78bSCy Schubert     error.text.data = strdup(text);
562*7f2fe78bSCy Schubert     if (error.text.data) {
563*7f2fe78bSCy Schubert         if (!krb5_mk_error(context, &error, &outbuf)) {
564*7f2fe78bSCy Schubert             (void)krb5_write_message(context, &fd, &outbuf);
565*7f2fe78bSCy Schubert             krb5_free_data_contents(context, &outbuf);
566*7f2fe78bSCy Schubert         }
567*7f2fe78bSCy Schubert         free(error.text.data);
568*7f2fe78bSCy Schubert     }
569*7f2fe78bSCy Schubert }
570*7f2fe78bSCy Schubert 
571*7f2fe78bSCy Schubert static void
update_last_prop_file(char * hostname,char * file_name)572*7f2fe78bSCy Schubert update_last_prop_file(char *hostname, char *file_name)
573*7f2fe78bSCy Schubert {
574*7f2fe78bSCy Schubert     char *file_last_prop;
575*7f2fe78bSCy Schubert     int fd;
576*7f2fe78bSCy Schubert     static char last_prop[] = ".last_prop";
577*7f2fe78bSCy Schubert 
578*7f2fe78bSCy Schubert     if (asprintf(&file_last_prop, "%s.%s%s", file_name, hostname,
579*7f2fe78bSCy Schubert                  last_prop) < 0) {
580*7f2fe78bSCy Schubert         com_err(progname, ENOMEM,
581*7f2fe78bSCy Schubert                 _("while allocating filename for update_last_prop_file"));
582*7f2fe78bSCy Schubert         return;
583*7f2fe78bSCy Schubert     }
584*7f2fe78bSCy Schubert     fd = THREEPARAMOPEN(file_last_prop, O_WRONLY | O_CREAT | O_TRUNC, 0600);
585*7f2fe78bSCy Schubert     if (fd < 0) {
586*7f2fe78bSCy Schubert         com_err(progname, errno, _("while creating 'last_prop' file, '%s'"),
587*7f2fe78bSCy Schubert                 file_last_prop);
588*7f2fe78bSCy Schubert         free(file_last_prop);
589*7f2fe78bSCy Schubert         return;
590*7f2fe78bSCy Schubert     }
591*7f2fe78bSCy Schubert     write(fd, "", 1);
592*7f2fe78bSCy Schubert     free(file_last_prop);
593*7f2fe78bSCy Schubert     close(fd);
594*7f2fe78bSCy Schubert }
595