2 * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. 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.
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
38 static int version_flag
;
40 static char *ktname
= HPROP_KEYTAB
;
41 static char *database
;
42 static char *mkeyfile
;
44 static int verbose_flag
;
45 static int encrypt_flag
;
46 static int decrypt_flag
;
47 static EncryptionKey mkey5
;
48 static krb5_data msched5
;
52 static char *afs_cell
;
58 static int kaspecials_flag
;
63 open_socket(krb5_context context
, const char *hostname
)
65 struct addrinfo
*ai
, *a
;
66 struct addrinfo hints
;
68 char portstr
[NI_MAXSERV
];
70 memset (&hints
, 0, sizeof(hints
));
71 hints
.ai_socktype
= SOCK_STREAM
;
72 hints
.ai_protocol
= IPPROTO_TCP
;
74 snprintf (portstr
, sizeof(portstr
),
76 ntohs(krb5_getportbyname (context
, "hprop", "tcp", HPROP_PORT
)));
78 error
= getaddrinfo (hostname
, portstr
, &hints
, &ai
);
80 warnx ("%s: %s", hostname
, gai_strerror(error
));
84 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
87 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
90 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
91 warn ("connect(%s)", hostname
);
98 warnx ("failed to contact %s", hostname
);
104 krb5_context context
;
105 krb5_auth_context auth_context
;
109 int hdb_entry2value(krb5_context
, hdb_entry
*, krb5_data
*);
111 static krb5_error_code
112 v5_prop(krb5_context context
, HDB
*db
, hdb_entry
*entry
, void *appdata
)
115 struct prop_data
*pd
= appdata
;
119 _hdb_seal_keys_int(entry
, 0, msched5
);
121 _hdb_unseal_keys_int(entry
, 0, msched5
);
123 ret
= hdb_entry2value(context
, entry
, &data
);
127 ret
= send_clear(context
, STDOUT_FILENO
, data
);
129 ret
= send_priv(context
, pd
->auth_context
, &data
, pd
->sock
);
130 krb5_data_free(&data
);
135 static des_cblock mkey4
;
136 static des_key_schedule msched4
;
137 static char realm_buf
[REALM_SZ
];
140 v4_prop(void *arg
, Principal
*p
)
142 struct prop_data
*pd
= arg
;
146 memset(&ent
, 0, sizeof(ent
));
148 ret
= krb5_425_conv_principal(pd
->context
, p
->name
, p
->instance
, realm
,
151 krb5_warn(pd
->context
, ret
,
152 "krb5_425_conv_principal %s.%s@%s",
153 p
->name
, p
->instance
, realm
);
159 krb5_unparse_name_short(pd
->context
, ent
.principal
, &s
);
160 krb5_warnx(pd
->context
, "%s.%s -> %s", p
->name
, p
->instance
, s
);
164 ent
.kvno
= p
->key_version
;
166 ent
.keys
.val
= malloc(ent
.keys
.len
* sizeof(*ent
.keys
.val
));
167 ent
.keys
.val
[0].mkvno
= NULL
;
169 ent
.keys
.val
[0].mkvno
= malloc (sizeof(*ent
.keys
.val
[0].mkvno
));
170 *(ent
.keys
.val
[0].mkvno
) = p
->kdc_key_ver
; /* XXX */
172 ent
.keys
.val
[0].salt
= calloc(1, sizeof(*ent
.keys
.val
[0].salt
));
173 ent
.keys
.val
[0].salt
->type
= pa_pw_salt
;
174 ent
.keys
.val
[0].key
.keytype
= ETYPE_DES_CBC_MD5
;
175 krb5_data_alloc(&ent
.keys
.val
[0].key
.keyvalue
, sizeof(des_cblock
));
178 unsigned char *key
= ent
.keys
.val
[0].key
.keyvalue
.data
;
179 unsigned char null_key
[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
180 memcpy(key
, &p
->key_low
, 4);
181 memcpy(key
+ 4, &p
->key_high
, 4);
182 kdb_encrypt_key((des_cblock
*)key
, (des_cblock
*)key
,
183 &mkey4
, msched4
, DES_DECRYPT
);
184 if(memcmp(key
, null_key
, sizeof(null_key
)) == 0) {
185 free_Key(&ent
.keys
.val
[0]);
187 ent
.flags
.invalid
= 1;
190 copy_Key(&ent
.keys
.val
[0], &ent
.keys
.val
[1]);
191 ent
.keys
.val
[1].key
.keytype
= ETYPE_DES_CBC_MD4
;
192 copy_Key(&ent
.keys
.val
[0], &ent
.keys
.val
[2]);
193 ent
.keys
.val
[2].key
.keytype
= ETYPE_DES_CBC_CRC
;
196 *ent
.max_life
= krb_life_to_time(0, p
->max_life
);
197 if(*ent
.max_life
== NEVERDATE
){
203 *ent
.pw_end
= p
->exp_date
;
204 ret
= krb5_make_principal(pd
->context
, &ent
.created_by
.principal
,
210 krb5_warn(pd
->context
, ret
, "krb5_make_principal");
214 ent
.created_by
.time
= time(NULL
);
215 ALLOC(ent
.modified_by
);
216 ret
= krb5_425_conv_principal(pd
->context
, p
->mod_name
, p
->mod_instance
,
217 realm
, &ent
.modified_by
->principal
);
219 krb5_warn(pd
->context
, ret
, "%s.%s@%s", p
->name
, p
->instance
, realm
);
220 ent
.modified_by
->principal
= NULL
;
224 ent
.modified_by
->time
= p
->mod_date
;
226 ent
.flags
.forwardable
= 1;
227 ent
.flags
.renewable
= 1;
228 ent
.flags
.proxiable
= 1;
229 ent
.flags
.postdate
= 1;
230 ent
.flags
.client
= 1;
231 ent
.flags
.server
= 1;
233 /* special case password changing service */
234 if(strcmp(p
->name
, "changepw") == 0 &&
235 strcmp(p
->instance
, "kerberos") == 0) {
236 ent
.flags
.forwardable
= 0;
237 ent
.flags
.renewable
= 0;
238 ent
.flags
.proxiable
= 0;
239 ent
.flags
.postdate
= 0;
240 ent
.flags
.initial
= 1;
241 ent
.flags
.change_pw
= 1;
244 ret
= v5_prop(pd
->context
, NULL
, &ent
, pd
);
246 if (strcmp (p
->name
, "krbtgt") == 0
247 && strcmp (realm
, p
->instance
) != 0) {
248 krb5_free_principal (pd
->context
, ent
.principal
);
249 ret
= krb5_425_conv_principal (pd
->context
, p
->name
,
253 ret
= v5_prop (pd
->context
, NULL
, &ent
, pd
);
257 hdb_free_entry(pd
->context
, &ent
);
265 /* read a `ka_entry' from `fd' at offset `pos' */
267 read_block(krb5_context context
, int fd
, int32_t pos
, void *buf
, size_t len
)
270 if(lseek(fd
, 64 + pos
, SEEK_SET
) == (off_t
)-1)
271 krb5_err(context
, 1, errno
, "lseek(%u)", 64 + pos
);
272 ret
= read(fd
, buf
, len
);
274 krb5_err(context
, 1, errno
, "read(%u)", len
);
276 krb5_errx(context
, 1, "read(%u) = %u", len
, ret
);
280 ka_convert(struct prop_data
*pd
, int fd
, struct ka_entry
*ent
,
283 int32_t flags
= ntohl(ent
->flags
);
288 && (flags
& KAFNORMAL
) == 0) /* remove special entries */
290 memset(&hdb
, 0, sizeof(hdb
));
291 ret
= krb5_425_conv_principal(pd
->context
, ent
->name
, ent
->instance
, realm
,
294 krb5_warn(pd
->context
, ret
,
295 "krb5_425_conv_principal (%s.%s@%s)",
296 ent
->name
, ent
->instance
, realm
);
299 hdb
.kvno
= ntohl(ent
->kvno
);
301 hdb
.keys
.val
= malloc(hdb
.keys
.len
* sizeof(*hdb
.keys
.val
));
302 hdb
.keys
.val
[0].mkvno
= NULL
;
303 hdb
.keys
.val
[0].salt
= calloc(1, sizeof(*hdb
.keys
.val
[0].salt
));
304 hdb
.keys
.val
[0].salt
->type
= hdb_afs3_salt
;
305 hdb
.keys
.val
[0].salt
->salt
.data
= strdup(cell
);
306 hdb
.keys
.val
[0].salt
->salt
.length
= strlen(cell
);
308 hdb
.keys
.val
[0].key
.keytype
= ETYPE_DES_CBC_MD5
;
309 krb5_data_copy(&hdb
.keys
.val
[0].key
.keyvalue
, ent
->key
, sizeof(ent
->key
));
310 copy_Key(&hdb
.keys
.val
[0], &hdb
.keys
.val
[1]);
311 hdb
.keys
.val
[1].key
.keytype
= ETYPE_DES_CBC_MD4
;
312 copy_Key(&hdb
.keys
.val
[0], &hdb
.keys
.val
[2]);
313 hdb
.keys
.val
[2].key
.keytype
= ETYPE_DES_CBC_CRC
;
316 *hdb
.max_life
= ntohl(ent
->max_life
);
318 if(ntohl(ent
->pw_end
) != NEVERDATE
&& ntohl(ent
->pw_end
) != -1){
320 *hdb
.pw_end
= ntohl(ent
->pw_end
);
323 ret
= krb5_make_principal(pd
->context
, &hdb
.created_by
.principal
,
328 hdb
.created_by
.time
= time(NULL
);
332 ALLOC(hdb
.modified_by
);
333 read_block(pd
->context
, fd
, ntohl(ent
->mod_ptr
), &mod
, sizeof(mod
));
335 krb5_425_conv_principal(pd
->context
, mod
.name
, mod
.instance
, realm
,
336 &hdb
.modified_by
->principal
);
337 hdb
.modified_by
->time
= ntohl(ent
->mod_time
);
338 memset(&mod
, 0, sizeof(mod
));
341 hdb
.flags
.forwardable
= 1;
342 hdb
.flags
.renewable
= 1;
343 hdb
.flags
.proxiable
= 1;
344 hdb
.flags
.postdate
= 1;
345 /* XXX - AFS 3.4a creates krbtgt.REALMOFCELL as NOTGS+NOSEAL */
346 if (strcmp(ent
->name
, "krbtgt") == 0 &&
347 (flags
& (KAFNOTGS
|KAFNOSEAL
)) == (KAFNOTGS
|KAFNOSEAL
))
348 flags
&= ~(KAFNOTGS
|KAFNOSEAL
);
350 hdb
.flags
.client
= (flags
& KAFNOTGS
) == 0;
351 hdb
.flags
.server
= (flags
& KAFNOSEAL
) == 0;
353 ret
= v5_prop(pd
->context
, NULL
, &hdb
, pd
);
354 hdb_free_entry(pd
->context
, &hdb
);
359 ka_dump(struct prop_data
*pd
, const char *file
, const char *cell
)
361 struct ka_header header
;
363 int fd
= open(file
, O_RDONLY
);
366 krb5_err(pd
->context
, 1, errno
, "open(%s)", file
);
367 read_block(pd
->context
, fd
, 0, &header
, sizeof(header
));
368 if(header
.version1
!= header
.version2
)
369 krb5_errx(pd
->context
, 1, "Version mismatch in header: %d/%d",
370 ntohl(header
.version1
), ntohl(header
.version2
));
371 if(ntohl(header
.version1
) != 5)
372 krb5_errx(pd
->context
, 1, "Unknown database version %d (expected 5)",
373 ntohl(header
.version1
));
374 for(i
= 0; i
< ntohl(header
.hashsize
); i
++){
375 int32_t pos
= ntohl(header
.hash
[i
]);
378 read_block(pd
->context
, fd
, pos
, &ent
, sizeof(ent
));
379 ka_convert(pd
, fd
, &ent
, cell
);
380 pos
= ntohl(ent
.next
);
386 #endif /* KASERVER_DB */
391 struct getargs args
[] = {
392 { "master-key", 'm', arg_string
, &mkeyfile
, "v5 master key file", "file" },
395 { "database", 'd', arg_string
, &database
, "database", "file" },
397 { "v4-db", '4', arg_flag
, &v4_db
, "use version 4 database" },
398 { "v4-realm", 'r', arg_string
, &realm
, "v4 realm to use" },
401 { "ka-db", 'K', arg_flag
, &ka_db
, "use kaserver database" },
402 { "cell", 'c', arg_string
, &afs_cell
, "name of AFS cell" },
403 { "kaspecials", 'S', arg_flag
, &kaspecials_flag
, "dump KASPECIAL keys"},
405 { "keytab", 'k', arg_string
, &ktname
, "keytab to use for authentication", "keytab" },
406 { "decrypt", 'D', arg_flag
, &decrypt_flag
, "decrypt keys" },
407 { "encrypt", 'E', arg_flag
, &encrypt_flag
, "encrypt keys" },
408 { "stdout", 'n', arg_flag
, &to_stdout
, "dump to stdout" },
409 { "verbose", 'v', arg_flag
, &verbose_flag
},
410 { "version", 0, arg_flag
, &version_flag
},
411 { "help", 'h', arg_flag
, &help_flag
}
414 static int num_args
= sizeof(args
) / sizeof(args
[0]);
419 arg_printusage (args
, num_args
, NULL
, "host ...");
424 get_creds(krb5_context context
, krb5_ccache
*cache
)
427 krb5_principal client
;
429 krb5_get_init_creds_opt init_opts
;
430 krb5_preauthtype preauth
= KRB5_PADATA_ENC_TIMESTAMP
;
433 ret
= krb5_kt_resolve(context
, ktname
, &keytab
);
434 if(ret
) krb5_err(context
, 1, ret
, "krb5_kt_resolve");
436 ret
= krb5_make_principal(context
, &client
, NULL
,
437 "kadmin", HPROP_NAME
, NULL
);
438 if(ret
) krb5_err(context
, 1, ret
, "krb5_make_principal");
440 krb5_get_init_creds_opt_init(&init_opts
);
441 krb5_get_init_creds_opt_set_preauth_list(&init_opts
, &preauth
, 1);
443 ret
= krb5_get_init_creds_keytab(context
, &creds
, client
, keytab
, 0, NULL
, &init_opts
);
444 if(ret
) krb5_err(context
, 1, ret
, "krb5_get_init_creds");
446 ret
= krb5_kt_close(context
, keytab
);
447 if(ret
) krb5_err(context
, 1, ret
, "krb5_kt_close");
449 ret
= krb5_cc_gen_new(context
, &krb5_mcc_ops
, cache
);
450 if(ret
) krb5_err(context
, 1, ret
, "krb5_cc_gen_new");
452 ret
= krb5_cc_initialize(context
, *cache
, client
);
453 if(ret
) krb5_err(context
, 1, ret
, "krb5_cc_initialize");
455 ret
= krb5_cc_store_cred(context
, *cache
, &creds
);
456 if(ret
) krb5_err(context
, 1, ret
, "krb5_cc_store_cred");
460 iterate (krb5_context context
,
461 const char *database
,
462 const char *afs_cell
,
464 int v4_db
, int ka_db
,
465 struct prop_data
*pd
)
469 int e
= kerb_db_iterate ((k_iter_proc_t
)v4_prop
, pd
);
471 krb5_errx(context
, 1, "kerb_db_iterate: %s",
472 krb_get_err_text(e
));
475 int e
= ka_dump(pd
, database
, afs_cell
);
477 krb5_errx(context
, 1, "ka_dump: %s", krb_get_err_text(e
));
482 krb5_error_code ret
= hdb_foreach(context
, db
, HDB_F_DECRYPT
,
485 krb5_err(context
, 1, ret
, "hdb_foreach");
490 dump_database (krb5_context context
, int v4_db
, int ka_db
,
491 const char *database
, const char *afs_cell
,
496 pd
.context
= context
;
497 pd
.auth_context
= NULL
;
498 pd
.sock
= STDOUT_FILENO
;
500 iterate (context
, database
, afs_cell
, db
, v4_db
, ka_db
, &pd
);
505 propagate_database (krb5_context context
, int v4_db
, int ka_db
,
506 const char *database
, const char *afs_cell
,
507 HDB
*db
, krb5_ccache ccache
,
508 int optind
, int argc
, char **argv
)
510 krb5_principal server
;
514 for(i
= optind
; i
< argc
; i
++){
515 krb5_auth_context auth_context
;
520 fd
= open_socket(context
, argv
[i
]);
522 krb5_warn (context
, errno
, "connect %s", argv
[i
]);
526 ret
= krb5_sname_to_principal(context
, argv
[i
],
527 HPROP_NAME
, KRB5_NT_SRV_HST
, &server
);
529 krb5_warn(context
, ret
, "krb5_sname_to_principal(%s)", argv
[i
]);
535 ret
= krb5_sendauth(context
,
541 AP_OPTS_MUTUAL_REQUIRED
,
550 krb5_warn(context
, ret
, "krb5_sendauth");
555 pd
.context
= context
;
556 pd
.auth_context
= auth_context
;
559 iterate (context
, database
, afs_cell
, db
,
564 ret
= send_priv(context
, auth_context
, &data
, fd
);
566 krb5_warn(context
, ret
, "send_priv");
568 ret
= recv_priv(context
, auth_context
, fd
, &data
);
570 krb5_warn(context
, ret
, "recv_priv");
572 krb5_data_free (&data
);
574 krb5_auth_con_free(context
, auth_context
);
581 main(int argc
, char **argv
)
584 krb5_context context
;
589 set_progname(argv
[0]);
591 if(getarg(args
, num_args
, argc
, argv
, &optind
))
602 ret
= krb5_init_context(&context
);
606 if(encrypt_flag
&& decrypt_flag
)
607 krb5_errx(context
, 1,
608 "Only one of `--encrypt' and `--decrypt' is meaningful");
611 get_creds(context
, &ccache
);
613 ret
= hdb_read_master_key(context
, mkeyfile
, &mkey5
);
614 if(ret
&& ret
!= ENOENT
)
615 krb5_err(context
, 1, ret
, "hdb_read_master_key");
617 if(encrypt_flag
|| decrypt_flag
)
618 krb5_errx(context
, 1, "No master key file found");
620 ret
= hdb_process_master_key(context
, mkey5
, &msched5
);
622 krb5_err(context
, 1, ret
, "hdb_process_master_key");
634 e
= krb_get_lrealm(realm_buf
, 1);
636 krb5_errx(context
, 1, "krb_get_lrealm: %s",
637 krb_get_err_text(e
));
643 int e
= kerb_db_set_name (database
);
645 krb5_errx(context
, 1, "kerb_db_set_name: %s",
646 krb_get_err_text(e
));
647 e
= kdb_get_master_key(0, &mkey4
, msched4
);
649 krb5_errx(context
, 1, "kdb_get_master_key: %s",
650 krb_get_err_text(e
));
654 /* no preparation required */
659 ret
= hdb_create (context
, &db
, database
);
661 krb5_err(context
, 1, ret
, "hdb_create: %s", database
);
662 ret
= db
->open(context
, db
, O_RDONLY
, 0);
664 krb5_err(context
, 1, ret
, "db->open");
668 dump_database (context
, v4_db
, ka_db
,
669 database
, afs_cell
, db
);
671 propagate_database (context
, v4_db
, ka_db
,