1 /* 2 * Copyright (c) 1997 - 2003 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "iprop.h" 35 36 RCSID("$Id: ipropd_slave.c,v 1.27.2.1 2003/08/15 16:45:15 lha Exp $"); 37 38 static krb5_log_facility *log_facility; 39 40 static int 41 connect_to_master (krb5_context context, const char *master) 42 { 43 int fd; 44 struct sockaddr_in addr; 45 struct hostent *he; 46 47 fd = socket (AF_INET, SOCK_STREAM, 0); 48 if (fd < 0) 49 krb5_err (context, 1, errno, "socket AF_INET"); 50 memset (&addr, 0, sizeof(addr)); 51 addr.sin_family = AF_INET; 52 addr.sin_port = krb5_getportbyname (context, 53 IPROP_SERVICE, "tcp", IPROP_PORT); 54 he = roken_gethostbyname (master); 55 if (he == NULL) 56 krb5_errx (context, 1, "gethostbyname: %s", hstrerror(h_errno)); 57 memcpy (&addr.sin_addr, he->h_addr, sizeof(addr.sin_addr)); 58 if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 59 krb5_err (context, 1, errno, "connect"); 60 return fd; 61 } 62 63 static void 64 get_creds(krb5_context context, const char *keytab_str, 65 krb5_ccache *cache, const char *host) 66 { 67 krb5_keytab keytab; 68 krb5_principal client; 69 krb5_error_code ret; 70 krb5_get_init_creds_opt init_opts; 71 krb5_creds creds; 72 char *server; 73 char keytab_buf[256]; 74 75 if (keytab_str == NULL) { 76 ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf)); 77 if (ret) 78 krb5_err (context, 1, ret, "krb5_kt_default_name"); 79 keytab_str = keytab_buf; 80 } 81 82 ret = krb5_kt_resolve(context, keytab_str, &keytab); 83 if(ret) 84 krb5_err(context, 1, ret, "%s", keytab_str); 85 86 ret = krb5_sname_to_principal (context, NULL, IPROP_NAME, 87 KRB5_NT_SRV_HST, &client); 88 if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal"); 89 90 krb5_get_init_creds_opt_init(&init_opts); 91 92 asprintf (&server, "%s/%s", IPROP_NAME, host); 93 if (server == NULL) 94 krb5_errx (context, 1, "malloc: no memory"); 95 96 ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 97 0, server, &init_opts); 98 free (server); 99 if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds"); 100 101 ret = krb5_kt_close(context, keytab); 102 if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); 103 104 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, cache); 105 if(ret) krb5_err(context, 1, ret, "krb5_cc_gen_new"); 106 107 ret = krb5_cc_initialize(context, *cache, client); 108 if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize"); 109 110 ret = krb5_cc_store_cred(context, *cache, &creds); 111 if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); 112 } 113 114 static void 115 ihave (krb5_context context, krb5_auth_context auth_context, 116 int fd, u_int32_t version) 117 { 118 int ret; 119 u_char buf[8]; 120 krb5_storage *sp; 121 krb5_data data, priv_data; 122 123 sp = krb5_storage_from_mem (buf, 8); 124 krb5_store_int32 (sp, I_HAVE); 125 krb5_store_int32 (sp, version); 126 krb5_storage_free (sp); 127 data.length = 8; 128 data.data = buf; 129 130 ret = krb5_mk_priv (context, auth_context, &data, &priv_data, NULL); 131 if (ret) 132 krb5_err (context, 1, ret, "krb_mk_priv"); 133 134 ret = krb5_write_message (context, &fd, &priv_data); 135 if (ret) 136 krb5_err (context, 1, ret, "krb5_write_message"); 137 138 krb5_data_free (&priv_data); 139 } 140 141 static void 142 receive_loop (krb5_context context, 143 krb5_storage *sp, 144 kadm5_server_context *server_context) 145 { 146 int ret; 147 off_t left, right; 148 void *buf; 149 int32_t vers; 150 151 do { 152 int32_t len, timestamp, tmp; 153 enum kadm_ops op; 154 155 if(krb5_ret_int32 (sp, &vers) != 0) 156 return; 157 krb5_ret_int32 (sp, ×tamp); 158 krb5_ret_int32 (sp, &tmp); 159 op = tmp; 160 krb5_ret_int32 (sp, &len); 161 if (vers <= server_context->log_context.version) 162 krb5_storage_seek(sp, len, SEEK_CUR); 163 } while(vers <= server_context->log_context.version); 164 165 left = krb5_storage_seek (sp, -16, SEEK_CUR); 166 right = krb5_storage_seek (sp, 0, SEEK_END); 167 buf = malloc (right - left); 168 if (buf == NULL && (right - left) != 0) { 169 krb5_warnx (context, "malloc: no memory"); 170 return; 171 } 172 krb5_storage_seek (sp, left, SEEK_SET); 173 krb5_storage_read (sp, buf, right - left); 174 write (server_context->log_context.log_fd, buf, right-left); 175 fsync (server_context->log_context.log_fd); 176 free (buf); 177 178 krb5_storage_seek (sp, left, SEEK_SET); 179 180 for(;;) { 181 int32_t len, timestamp, tmp; 182 enum kadm_ops op; 183 184 if(krb5_ret_int32 (sp, &vers) != 0) 185 break; 186 krb5_ret_int32 (sp, ×tamp); 187 krb5_ret_int32 (sp, &tmp); 188 op = tmp; 189 krb5_ret_int32 (sp, &len); 190 191 ret = kadm5_log_replay (server_context, 192 op, vers, len, sp); 193 if (ret) 194 krb5_warn (context, ret, "kadm5_log_replay"); 195 else 196 server_context->log_context.version = vers; 197 krb5_storage_seek (sp, 8, SEEK_CUR); 198 } 199 } 200 201 static void 202 receive (krb5_context context, 203 krb5_storage *sp, 204 kadm5_server_context *server_context) 205 { 206 int ret; 207 208 ret = server_context->db->open(context, 209 server_context->db, 210 O_RDWR | O_CREAT, 0600); 211 if (ret) 212 krb5_err (context, 1, ret, "db->open"); 213 214 receive_loop (context, sp, server_context); 215 216 ret = server_context->db->close (context, server_context->db); 217 if (ret) 218 krb5_err (context, 1, ret, "db->close"); 219 } 220 221 static void 222 receive_everything (krb5_context context, int fd, 223 kadm5_server_context *server_context, 224 krb5_auth_context auth_context) 225 { 226 int ret; 227 krb5_data data; 228 int32_t vno; 229 int32_t opcode; 230 unsigned long tmp; 231 232 char *dbname; 233 HDB *mydb; 234 235 asprintf(&dbname, "%s-NEW", server_context->db->name); 236 ret = hdb_create(context, &mydb, dbname); 237 if(ret) 238 krb5_err(context,1, ret, "hdb_create"); 239 free(dbname); 240 241 ret = hdb_set_master_keyfile (context, 242 mydb, server_context->config.stash_file); 243 if(ret) 244 krb5_err(context,1, ret, "hdb_set_master_keyfile"); 245 246 /* I really want to use O_EXCL here, but given that I can't easily clean 247 up on error, I won't */ 248 ret = mydb->open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); 249 250 if (ret) 251 krb5_err (context, 1, ret, "db->open"); 252 253 do { 254 krb5_storage *sp; 255 256 ret = krb5_read_priv_message(context, auth_context, &fd, &data); 257 258 if (ret) 259 krb5_err (context, 1, ret, "krb5_read_priv_message"); 260 261 sp = krb5_storage_from_data (&data); 262 krb5_ret_int32 (sp, &opcode); 263 if (opcode == ONE_PRINC) { 264 krb5_data fake_data; 265 hdb_entry entry; 266 267 fake_data.data = (char *)data.data + 4; 268 fake_data.length = data.length - 4; 269 270 ret = hdb_value2entry (context, &fake_data, &entry); 271 if (ret) 272 krb5_err (context, 1, ret, "hdb_value2entry"); 273 ret = mydb->store(server_context->context, 274 mydb, 275 0, &entry); 276 if (ret) 277 krb5_err (context, 1, ret, "hdb_store"); 278 279 hdb_free_entry (context, &entry); 280 krb5_data_free (&data); 281 } 282 } while (opcode == ONE_PRINC); 283 284 if (opcode != NOW_YOU_HAVE) 285 krb5_errx (context, 1, "receive_everything: strange %d", opcode); 286 287 _krb5_get_int ((char *)data.data + 4, &tmp, 4); 288 vno = tmp; 289 290 ret = kadm5_log_reinit (server_context); 291 if (ret) 292 krb5_err(context, 1, ret, "kadm5_log_reinit"); 293 294 ret = kadm5_log_set_version (server_context, vno - 1); 295 if (ret) 296 krb5_err (context, 1, ret, "kadm5_log_set_version"); 297 298 ret = kadm5_log_nop (server_context); 299 if (ret) 300 krb5_err (context, 1, ret, "kadm5_log_nop"); 301 302 krb5_data_free (&data); 303 304 ret = mydb->rename (context, mydb, server_context->db->name); 305 if (ret) 306 krb5_err (context, 1, ret, "db->rename"); 307 308 ret = mydb->close (context, mydb); 309 if (ret) 310 krb5_err (context, 1, ret, "db->close"); 311 312 ret = mydb->destroy (context, mydb); 313 if (ret) 314 krb5_err (context, 1, ret, "db->destroy"); 315 } 316 317 static char *realm; 318 static int version_flag; 319 static int help_flag; 320 static char *keytab_str; 321 322 static struct getargs args[] = { 323 { "realm", 'r', arg_string, &realm }, 324 { "keytab", 'k', arg_string, &keytab_str, 325 "keytab to get authentication from", "kspec" }, 326 { "version", 0, arg_flag, &version_flag }, 327 { "help", 0, arg_flag, &help_flag } 328 }; 329 330 static int num_args = sizeof(args) / sizeof(args[0]); 331 332 static void 333 usage (int code, struct getargs *args, int num_args) 334 { 335 arg_printusage (args, num_args, NULL, "master"); 336 exit (code); 337 } 338 339 int 340 main(int argc, char **argv) 341 { 342 krb5_error_code ret; 343 krb5_context context; 344 krb5_auth_context auth_context; 345 void *kadm_handle; 346 kadm5_server_context *server_context; 347 kadm5_config_params conf; 348 int master_fd; 349 krb5_ccache ccache; 350 krb5_principal server; 351 352 int optind; 353 const char *master; 354 355 optind = krb5_program_setup(&context, argc, argv, args, num_args, usage); 356 357 if(help_flag) 358 usage (0, args, num_args); 359 if(version_flag) { 360 print_version(NULL); 361 exit(0); 362 } 363 364 argc -= optind; 365 argv += optind; 366 367 if (argc != 1) 368 usage (1, args, num_args); 369 370 master = argv[0]; 371 372 pidfile (NULL); 373 krb5_openlog (context, "ipropd-slave", &log_facility); 374 krb5_set_warn_dest(context, log_facility); 375 376 ret = krb5_kt_register(context, &hdb_kt_ops); 377 if(ret) 378 krb5_err(context, 1, ret, "krb5_kt_register"); 379 380 memset(&conf, 0, sizeof(conf)); 381 if(realm) { 382 conf.mask |= KADM5_CONFIG_REALM; 383 conf.realm = realm; 384 } 385 ret = kadm5_init_with_password_ctx (context, 386 KADM5_ADMIN_SERVICE, 387 NULL, 388 KADM5_ADMIN_SERVICE, 389 &conf, 0, 0, 390 &kadm_handle); 391 if (ret) 392 krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 393 394 server_context = (kadm5_server_context *)kadm_handle; 395 396 ret = kadm5_log_init (server_context); 397 if (ret) 398 krb5_err (context, 1, ret, "kadm5_log_init"); 399 400 get_creds(context, keytab_str, &ccache, master); 401 402 master_fd = connect_to_master (context, master); 403 404 ret = krb5_sname_to_principal (context, master, IPROP_NAME, 405 KRB5_NT_SRV_HST, &server); 406 if (ret) 407 krb5_err (context, 1, ret, "krb5_sname_to_principal"); 408 409 auth_context = NULL; 410 ret = krb5_sendauth (context, &auth_context, &master_fd, 411 IPROP_VERSION, NULL, server, 412 AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, 413 ccache, NULL, NULL, NULL); 414 if (ret) 415 krb5_err (context, 1, ret, "krb5_sendauth"); 416 417 ihave (context, auth_context, master_fd, 418 server_context->log_context.version); 419 420 for (;;) { 421 int ret; 422 krb5_data out; 423 krb5_storage *sp; 424 int32_t tmp; 425 426 ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); 427 428 if (ret) 429 krb5_err (context, 1, ret, "krb5_read_priv_message"); 430 431 sp = krb5_storage_from_mem (out.data, out.length); 432 krb5_ret_int32 (sp, &tmp); 433 switch (tmp) { 434 case FOR_YOU : 435 receive (context, sp, server_context); 436 ihave (context, auth_context, master_fd, 437 server_context->log_context.version); 438 break; 439 case TELL_YOU_EVERYTHING : 440 receive_everything (context, master_fd, server_context, 441 auth_context); 442 break; 443 case NOW_YOU_HAVE : 444 case I_HAVE : 445 case ONE_PRINC : 446 default : 447 krb5_warnx (context, "Ignoring command %d", tmp); 448 break; 449 } 450 krb5_storage_free (sp); 451 krb5_data_free (&out); 452 } 453 454 return 0; 455 } 456