1 /* 2 * Copyright (c) 1997 - 2000 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.21 2000/08/06 02:06:19 assar 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 ret = krb5_kt_register(context, &hdb_kt_ops); 76 if(ret) 77 krb5_err(context, 1, ret, "krb5_kt_register"); 78 79 if (keytab_str == NULL) { 80 ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf)); 81 if (ret) 82 krb5_err (context, 1, ret, "krb5_kt_default_name"); 83 keytab_str = keytab_buf; 84 } 85 86 ret = krb5_kt_resolve(context, keytab_str, &keytab); 87 if(ret) 88 krb5_err(context, 1, ret, "%s", keytab_str); 89 90 ret = krb5_sname_to_principal (context, NULL, IPROP_NAME, 91 KRB5_NT_SRV_HST, &client); 92 if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal"); 93 94 krb5_get_init_creds_opt_init(&init_opts); 95 96 asprintf (&server, "%s/%s", IPROP_NAME, host); 97 if (server == NULL) 98 krb5_errx (context, 1, "malloc: no memory"); 99 100 ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 101 0, server, &init_opts); 102 free (server); 103 if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds"); 104 105 ret = krb5_kt_close(context, keytab); 106 if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); 107 108 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, cache); 109 if(ret) krb5_err(context, 1, ret, "krb5_cc_gen_new"); 110 111 ret = krb5_cc_initialize(context, *cache, client); 112 if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize"); 113 114 ret = krb5_cc_store_cred(context, *cache, &creds); 115 if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); 116 } 117 118 static void 119 ihave (krb5_context context, krb5_auth_context auth_context, 120 int fd, u_int32_t version) 121 { 122 int ret; 123 u_char buf[8]; 124 krb5_storage *sp; 125 krb5_data data, priv_data; 126 127 sp = krb5_storage_from_mem (buf, 8); 128 krb5_store_int32 (sp, I_HAVE); 129 krb5_store_int32 (sp, version); 130 krb5_storage_free (sp); 131 data.length = 8; 132 data.data = buf; 133 134 ret = krb5_mk_priv (context, auth_context, &data, &priv_data, NULL); 135 if (ret) 136 krb5_err (context, 1, ret, "krb_mk_priv"); 137 138 ret = krb5_write_message (context, &fd, &priv_data); 139 if (ret) 140 krb5_err (context, 1, ret, "krb5_write_message"); 141 142 krb5_data_free (&priv_data); 143 } 144 145 static void 146 receive_loop (krb5_context context, 147 krb5_storage *sp, 148 kadm5_server_context *server_context) 149 { 150 int ret; 151 off_t left, right; 152 void *buf; 153 int32_t vers; 154 155 do { 156 int32_t len, timestamp, tmp; 157 enum kadm_ops op; 158 159 if(krb5_ret_int32 (sp, &vers) != 0) 160 return; 161 krb5_ret_int32 (sp, ×tamp); 162 krb5_ret_int32 (sp, &tmp); 163 op = tmp; 164 krb5_ret_int32 (sp, &len); 165 if (vers <= server_context->log_context.version) 166 sp->seek(sp, len, SEEK_CUR); 167 } while(vers <= server_context->log_context.version); 168 169 left = sp->seek (sp, -16, SEEK_CUR); 170 right = sp->seek (sp, 0, SEEK_END); 171 buf = malloc (right - left); 172 if (buf == NULL && (right - left) != 0) { 173 krb5_warnx (context, "malloc: no memory"); 174 return; 175 } 176 sp->seek (sp, left, SEEK_SET); 177 sp->fetch (sp, buf, right - left); 178 write (server_context->log_context.log_fd, buf, right-left); 179 fsync (server_context->log_context.log_fd); 180 free (buf); 181 182 sp->seek (sp, left, SEEK_SET); 183 184 for(;;) { 185 int32_t len, timestamp, tmp; 186 enum kadm_ops op; 187 188 if(krb5_ret_int32 (sp, &vers) != 0) 189 break; 190 krb5_ret_int32 (sp, ×tamp); 191 krb5_ret_int32 (sp, &tmp); 192 op = tmp; 193 krb5_ret_int32 (sp, &len); 194 195 ret = kadm5_log_replay (server_context, 196 op, vers, len, sp); 197 if (ret) 198 krb5_warn (context, ret, "kadm5_log_replay"); 199 else 200 server_context->log_context.version = vers; 201 sp->seek (sp, 8, SEEK_CUR); 202 } 203 } 204 205 static void 206 receive (krb5_context context, 207 krb5_storage *sp, 208 kadm5_server_context *server_context) 209 { 210 int ret; 211 212 ret = server_context->db->open(context, 213 server_context->db, 214 O_RDWR | O_CREAT, 0600); 215 if (ret) 216 krb5_err (context, 1, ret, "db->open"); 217 218 receive_loop (context, sp, server_context); 219 220 ret = server_context->db->close (context, server_context->db); 221 if (ret) 222 krb5_err (context, 1, ret, "db->close"); 223 } 224 225 static void 226 receive_everything (krb5_context context, int fd, 227 kadm5_server_context *server_context, 228 krb5_auth_context auth_context) 229 { 230 int ret; 231 krb5_data data; 232 int32_t vno; 233 int32_t opcode; 234 235 ret = server_context->db->open(context, 236 server_context->db, 237 O_RDWR | O_CREAT | O_TRUNC, 0600); 238 if (ret) 239 krb5_err (context, 1, ret, "db->open"); 240 241 do { 242 krb5_storage *sp; 243 244 ret = krb5_read_priv_message(context, auth_context, &fd, &data); 245 246 if (ret) 247 krb5_err (context, 1, ret, "krb5_read_priv_message"); 248 249 sp = krb5_storage_from_data (&data); 250 krb5_ret_int32 (sp, &opcode); 251 if (opcode == ONE_PRINC) { 252 krb5_data fake_data; 253 hdb_entry entry; 254 255 fake_data.data = (char *)data.data + 4; 256 fake_data.length = data.length - 4; 257 258 ret = hdb_value2entry (context, &fake_data, &entry); 259 if (ret) 260 krb5_err (context, 1, ret, "hdb_value2entry"); 261 ret = server_context->db->store(server_context->context, 262 server_context->db, 263 0, &entry); 264 if (ret) 265 krb5_err (context, 1, ret, "hdb_store"); 266 267 hdb_free_entry (context, &entry); 268 krb5_data_free (&data); 269 } 270 } while (opcode == ONE_PRINC); 271 272 if (opcode != NOW_YOU_HAVE) 273 krb5_errx (context, 1, "receive_everything: strange %d", opcode); 274 275 _krb5_get_int ((char *)data.data + 4, &vno, 4); 276 277 ret = kadm5_log_reinit (server_context); 278 if (ret) 279 krb5_err(context, 1, ret, "kadm5_log_reinit"); 280 281 ret = kadm5_log_set_version (server_context, vno - 1); 282 if (ret) 283 krb5_err (context, 1, ret, "kadm5_log_set_version"); 284 285 ret = kadm5_log_nop (server_context); 286 if (ret) 287 krb5_err (context, 1, ret, "kadm5_log_nop"); 288 289 krb5_data_free (&data); 290 291 ret = server_context->db->close (context, server_context->db); 292 if (ret) 293 krb5_err (context, 1, ret, "db->close"); 294 } 295 296 static char *realm; 297 static int version_flag; 298 static int help_flag; 299 static char *keytab_str; 300 301 static struct getargs args[] = { 302 { "realm", 'r', arg_string, &realm }, 303 { "keytab", 'k', arg_string, &keytab_str, 304 "keytab to get authentication from", "kspec" }, 305 { "version", 0, arg_flag, &version_flag }, 306 { "help", 0, arg_flag, &help_flag } 307 }; 308 309 static int num_args = sizeof(args) / sizeof(args[0]); 310 311 static void 312 usage (int code, struct getargs *args, int num_args) 313 { 314 arg_printusage (args, num_args, NULL, "master"); 315 exit (code); 316 } 317 318 int 319 main(int argc, char **argv) 320 { 321 krb5_error_code ret; 322 krb5_context context; 323 krb5_auth_context auth_context; 324 void *kadm_handle; 325 kadm5_server_context *server_context; 326 kadm5_config_params conf; 327 int master_fd; 328 krb5_ccache ccache; 329 krb5_principal server; 330 331 int optind; 332 const char *master; 333 334 optind = krb5_program_setup(&context, argc, argv, args, num_args, usage); 335 336 if(help_flag) 337 usage (0, args, num_args); 338 if(version_flag) { 339 print_version(NULL); 340 exit(0); 341 } 342 343 argc -= optind; 344 argv += optind; 345 346 if (argc != 1) 347 usage (1, args, num_args); 348 349 master = argv[0]; 350 351 krb5_openlog (context, "ipropd-master", &log_facility); 352 krb5_set_warn_dest(context, log_facility); 353 354 ret = krb5_kt_register(context, &hdb_kt_ops); 355 if(ret) 356 krb5_err(context, 1, ret, "krb5_kt_register"); 357 358 memset(&conf, 0, sizeof(conf)); 359 if(realm) { 360 conf.mask |= KADM5_CONFIG_REALM; 361 conf.realm = realm; 362 } 363 ret = kadm5_init_with_password_ctx (context, 364 KADM5_ADMIN_SERVICE, 365 NULL, 366 KADM5_ADMIN_SERVICE, 367 &conf, 0, 0, 368 &kadm_handle); 369 if (ret) 370 krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 371 372 server_context = (kadm5_server_context *)kadm_handle; 373 374 ret = kadm5_log_init (server_context); 375 if (ret) 376 krb5_err (context, 1, ret, "kadm5_log_init"); 377 378 get_creds(context, keytab_str, &ccache, master); 379 380 master_fd = connect_to_master (context, master); 381 382 ret = krb5_sname_to_principal (context, master, IPROP_NAME, 383 KRB5_NT_SRV_HST, &server); 384 if (ret) 385 krb5_err (context, 1, ret, "krb5_sname_to_principal"); 386 387 auth_context = NULL; 388 ret = krb5_sendauth (context, &auth_context, &master_fd, 389 IPROP_VERSION, NULL, server, 390 AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, 391 ccache, NULL, NULL, NULL); 392 if (ret) 393 krb5_err (context, 1, ret, "krb5_sendauth"); 394 395 ihave (context, auth_context, master_fd, 396 server_context->log_context.version); 397 398 for (;;) { 399 int ret; 400 krb5_data out; 401 krb5_storage *sp; 402 int32_t tmp; 403 404 ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); 405 406 if (ret) 407 krb5_err (context, 1, ret, "krb5_read_priv_message"); 408 409 sp = krb5_storage_from_mem (out.data, out.length); 410 krb5_ret_int32 (sp, &tmp); 411 switch (tmp) { 412 case FOR_YOU : 413 receive (context, sp, server_context); 414 ihave (context, auth_context, master_fd, 415 server_context->log_context.version); 416 break; 417 case TELL_YOU_EVERYTHING : 418 receive_everything (context, master_fd, server_context, 419 auth_context); 420 break; 421 case NOW_YOU_HAVE : 422 case I_HAVE : 423 case ONE_PRINC : 424 default : 425 krb5_warnx (context, "Ignoring command %d", tmp); 426 break; 427 } 428 krb5_storage_free (sp); 429 krb5_data_free (&out); 430 } 431 432 return 0; 433 } 434