Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / slave / kprop.c
1 /*
2
3 Copyright 1987, 1988 by the Student Information Processing Board
4         of the Massachusetts Institute of Technology
5
6 Permission to use, copy, modify, and distribute this software
7 and its documentation for any purpose and without fee is
8 hereby granted, provided that the above copyright notice
9 appear in all copies and that both that copyright notice and
10 this permission notice appear in supporting documentation,
11 and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
12 used in advertising or publicity pertaining to distribution
13 of the software without specific, written prior permission.
14 M.I.T. and the M.I.T. S.I.P.B. make no representations about
15 the suitability of this software for any purpose.  It is
16 provided "as is" without express or implied warranty.
17
18 */
19
20 #include "slav_locl.h"
21
22 RCSID("$Id: kprop.c,v 1.37 1999/09/16 20:41:59 assar Exp $");
23
24 #include "kprop.h"
25
26 static char kprop_version[KPROP_PROT_VERSION_LEN] = KPROP_PROT_VERSION;
27
28 int     debug = 0;
29
30 char    my_realm[REALM_SZ];
31 int     princ_data_size = 3 * sizeof(int32_t) + 3 * sizeof(unsigned char);
32 short   transfer_mode, net_transfer_mode;
33 int force_flag;
34 static char ok[] = ".dump_ok";
35
36 struct slave_host {
37     u_int32_t  net_addr;
38     char   *name;
39     char   *instance;
40     char   *realm;
41     int    not_time_yet;
42     int    succeeded;
43     struct slave_host *next;
44 };
45
46 static int
47 get_slaves(struct slave_host **psl,
48            const char *dir_path,
49            const char *file,
50            time_t ok_mtime)
51 {
52     FILE   *fin;
53     char    namebuf[128], *inst;
54     char   *pc;
55     struct hostent *host;
56     struct slave_host **th;
57     char   *last_prop_path;
58     struct stat stbuf;
59
60     if ((fin = fopen(file, "r")) == NULL)
61         err (1, "open(%s)", file);
62
63     th = psl;
64     while(fgets(namebuf, sizeof(namebuf), fin)){
65         if ((pc = strchr(namebuf, '\n'))) {
66             *pc = '\0';
67         } else {
68             if(strlen(namebuf) == sizeof(namebuf) - 1){
69                 warnx ("Hostname too long (>= %d chars) in '%s'.",
70                        (int) sizeof(namebuf), file);
71                 do{
72                     if(fgets(namebuf, sizeof(namebuf), fin) == NULL)
73                         break;
74                 }while(strchr(namebuf, '\n') == NULL);
75                 continue;
76             }
77         }
78         if(namebuf[0] == 0 || namebuf[0] == '#')
79             continue;
80         host = gethostbyname(namebuf);
81         if (host == NULL) {
82             warnx ("Ignoring host '%s' in '%s': %s", 
83                    namebuf, file,
84                    hstrerror(h_errno));
85             continue;
86         }
87         (*th) = (struct slave_host *) malloc(sizeof(struct slave_host));
88         if (!*th)
89             errx (1, "No memory reading host list from '%s'.",
90                     file);
91         memset(*th, 0, sizeof(struct slave_host));
92         (*th)->name = strdup(namebuf);
93         if ((*th)->name == NULL)
94             errx (1, "No memory reading host list from '%s'.",
95                   file);
96         /* get kerberos cannonical instance name */
97         inst = krb_get_phost ((*th)->name);
98         (*th)->instance = strdup(inst);
99         if ((*th)->instance == NULL)
100             errx (1, "No memory reading host list from '%s'.",
101                   file);
102         /* what a concept, slave servers in different realms! */
103         (*th)->realm = my_realm;
104         memcpy(&(*th)->net_addr, host->h_addr, sizeof((*th)->net_addr));
105         (*th)->not_time_yet = 0;
106         (*th)->succeeded = 0;
107         (*th)->next = NULL;
108         asprintf(&last_prop_path, "%s%s-last-prop", dir_path, (*th)->name);
109         if (last_prop_path == NULL)
110             errx (1, "malloc failed");
111         if (!force_flag
112             && !stat(last_prop_path, &stbuf)
113             && stbuf.st_mtime > ok_mtime) {
114             (*th)->not_time_yet = 1;
115             (*th)->succeeded = 1;       /* no change since last success */
116         }
117         free(last_prop_path);
118         th = &(*th)->next;
119     }
120     fclose(fin);
121     return (1);
122 }
123
124 /* The master -> slave protocol looks like this:
125      1) 8 byte version string
126      2) 2 bytes of "transfer mode" (net byte order of course)
127      3) ticket/authentication send by sendauth
128      4) 4 bytes of "block" length (u_int32_t)
129      5) data
130
131      4 and 5 repeat til EOF ...
132 */
133
134 static int
135 prop_to_slaves(struct slave_host *sl,
136                int fd,
137                const char *dir_path,
138                const char *fslv)
139 {
140     u_char buf[KPROP_BUFSIZ];
141     u_char obuf[KPROP_BUFSIZ + 64]; /* leave room for private msg overhead */
142     struct sockaddr_in sin, my_sin;
143     int     i, n, s;
144     struct slave_host *cs;      /* current slave */
145     char   my_host_name[MaxHostNameLen], *p_my_host_name;
146     char   kprop_service_instance[INST_SZ];
147     u_int32_t cksum;
148     u_int32_t length, nlength;
149     long   kerror;
150     KTEXT_ST     ticket;
151     CREDENTIALS  cred;
152     MSG_DAT msg_dat;
153     static char tkstring[] = "/tmp/kproptktXXXXXX";
154     des_key_schedule session_sched;
155     char   *last_prop_path;
156
157     close(mkstemp(tkstring));
158     krb_set_tkt_string(tkstring);
159     
160     memset(&sin, 0, sizeof sin);
161     sin.sin_family = AF_INET;
162     sin.sin_port = k_getportbyname ("krb_prop", "tcp", htons(KPROP_PORT));
163     sin.sin_addr.s_addr = INADDR_ANY;
164
165     for (i = 0; i < 5; i++) {   /* try each slave five times max */
166         for (cs = sl; cs; cs = cs->next) {
167             if (!cs->succeeded) {
168                 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
169                     err (1, "socket");
170                 memcpy(&sin.sin_addr, &cs->net_addr, 
171                       sizeof cs->net_addr);
172
173                 if (connect(s, (struct sockaddr *) &sin, sizeof sin) < 0) {
174                     warn ("connect(%s)", cs->name);
175                     close(s);
176                     continue;   /*** NEXT SLAVE ***/
177                 }
178                 
179                 /* for krb_mk_{priv, safe} */
180                 memset(&my_sin, 0, sizeof my_sin);
181                 n = sizeof my_sin;
182                 if (getsockname (s, (struct sockaddr *) &my_sin, &n) != 0) {
183                     warn ("getsockname(%s)", cs->name);
184                     close (s);
185                     continue;   /*** NEXT SLAVE ***/
186                 }
187                 if (n != sizeof (my_sin)) {
188                     warnx ("can't get socketname %s length", cs->name);
189                     close (s);
190                     continue;   /*** NEXT SLAVE ***/
191                 }
192                 
193                 /* Get ticket */
194                 kerror = krb_mk_req (&ticket, KPROP_SERVICE_NAME, 
195                                      cs->instance, cs->realm, (u_int32_t) 0);
196                 /* if ticket has expired try to get a new one, but
197                  * first get a TGT ...
198                  */
199                 if (kerror != MK_AP_OK) {
200                     if (gethostname (my_host_name, sizeof(my_host_name)) != 0) {
201                         warnx ("gethostname(%s): %s",
202                                my_host_name,
203                                hstrerror(h_errno));
204                         close (s);
205                         break;  /* next one can't work either! */
206                     }
207                     /* get canonical kerberos service instance name */
208                     p_my_host_name = krb_get_phost (my_host_name);
209                     /* copy it to make sure gethostbyname static doesn't
210                      * screw us. */
211                     strlcpy (kprop_service_instance,
212                                      p_my_host_name,
213                                      INST_SZ);
214                     kerror = krb_get_svc_in_tkt (KPROP_SERVICE_NAME, 
215 #if 0
216                                                  kprop_service_instance,
217 #else
218                                                  KRB_MASTER,
219 #endif
220                                                  my_realm,
221                                                  KRB_TICKET_GRANTING_TICKET,
222                                                  my_realm,
223                                                  96,
224                                                  KPROP_SRVTAB);
225                     if (kerror != INTK_OK) {
226                         warnx ("%s: %s.  While getting initial ticket\n",
227                                cs->name, krb_get_err_text(kerror));
228                         close (s);
229                         goto punt;
230                     }
231                     kerror = krb_mk_req (&ticket, KPROP_SERVICE_NAME, 
232                                          cs->instance, cs->realm,
233                                          (u_int32_t) 0);
234                 }
235                 if (kerror != MK_AP_OK) {
236                     warnx ("%s: krb_mk_req: %s",
237                            cs->name, krb_get_err_text(kerror));
238                     close (s);
239                     continue;   /*** NEXT SLAVE ***/
240                 }                   
241
242                 if (write(s, kprop_version, sizeof(kprop_version))
243                     != sizeof(kprop_version)) {
244                     warn ("%s", cs->name);
245                     close (s);
246                     continue;   /*** NEXT SLAVE ***/
247                 }
248
249                 net_transfer_mode = htons (transfer_mode);
250                 if (write(s, &net_transfer_mode, sizeof(net_transfer_mode))
251                     != sizeof(net_transfer_mode)) {
252                     warn ("write(%s)", cs->name);
253                     close (s);
254                     continue;   /*** NEXT SLAVE ***/
255                 }
256
257                 kerror = krb_get_cred (KPROP_SERVICE_NAME, cs->instance,
258                                        cs->realm, &cred);
259                 if (kerror != KSUCCESS) {
260                     warnx ("%s: %s.  Getting session key.", 
261                            cs->name, krb_get_err_text(kerror));
262                     close (s);
263                     continue;   /*** NEXT SLAVE ***/
264                 }
265 #ifdef NOENCRYPTION
266                 memset(session_sched, 0, sizeof(session_sched));
267 #else
268                 if (des_key_sched (&cred.session, session_sched)) {
269                     warnx ("%s: can't make key schedule.",
270                            cs->name);
271                     close (s);
272                     continue;   /*** NEXT SLAVE ***/
273                 }
274 #endif
275                 /* SAFE (quad_cksum) and CLEAR are just not good enough */
276                 cksum = 0;
277 #ifdef not_working_yet
278                 if (transfer_mode != KPROP_TRANSFER_PRIVATE) {
279                     cksum = get_data_checksum(fd, session_sched);
280                     lseek(fd, 0L, 0);
281                 }
282                 else
283 #endif
284                 {
285                     struct stat st;
286                     fstat (fd, &st);
287                     cksum = st.st_size;
288                 }
289                 kerror = krb_sendauth(KOPT_DO_MUTUAL,
290                                       s,
291                                       &ticket,
292                                       KPROP_SERVICE_NAME,
293                                       cs->instance,
294                                       cs->realm,
295                                       cksum,
296                                       &msg_dat,
297                                       &cred,
298                                       session_sched,
299                                       &my_sin,
300                                       &sin,
301                                       KPROP_PROT_VERSION);
302                 if (kerror != KSUCCESS) {
303                     warnx ("%s: krb_sendauth: %s.",
304                            cs->name, krb_get_err_text(kerror));
305                     close (s);
306                     continue;   /*** NEXT SLAVE ***/
307                 }
308
309                 lseek(fd, 0L, SEEK_SET); /* Rewind file before rereading it. */
310                 while ((n = read(fd, buf, sizeof buf))) {
311                     if (n < 0)
312                         err (1, "read");
313                     switch (transfer_mode) {
314                     case KPROP_TRANSFER_PRIVATE:
315                     case KPROP_TRANSFER_SAFE:
316                         if (transfer_mode == KPROP_TRANSFER_PRIVATE)
317                             length = krb_mk_priv (buf, obuf, n, 
318                                                   session_sched, &cred.session,
319                                                   &my_sin, &sin);
320                         else
321                             length = krb_mk_safe (buf, obuf, n,
322                                                   &cred.session,
323                                                   &my_sin, &sin);
324                         if (length == -1) {
325                             warnx ("%s: %s failed.",
326                                    cs->name,
327                                    (transfer_mode == KPROP_TRANSFER_PRIVATE) 
328                                    ? "krb_rd_priv" : "krb_rd_safe");
329                             close (s);
330                             continue; /*** NEXT SLAVE ***/
331                         }
332                         nlength = htonl(length);
333                         if (write(s, &nlength, sizeof nlength)
334                             != sizeof nlength) {
335                             warn ("write(%s)", cs->name);
336                             close (s);
337                             continue; /*** NEXT SLAVE ***/
338                         }
339                         if (write(s, obuf, length) != length) {
340                             warn ("write(%s)", cs->name);
341                             close(s);
342                             continue; /*** NEXT SLAVE ***/
343                         }
344                         break;
345                     case KPROP_TRANSFER_CLEAR:
346                         if (write(s, buf, n) != n) {
347                             warn ("write(%s)", cs->name);
348                             close(s);
349                             continue; /*** NEXT SLAVE ***/
350                         }
351                         break;
352                     }
353                 }
354                 close(s);
355                 cs->succeeded = 1;
356                 printf("%s: success.\n", cs->name);
357
358                 asprintf(&last_prop_path,
359                          "%s%s-last-prop",
360                          dir_path,
361                          cs->name);
362                 if (last_prop_path == NULL)
363                     errx (1, "malloc failed");
364
365                 unlink(last_prop_path);
366                 close(creat(last_prop_path, 0600));
367             }
368         }
369     }
370 punt:
371     
372     dest_tkt();
373     for (cs = sl; cs; cs = cs->next) {
374         if (!cs->succeeded)
375             return (0);         /* didn't get this slave */
376     }
377     return (1);
378 }
379
380 static void
381 usage(void)
382 {
383     /* already got floc and fslv, what is this? */
384     fprintf(stderr,
385             "\nUsage: kprop [-force] [-realm realm] [-private"
386 #ifdef not_safe_yet
387             "|-safe|-clear"
388 #endif
389             "] [data_file [slaves_file]]\n\n");
390     exit(1);
391 }
392
393
394 int
395 main(int argc, char **argv)
396 {
397     int     fd, i;
398     char   *floc, *floc_ok;
399     char   *fslv;
400     char   *dir_path;
401     struct stat stbuf, stbuf_ok;
402     time_t   l_init, l_final;
403     char   *pc;
404     int    l_diff;
405     static struct slave_host *slave_host_list = NULL;
406     struct slave_host *sh;
407
408     set_progname (argv[0]);
409
410     transfer_mode = KPROP_TRANSFER_PRIVATE;
411
412     time(&l_init);
413     pc = ctime(&l_init);
414     pc[strlen(pc) - 1] = '\0';
415     printf("\nStart slave propagation: %s\n", pc);
416  
417     floc = NULL;
418     fslv = NULL;
419
420     if (krb_get_lrealm(my_realm,1) != KSUCCESS)
421       errx (1, "Getting my kerberos realm.  Check krb.conf");
422
423     for (i = 1; i < argc; i++) 
424       switch (argv[i][0]) {
425       case '-':
426         if (strcmp (argv[i], "-private") == 0) 
427           transfer_mode = KPROP_TRANSFER_PRIVATE;
428 #ifdef not_safe_yet
429         else if (strcmp (argv[i], "-safe") == 0) 
430           transfer_mode = KPROP_TRANSFER_SAFE;
431         else if (strcmp (argv[i], "-clear") == 0) 
432           transfer_mode = KPROP_TRANSFER_CLEAR;
433 #endif
434         else if (strcmp (argv[i], "-realm") == 0) {
435             i++;
436             if (i < argc)
437                 strlcpy(my_realm, argv[i], REALM_SZ);
438             else
439                 usage();
440         } else if (strcmp (argv[i], "-force") == 0)
441             force_flag++;
442         else {
443             warnx("unknown control argument %s.", argv[i]);
444             usage ();
445         }
446         break;
447       default:
448         /* positional arguments are marginal at best ... */
449         if (floc == NULL)
450           floc = argv[i];
451         else {
452           if (fslv == NULL)
453             fslv = argv[i];
454           else 
455               usage();
456         }
457       }
458     if(floc == NULL)
459         floc = DB_DIR "/slave_dump";
460     if(fslv == NULL)
461         fslv = DB_DIR "/slaves";
462         
463     asprintf (&floc_ok, "%s%s", floc, ok);
464     if (floc_ok == NULL)
465         errx (1, "out of memory in copying %s", floc);
466
467     dir_path = strdup(fslv);
468     if(dir_path == NULL)
469         errx (1, "malloc failed");
470     pc = strrchr(dir_path, '/');
471     if (pc != NULL)
472         ++pc;
473     else
474         pc = dir_path;
475     *pc = '\0';
476
477     if ((fd = open(floc, O_RDONLY)) < 0)
478         err (1, "open(%s)", floc);
479     if (flock(fd, LOCK_SH | LOCK_NB))
480         err (1, "flock(%s)", floc);
481     if (stat(floc, &stbuf))
482         err (1, "stat(%s)", floc);
483     if (stat(floc_ok, &stbuf_ok))
484         err (1, "stat(%s)", floc_ok);
485     if (stbuf.st_mtime > stbuf_ok.st_mtime)
486         errx (1, "'%s' more recent than '%s'.", floc, floc_ok);
487     if (!get_slaves(&slave_host_list, dir_path, fslv, stbuf_ok.st_mtime))
488         errx (1, "can't read slave host file '%s'.", fslv);
489 #ifdef KPROP_DBG
490     {
491         struct slave_host *sh;
492         int     i;
493         fprintf(stderr, "\n\n");
494         fflush(stderr);
495         for (sh = slave_host_list; sh; sh = sh->next) {
496             fprintf(stderr, "slave %d: %s, %s", i++, sh->name,
497                     inet_ntoa(sh->net_addr));
498             fflush(stderr);
499         }
500     }
501 #endif                          /* KPROP_DBG */
502
503     if (!prop_to_slaves(slave_host_list, fd, dir_path, fslv))
504         errx (1, "propagation failed.");
505     if (flock(fd, LOCK_UN))
506         err (1, "flock(%s, LOCK_UN)", floc);
507     printf("\n\n");
508     for (sh = slave_host_list; sh; sh = sh->next) {
509         if (sh->not_time_yet)
510             printf(         "%s:\t\tNot time yet\n", sh->name);
511         else if (sh->succeeded)
512             printf(         "%s:\t\tSucceeded\n", sh->name);
513         else
514             fprintf(stderr, "%s:\t\tFAILED\n", sh->name);
515         fflush(stdout);
516     }
517
518     time(&l_final);
519     l_diff = l_final - l_init;
520     printf("propagation finished, %d:%02d:%02d elapsed\n",
521            l_diff / 3600, (l_diff % 3600) / 60, l_diff % 60);
522
523     exit(0);
524 }
525
526 #ifdef doesnt_work_yet
527 u_long get_data_checksum(fd, key_sched)
528      int fd;
529      des_key_schedule key_sched;
530 {
531         u_int32_t cksum = 0;
532         int n;
533         char buf[BUFSIZ];
534         u_int32_t obuf[2];
535
536         while (n = read(fd, buf, sizeof buf)) {
537             if (n < 0)
538                 err (1, "read");
539             cksum = cbc_cksum(buf, obuf, n, key_sched, key_sched);
540         }
541         return cksum;
542 }
543 #endif