Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / kadmin / ksrvutil.c
1 /* 
2   Copyright (C) 1989 by the Massachusetts Institute of Technology
3
4    Export of this software from the United States of America is assumed
5    to require a specific license from the United States Government.
6    It is the responsibility of any person or organization contemplating
7    export to obtain such a license before exporting.
8
9 WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 distribute this software and its documentation for any purpose and
11 without fee is hereby granted, provided that the above copyright
12 notice appear in all copies and that both that copyright notice and
13 this permission notice appear in supporting documentation, and that
14 the name of M.I.T. not be used in advertising or publicity pertaining
15 to distribution of the software without specific, written prior
16 permission.  M.I.T. makes no representations about the suitability of
17 this software for any purpose.  It is provided "as is" without express
18 or implied warranty.
19
20   */
21 /* $FreeBSD: src/crypto/kerberosIV/kadmin/ksrvutil.c,v 1.1.1.3.2.1 2003/02/14 22:37:37 nectar Exp $ */
22
23 /*
24  * list and update contents of srvtab files
25  */
26
27 /*
28  * ksrvutil
29  * list and update the contents of srvtab files
30  */
31
32 #include "kadm_locl.h"
33
34 RCSID("$Id: ksrvutil.c,v 1.50 1999/11/13 06:33:59 assar Exp $");
35
36 #include "ksrvutil.h"
37
38 #ifdef NOENCRYPTION
39 #define read_long_pw_string placebo_read_pw_string
40 #else /* NOENCRYPTION */
41 #define read_long_pw_string des_read_pw_string
42 #endif /* NOENCRYPTION */
43
44 #define SRVTAB_MODE 0600        /* rw------- */
45 #define PAD "  "
46 #define VNO_HEADER "Version"
47 #define VNO_FORMAT "%4d   "
48 #define KEY_HEADER "       Key       " /* 17 characters long */
49 #define PRINC_HEADER "  Principal\n"
50 #define PRINC_FORMAT "%s"
51
52 char u_name[ANAME_SZ];
53 char u_inst[INST_SZ];
54 char u_realm[REALM_SZ];
55
56 int destroyp = FALSE;           /* Should the ticket file be destroyed? */
57
58 static unsigned short
59 get_mode(char *filename)
60 {
61     struct stat statbuf;
62     unsigned short mode;
63
64     memset(&statbuf, 0, sizeof(statbuf));
65     
66     if (stat(filename, &statbuf) < 0) 
67         mode = SRVTAB_MODE;
68     else
69         mode = statbuf.st_mode;
70
71     return(mode);
72 }
73
74 static void
75 copy_keyfile(char *keyfile, char *backup_keyfile)
76 {
77     int keyfile_fd;
78     int backup_keyfile_fd;
79     int keyfile_mode;
80     char buf[BUFSIZ];           /* for copying keyfiles */
81     int rcount;                 /* for copying keyfiles */
82     int try_again;
83     
84     memset(buf, 0, sizeof(buf));
85     
86     do {
87         try_again = FALSE;
88         if ((keyfile_fd = open(keyfile, O_RDONLY, 0)) < 0) {
89             if (errno != ENOENT)
90               err (1, "open %s", keyfile);
91             else {
92                 try_again = TRUE;
93                 if ((keyfile_fd = 
94                      open(keyfile, 
95                           O_WRONLY | O_TRUNC | O_CREAT, SRVTAB_MODE)) < 0)
96                   err(1, "create %s", keyfile);
97                 else
98                     if (close(keyfile_fd) < 0)
99                       err (1, "close %s", keyfile);
100             }
101         }
102     } while(try_again);
103
104     keyfile_mode = get_mode(keyfile);
105
106     if ((backup_keyfile_fd = 
107          open(backup_keyfile, O_WRONLY | O_TRUNC | O_CREAT, 
108               keyfile_mode)) < 0)
109         err (1, "open %s", backup_keyfile);
110     do {
111         if ((rcount = read(keyfile_fd, buf, sizeof(buf))) < 0)
112             err (1, "read %s", keyfile);
113         if (rcount && (write(backup_keyfile_fd, buf, rcount) != rcount))
114             err (1, "write %s", backup_keyfile);
115     } while (rcount);
116     if (close(backup_keyfile_fd) < 0)
117         err(1, "close %s", backup_keyfile);
118     if (close(keyfile_fd) < 0)
119         err(1, "close %s", keyfile);
120 }
121
122 void
123 leave(char *str, int x)
124 {
125     if (str)
126         fprintf(stderr, "%s\n", str);
127     if (destroyp)
128          dest_tkt();
129     exit(x);
130 }
131
132 void
133 safe_read_stdin(char *prompt, char *buf, size_t size)
134 {
135     printf("%s", prompt);
136     fflush(stdout);
137     memset(buf, 0, size);
138     if (read(0, buf, size - 1) < 0) {
139         warn("read stdin");
140         leave(NULL, 1);
141     }
142     buf[strlen(buf)-1] = 0;
143 }
144
145 void
146 safe_write(char *filename, int fd, void *buf, size_t len)
147 {
148     if (write(fd, buf, len) != len) {
149         warn("write %s", filename);
150         close(fd);
151         leave("In progress srvtab in this file.", 1);
152     }
153 }
154
155 static int
156 yes_no(char *string, int dflt)
157 {
158   char ynbuf[5];
159   
160   printf("%s (y,n) [%c]", string, dflt?'y':'n');
161   for (;;) {
162     safe_read_stdin("", ynbuf, sizeof(ynbuf));
163     
164     if ((ynbuf[0] == 'n') || (ynbuf[0] == 'N'))
165       return(0);
166     else if ((ynbuf[0] == 'y') || (ynbuf[0] == 'Y'))
167       return(1);
168     else if(ynbuf[0] == 0)
169       return dflt;
170     else {
171       printf("Please enter 'y' or 'n': ");
172       fflush(stdout);
173     }
174   }
175 }
176
177 int yn(char *string)
178 {
179   return yes_no(string, 1);
180 }
181
182 int ny(char *string)
183 {
184   return yes_no(string, 0);
185 }
186
187 static void
188 append_srvtab(char *filename, int fd, char *sname, char *sinst, char *srealm,
189               unsigned char key_vno, unsigned char *key)
190 {
191   /* Add one to append null */
192     safe_write(filename, fd, sname, strlen(sname) + 1);
193     safe_write(filename, fd, sinst, strlen(sinst) + 1);
194     safe_write(filename, fd, srealm, strlen(srealm) + 1);
195     safe_write(filename, fd, &key_vno, 1);
196     safe_write(filename, fd, key, sizeof(des_cblock));
197     fsync(fd);
198 }    
199
200 static void
201 print_key(unsigned char *key)
202 {
203     int i;
204
205     for (i = 0; i < 4; i++)
206         printf("%02x", key[i]);
207     printf(" ");
208     for (i = 4; i < 8; i++)
209         printf("%02x", key[i]);
210 }
211
212 static void
213 print_name(char *name, char *inst, char *realm)
214 {
215     printf("%s", krb_unparse_name_long(name, inst, realm));
216 }
217
218 static int
219 get_svc_new_key(des_cblock *new_key, char *sname, char *sinst,
220                 char *srealm, char *keyfile)
221 {
222     int status = KADM_SUCCESS;
223
224     if (((status = krb_get_svc_in_tkt(sname, sinst, srealm, PWSERV_NAME,
225                                       KADM_SINST, 1, keyfile)) == KSUCCESS) &&
226         ((status = kadm_init_link(PWSERV_NAME, KRB_MASTER, srealm)) == 
227          KADM_SUCCESS)) {
228 #ifdef NOENCRYPTION
229         memset(new_key, 0, sizeof(des_cblock));
230         (*new_key)[0] = (unsigned char) 1;
231 #else /* NOENCRYPTION */
232         des_random_key(*new_key);
233 #endif /* NOENCRYPTION */
234         return(KADM_SUCCESS);
235     }
236     
237     return(status);
238 }
239
240 static void
241 get_key_from_password(des_cblock (*key), char *cellname)
242 {
243     char password[MAX_KPW_LEN]; /* storage for the password */
244
245     if (read_long_pw_string(password, sizeof(password)-1, "Password: ", 1))
246         leave("Error reading password.", 1);
247
248 #ifdef NOENCRYPTION
249     memset(key, 0, sizeof(des_cblock));
250     (*key)[0] = (unsigned char) 1;
251 #else /* NOENCRYPTION */
252     if (strlen(cellname) == 0)
253       des_string_to_key(password, key);
254     else
255       afs_string_to_key(password, cellname, key);
256 #endif /* NOENCRYPTION */
257     memset(password, 0, sizeof(password));
258 }    
259
260 static void
261 usage(void)
262 {
263     fprintf(stderr, "Usage: ksrvutil [-f keyfile] [-i] [-k] ");
264     fprintf(stderr, "[-p principal] [-r realm] [-u]");
265     fprintf(stderr, "[-c AFS cellname] ");
266     fprintf(stderr, "{list | change | add | get | delete}\n");
267     fprintf(stderr, "   -i causes the program to ask for "
268             "confirmation before changing keys.\n");
269     fprintf(stderr, "   -k causes the key to printed for list or change.\n");
270     fprintf(stderr, "   -u creates one keyfile for each principal "
271             "(only used with `get')\n");
272     exit(1);
273 }
274
275 int
276 main(int argc, char **argv)
277 {
278     char sname[ANAME_SZ];       /* name of service */
279     char sinst[INST_SZ];        /* instance of service */
280     char srealm[REALM_SZ];      /* realm of service */
281     unsigned char key_vno;      /* key version number */
282     int status;                 /* general purpose error status */
283     des_cblock new_key;
284     des_cblock old_key;
285     char change_tkt[MaxPathLen]; /* Ticket to use for key change */
286     char keyfile[MaxPathLen];   /* Original keyfile */
287     char work_keyfile[MaxPathLen]; /* Working copy of keyfile */
288     char backup_keyfile[MaxPathLen]; /* Backup copy of keyfile */
289     unsigned short keyfile_mode; /* Protections on keyfile */
290     int work_keyfile_fd = -1;   /* Initialize so that */
291     int backup_keyfile_fd = -1; /* compiler doesn't complain */
292     char local_realm[REALM_SZ]; /* local kerberos realm */
293     char cellname[1024];         /* AFS cell name */
294     int c;
295     int interactive = FALSE;
296     int list = FALSE;
297     int change = FALSE;
298     int unique_filename = FALSE;
299     int add = FALSE;
300     int delete = FALSE;
301     int get = FALSE;
302     int key = FALSE;            /* do we show keys? */
303     int arg_entered = FALSE;
304     int change_this_key = FALSE;
305     char databuf[BUFSIZ];
306     int first_printed = FALSE;  /* have we printed the first item? */
307     
308     memset(sname, 0, sizeof(sname));
309     memset(sinst, 0, sizeof(sinst));
310     memset(srealm, 0, sizeof(srealm));
311           
312     memset(change_tkt, 0, sizeof(change_tkt));
313     memset(keyfile, 0, sizeof(keyfile));
314     memset(work_keyfile, 0, sizeof(work_keyfile));
315     memset(backup_keyfile, 0, sizeof(backup_keyfile));
316     memset(local_realm, 0, sizeof(local_realm));
317     memset(cellname, 0, sizeof(cellname));
318     
319     set_progname (argv[0]);
320
321     if (krb_get_default_principal(u_name, u_inst, u_realm) < 0)
322         errx (1, "could not get default principal");
323
324     /* This is used only as a default for adding keys */
325     if (krb_get_lrealm(local_realm, 1) != KSUCCESS)
326         strlcpy(local_realm,
327                         KRB_REALM,
328                         sizeof(local_realm));
329     
330     while((c = getopt(argc, argv, "ikc:f:p:r:u")) != -1) {
331         switch (c) {
332         case 'i':
333             interactive++;
334             break;
335         case 'k':
336             key++;
337             break;
338         case 'c':
339             strlcpy(cellname, optarg, sizeof(cellname));
340             break;
341         case 'f':
342             strlcpy(keyfile, optarg, sizeof(keyfile));
343             break;
344         case 'p':
345             if((status = kname_parse (u_name, u_inst, u_realm, optarg)) !=
346                KSUCCESS)
347                 errx (1, "principal %s: %s", optarg,
348                       krb_get_err_text(status));
349             break;
350         case 'r':
351             strlcpy(u_realm, optarg, sizeof(u_realm));
352             break;
353         case 'u':
354             unique_filename = 1;
355             break;
356         case '?':
357             usage();
358         }
359     }
360     if (optind >= argc)
361         usage();
362     if (*u_realm == '\0')
363          strlcpy (u_realm, local_realm, sizeof(u_realm));
364     if (strcmp(argv[optind], "list") == 0) {
365         if (arg_entered)
366             usage();
367         else {
368             arg_entered++;
369             list++;
370         }
371     }
372     else if (strcmp(argv[optind], "change") == 0) {
373         if (arg_entered)
374             usage();
375         else {
376             arg_entered++;
377             change++;
378         }
379     }
380     else if (strcmp(argv[optind], "add") == 0) {
381         if (arg_entered)
382             usage();
383         else {
384             arg_entered++;
385             add++;
386         }
387     }
388     else if (strcmp(argv[optind], "get") == 0) {
389         if (arg_entered)
390             usage();
391         else {
392             arg_entered++;
393             get++;
394         }
395     }
396     else if (strcmp(argv[optind], "delete") == 0) {
397         if (arg_entered)
398             usage();
399         else {
400             arg_entered++;
401             delete++;
402         }
403     }
404     else
405         usage();
406     ++optind;
407     
408     if (!arg_entered)
409         usage();
410
411     if(unique_filename && !get)
412         warnx("`-u' flag is only used with `get'");
413
414     if (!keyfile[0])
415         strlcpy(keyfile, KEYFILE, sizeof(keyfile));
416
417     strlcpy(work_keyfile, keyfile, sizeof(work_keyfile));
418     strlcpy(backup_keyfile, keyfile, sizeof(backup_keyfile));
419     
420     if (change || add || (get && !unique_filename) || delete) {
421         snprintf(work_keyfile, sizeof(work_keyfile), "%s.work", keyfile);
422         snprintf(backup_keyfile, sizeof(backup_keyfile), "%s.old", keyfile);
423         copy_keyfile(keyfile, backup_keyfile);
424     }
425     
426     if (add || (get && !unique_filename))
427         copy_keyfile(backup_keyfile, work_keyfile);
428
429     keyfile_mode = get_mode(keyfile);
430
431     if (change || list || delete)
432         if ((backup_keyfile_fd = open(backup_keyfile, O_RDONLY, 0)) < 0)
433             err (1, "open %s", backup_keyfile);
434
435     if (change || delete) {
436         if ((work_keyfile_fd = 
437              open(work_keyfile, O_WRONLY | O_CREAT | O_TRUNC, 
438                   SRVTAB_MODE)) < 0)
439             err (1, "creat %s", work_keyfile);
440     }
441     else if (add) {
442         if ((work_keyfile_fd =
443              open(work_keyfile, O_APPEND | O_WRONLY, SRVTAB_MODE)) < 0)
444             err (1, "open with append %s", work_keyfile );
445     }
446     else if (get && !unique_filename) {
447         if ((work_keyfile_fd =
448              open(work_keyfile, O_RDWR | O_CREAT, SRVTAB_MODE)) < 0)
449             err (1, "open for writing %s", work_keyfile);
450     }
451     
452     if (change || list || delete) {
453         while ((getst(backup_keyfile_fd, sname, SNAME_SZ) > 0) &&
454                (getst(backup_keyfile_fd, sinst, INST_SZ) > 0) &&
455                (getst(backup_keyfile_fd, srealm, REALM_SZ) > 0) &&
456                (read(backup_keyfile_fd, &key_vno, 1) > 0) &&
457                (read(backup_keyfile_fd, old_key, sizeof(old_key)) > 0)) {
458             if (list) {
459                 if (!first_printed) {
460                     printf(VNO_HEADER);
461                     printf(PAD);
462                     if (key) {
463                         printf(KEY_HEADER);
464                         printf(PAD);
465                     }
466                     printf(PRINC_HEADER);
467                     first_printed = 1;
468                 }
469                 printf(VNO_FORMAT, key_vno);
470                 printf(PAD);
471                 if (key) {
472                     print_key(old_key);
473                     printf(PAD);
474                 }
475                 print_name(sname, sinst, srealm);
476                 printf("\n");
477             }
478             else if (change) {
479                 snprintf(change_tkt, sizeof(change_tkt), "%s_ksrvutil.%u",
480                          TKT_ROOT, (unsigned)getpid());
481                 krb_set_tkt_string(change_tkt);
482                 destroyp = TRUE;
483
484                 printf("\nPrincipal: ");
485                 print_name(sname, sinst, srealm);
486                 printf("; version %d\n", key_vno);
487                 if (interactive)
488                     change_this_key = yn("Change this key?");
489                 else
490                     change_this_key = 1;
491                 
492                 if (change_this_key)
493                     printf("Changing to version %d.\n", key_vno + 1);
494                 else if (change)
495                     printf("Not changing this key.\n");
496                 
497                 if (change_this_key) {
498                     /* 
499                      * Pick a new key and determine whether or not
500                      * it is safe to change
501                      */
502                     if ((status = 
503                          get_svc_new_key(&new_key, sname, sinst, 
504                                          srealm, keyfile)) == KADM_SUCCESS)
505                         key_vno++;
506                     else {
507                         memcpy(new_key, old_key, sizeof(new_key));
508                         warnx ("Key NOT changed: %s\n",
509                                krb_get_err_text(status));
510                         change_this_key = FALSE;
511                     }
512                 }
513                 else 
514                     memcpy(new_key, old_key, sizeof(new_key));
515                 append_srvtab(work_keyfile, work_keyfile_fd, 
516                               sname, sinst, srealm, key_vno, new_key);
517                 if (key && change_this_key) {
518                     printf("Old key: ");
519                     print_key(old_key);
520                     printf("; new key: ");
521                     print_key(new_key);
522                     printf("\n");
523                 }
524                 if (change_this_key) {
525                     if ((status = kadm_change_pw(new_key)) == KADM_SUCCESS) {
526                         printf("Key changed.\n");
527                         dest_tkt();
528                     }
529                     else {
530                         com_err(__progname, status, 
531                                 " attempting to change password.");
532                         dest_tkt();
533                         /* XXX This knows the format of a keyfile */
534                         if (lseek(work_keyfile_fd, -9, SEEK_CUR) >= 0) {
535                             key_vno--;
536                             safe_write(work_keyfile,
537                                        work_keyfile_fd, &key_vno, 1);
538                             safe_write(work_keyfile, work_keyfile_fd,
539                                        old_key, sizeof(des_cblock));
540                             fsync(work_keyfile_fd);
541                             fprintf(stderr,"Key NOT changed.\n");
542                         } else {
543                             warn ("Unable to revert keyfile");
544                             leave("", 1);
545                         }
546                     }
547                 }
548             } else if(delete) {
549                 int delete_this_key;
550                 printf("\nPrincipal: ");
551                 print_name(sname, sinst, srealm);
552                 printf("; version %d\n", key_vno);
553                 delete_this_key = yn("Delete this key?");
554                 
555                 if (delete_this_key)
556                     printf("Deleting this key.\n");
557                 
558                 if (!delete_this_key) {
559                     append_srvtab(work_keyfile, work_keyfile_fd, 
560                                   sname, sinst, srealm, key_vno, old_key);
561                 }
562             }
563             memset(old_key, 0, sizeof(des_cblock));
564             memset(new_key, 0, sizeof(des_cblock));
565         }
566     }
567     else if (add) {
568         do {
569             do {
570                 char *p;
571
572                 safe_read_stdin("Name: ", databuf, sizeof(databuf));
573                 p = strchr(databuf, '.');
574                 if (p != NULL) {
575                     *p++ = '\0';
576                     strlcpy (sname, databuf, sizeof(sname));
577                     strlcpy (sinst, p, sizeof(sinst));
578                 } else {
579                     strlcpy (sname, databuf, sizeof(sname));
580                     safe_read_stdin("Instance: ", databuf, sizeof(databuf));
581                     strlcpy (sinst, databuf, sizeof(databuf));
582                 }
583
584                 safe_read_stdin("Realm: ", databuf, sizeof(databuf));
585                 if (databuf[0] != '\0')
586                     strlcpy (srealm, databuf, sizeof(srealm));
587                 else
588                     strlcpy (srealm, local_realm, sizeof(srealm));
589
590                 safe_read_stdin("Version number: ", databuf, sizeof(databuf));
591                 key_vno = atoi(databuf);
592                 if (!srealm[0])
593                     strlcpy(srealm, local_realm, sizeof(srealm));
594                 printf("New principal: ");
595                 print_name(sname, sinst, srealm);
596                 printf("; version %d\n", key_vno);
597             } while (!yn("Is this correct?"));
598             get_key_from_password(&new_key, cellname);
599             if (key) {
600                 printf("Key: ");
601                 print_key(new_key);
602                 printf("\n");
603             }
604             append_srvtab(work_keyfile, work_keyfile_fd, 
605                           sname, sinst, srealm, key_vno, new_key);
606             printf("Key successfully added.\n");
607         } while (yn("Would you like to add another key?"));
608     }
609     else if (get) {
610         ksrvutil_get(unique_filename, work_keyfile_fd, work_keyfile,
611                      argc - optind, argv + optind);
612     }
613
614     if (change || list || delete) 
615         if (close(backup_keyfile_fd) < 0)
616             warn ("close %s", backup_keyfile);
617     
618     if (change || add || (get && !unique_filename) || delete) {
619         if (close(work_keyfile_fd) < 0)
620             err (1, "close %s", work_keyfile);
621         if (rename(work_keyfile, keyfile) < 0)
622             err (1, "rename(%s, %s)", work_keyfile, keyfile);
623         chmod(backup_keyfile, keyfile_mode);
624         chmod(keyfile, keyfile_mode);
625         printf("Old keyfile in %s.\n", backup_keyfile);
626     }
627     return 0;
628 }