static void v5_loop (krb5_context context, krb5_auth_context ac, krb5_boolean initial, void *kadm_handle, krb5_socket_t fd) { krb5_error_code ret; krb5_data in, out; for (;;) { doing_useful_work = 0; if(term_flag) exit(0); ret = krb5_read_priv_message(context, ac, &fd, &in); if(ret == HEIM_ERR_EOF) exit(0); if(ret) krb5_err(context, 1, ret, "krb5_read_priv_message"); doing_useful_work = 1; kadmind_dispatch(kadm_handle, initial, &in, &out); krb5_data_free(&in); ret = krb5_write_priv_message(context, ac, &fd, &out); if(ret) krb5_err(context, 1, ret, "krb5_write_priv_message"); } }
static void handle_v5(krb5_context contextp, krb5_keytab keytab, krb5_socket_t fd) { krb5_error_code ret; krb5_ticket *ticket; char *server_name; char *client; void *kadm_handlep; krb5_boolean initial; krb5_auth_context ac = NULL; unsigned kadm_version; kadm5_config_params realm_params; ret = krb5_recvauth_match_version(contextp, &ac, &fd, match_appl_version, &kadm_version, NULL, KRB5_RECVAUTH_IGNORE_VERSION, keytab, &ticket); if (ret) krb5_err(contextp, 1, ret, "krb5_recvauth"); ret = krb5_unparse_name (contextp, ticket->server, &server_name); if (ret) krb5_err (contextp, 1, ret, "krb5_unparse_name"); if (strncmp (server_name, KADM5_ADMIN_SERVICE, strlen(KADM5_ADMIN_SERVICE)) != 0) krb5_errx (contextp, 1, "ticket for strange principal (%s)", server_name); free (server_name); memset(&realm_params, 0, sizeof(realm_params)); if(kadm_version == 1) { krb5_data params; ret = krb5_read_priv_message(contextp, ac, &fd, ¶ms); if(ret) krb5_err(contextp, 1, ret, "krb5_read_priv_message"); _kadm5_unmarshal_params(contextp, ¶ms, &realm_params); } initial = ticket->ticket.flags.initial; ret = krb5_unparse_name(contextp, ticket->client, &client); if (ret) krb5_err (contextp, 1, ret, "krb5_unparse_name"); krb5_free_ticket (contextp, ticket); ret = kadm5_s_init_with_password_ctx(contextp, client, NULL, KADM5_ADMIN_SERVICE, &realm_params, 0, 0, &kadm_handlep); if(ret) krb5_err (contextp, 1, ret, "kadm5_init_with_password_ctx"); v5_loop (contextp, ac, initial, kadm_handlep, fd); }
static int process_msg (krb5_context context, slave *s, int log_fd, const char *database, u_int32_t current_version) { int ret = 0; krb5_data out; krb5_storage *sp; int32_t tmp; ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); if(ret) { krb5_warn (context, ret, "error reading message from %s", s->name); return 1; } sp = krb5_storage_from_mem (out.data, out.length); if (sp == NULL) { krb5_warnx (context, "process_msg: no memory"); krb5_data_free (&out); return 1; } if (krb5_ret_int32 (sp, &tmp) != 0) { krb5_warnx (context, "process_msg: client send too short command"); krb5_data_free (&out); return 1; } switch (tmp) { case I_HAVE : ret = krb5_ret_int32 (sp, &tmp); if (ret != 0) { krb5_warnx (context, "process_msg: client send too I_HAVE data"); break; } s->version = tmp; ret = send_diffs (context, s, log_fd, database, current_version); break; case I_AM_HERE : break; case ARE_YOU_THERE: case FOR_YOU : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_data_free (&out); slave_seen(s); return ret; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context ac = NULL; krb5_principal c1, c2; krb5_authenticator authent; krb5_keytab keytab; krb5_socket_t sock = rk_INVALID_SOCKET; HDB *db = NULL; int optidx = 0; char *tmp_db; krb5_log_facility *fac; int nprincs; setprogname(argv[0]); ret = krb5_init_context(&context); if(ret) exit(1); ret = krb5_openlog(context, "hpropd", &fac); if(ret) errx(1, "krb5_openlog"); krb5_set_warn_dest(context, fac); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(local_realm != NULL) krb5_set_default_realm(context, local_realm); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc != 0) usage(1); if (database == NULL) database = hdb_default_db(context); if(from_stdin) { sock = STDIN_FILENO; } else { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; socklen_t sin_len = sizeof(ss); char addr_name[256]; krb5_ticket *ticket; char *server; sock = STDIN_FILENO; #ifdef SUPPORT_INETD if (inetd_flag == -1) { if (getpeername (sock, sa, &sin_len) < 0) { inetd_flag = 0; } else { inetd_flag = 1; } } #else inetd_flag = 0; #endif if (!inetd_flag) { mini_inetd (krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT), &sock); } sin_len = sizeof(ss); if(getpeername(sock, sa, &sin_len) < 0) krb5_err(context, 1, errno, "getpeername"); if (inet_ntop(sa->sa_family, socket_get_address (sa), addr_name, sizeof(addr_name)) == NULL) strlcpy (addr_name, "unknown address", sizeof(addr_name)); krb5_log(context, fac, 0, "Connection from %s", addr_name); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); if (ktname != NULL) { ret = krb5_kt_resolve(context, ktname, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname); } else { ret = krb5_kt_default (context, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_default"); } ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL, 0, keytab, &ticket); if(ret) krb5_err(context, 1, ret, "krb5_recvauth"); ret = krb5_unparse_name(context, ticket->server, &server); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); if (strncmp(server, "hprop/", 5) != 0) krb5_errx(context, 1, "ticket not for hprop (%s)", server); free(server); krb5_free_ticket (context, ticket); ret = krb5_auth_con_getauthenticator(context, ac, &authent); if(ret) krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator"); ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); _krb5_principalname2krb5_principal(context, &c2, authent->cname, authent->crealm); if(!krb5_principal_compare(context, c1, c2)) { char *s; ret = krb5_unparse_name(context, c2, &s); if (ret) s = unparseable_name; krb5_errx(context, 1, "Unauthorized connection from %s", s); } krb5_free_principal(context, c1); krb5_free_principal(context, c2); ret = krb5_kt_close(context, keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); } if(!print_dump) { asprintf(&tmp_db, "%s~", database); ret = hdb_create(context, &db, tmp_db); if(ret) krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); if(ret) krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); } nprincs = 0; while(1){ krb5_data data; hdb_entry_ex entry; if(from_stdin) { ret = krb5_read_message(context, &sock, &data); if(ret != 0 && ret != HEIM_ERR_EOF) krb5_err(context, 1, ret, "krb5_read_message"); } else { ret = krb5_read_priv_message(context, ac, &sock, &data); if(ret) krb5_err(context, 1, ret, "krb5_read_priv_message"); } if(ret == HEIM_ERR_EOF || data.length == 0) { if(!from_stdin) { data.data = NULL; data.length = 0; krb5_write_priv_message(context, ac, &sock, &data); } if(!print_dump) { ret = db->hdb_close(context, db); if(ret) krb5_err(context, 1, ret, "db_close"); ret = db->hdb_rename(context, db, database); if(ret) krb5_err(context, 1, ret, "db_rename"); } break; } memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry(context, &data, &entry.entry); krb5_data_free(&data); if(ret) krb5_err(context, 1, ret, "hdb_value2entry"); if(print_dump) hdb_print_entry(context, db, &entry, stdout); else { ret = db->hdb_store(context, db, 0, &entry); if(ret == HDB_ERR_EXISTS) { char *s; ret = krb5_unparse_name(context, entry.entry.principal, &s); if (ret) s = strdup(unparseable_name); krb5_warnx(context, "Entry exists: %s", s); free(s); } else if(ret) krb5_err(context, 1, ret, "db_store"); else nprincs++; } hdb_free_entry(context, &entry); } if (!print_dump) krb5_log(context, fac, 0, "Received %d principals", nprincs); if (inetd_flag == 0) rk_closesocket(sock); exit(0); }
static int process_msg (kadm5_server_context *server_context, slave *s, int log_fd, const char *database, uint32_t current_version, uint32_t current_tstamp) { krb5_context context = server_context->context; int ret = 0; krb5_data out; krb5_storage *sp; uint32_t tmp; ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); if(ret) { krb5_warn(context, ret, "error reading message from %s", s->name); return 1; } sp = krb5_storage_from_mem(out.data, out.length); if (sp == NULL) { krb5_warnx(context, "process_msg: no memory"); krb5_data_free(&out); return 1; } if (krb5_ret_uint32(sp, &tmp) != 0) { krb5_warnx(context, "process_msg: client send too short command"); krb5_data_free(&out); return 1; } switch (tmp) { case I_HAVE : ret = krb5_ret_uint32(sp, &tmp); if (ret != 0) { krb5_warnx(context, "process_msg: client send too little I_HAVE data"); break; } /* new started slave that have old log */ if (s->version == 0 && tmp != 0) { if (current_version < tmp) { krb5_warnx(context, "Slave %s (version %u) have later version " "the master (version %u) OUT OF SYNC", s->name, tmp, current_version); } if (verbose) krb5_warnx(context, "slave %s updated from %u to %u", s->name, s->version, tmp); s->version = tmp; } if (tmp < s->version) { krb5_warnx(context, "Slave %s claims to not have " "version we already sent to it", s->name); s->version = tmp; } ret = send_diffs(server_context, s, log_fd, database, current_version, current_tstamp); break; case I_AM_HERE : if (verbose) krb5_warnx(context, "slave %s is there", s->name); break; case ARE_YOU_THERE: case FOR_YOU : default : krb5_warnx(context, "Ignoring command %d", tmp); break; } krb5_data_free(&out); krb5_storage_free(sp); slave_seen(s); return ret; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context auth_context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; int master_fd; krb5_ccache ccache; krb5_principal server; char **files; int optidx = 0; time_t reconnect_min; time_t backoff; time_t reconnect_max; time_t reconnect; time_t before = 0; const char *master; setprogname(argv[0]); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); setup_signal(); if (config_file == NULL) { asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); if (config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); argc -= optidx; argv += optidx; if (argc != 1) usage(1); master = argv[0]; #ifdef SUPPORT_DETACH if (detach_from_console) daemon(0, 0); #endif pidfile (NULL); krb5_openlog (context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); time_before_lost = parse_time (server_time_lost, "s"); if (time_before_lost < 0) krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_password_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); get_creds(context, keytab_str, &ccache, master); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) krb5_err (context, 1, ret, "krb5_sname_to_principal"); auth_context = NULL; master_fd = -1; krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 10, &reconnect_min); krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 300, &reconnect_max); krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 10, &backoff); reconnect = reconnect_min; while (!exit_flag) { time_t now, elapsed; int connected = FALSE; now = time(NULL); elapsed = now - before; if (elapsed < reconnect) { time_t left = reconnect - elapsed; krb5_warnx(context, "sleeping %d seconds before " "retrying to connect", (int)left); sleep(left); } before = now; master_fd = connect_to_master (context, master, port_str); if (master_fd < 0) goto retry; reconnect = reconnect_min; if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; krb5_cc_destroy(context, ccache); get_creds(context, keytab_str, &ccache, master); } ret = krb5_sendauth (context, &auth_context, &master_fd, IPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, ccache, NULL, NULL, NULL); if (ret) { krb5_warn (context, ret, "krb5_sendauth"); goto retry; } krb5_warnx(context, "ipropd-slave started at version: %ld", (long)server_context->log_context.version); ret = ihave (context, auth_context, master_fd, server_context->log_context.version); if (ret) goto retry; connected = TRUE; while (connected && !exit_flag) { krb5_data out; krb5_storage *sp; int32_t tmp; fd_set readset; struct timeval to; #ifndef NO_LIMIT_FD_SETSIZE if (master_fd >= FD_SETSIZE) krb5_errx (context, 1, "fd too large"); #endif FD_ZERO(&readset); FD_SET(master_fd, &readset); to.tv_sec = time_before_lost; to.tv_usec = 0; ret = select (master_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) krb5_errx (context, 1, "server didn't send a message " "in %d seconds", time_before_lost); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); if (ret) { krb5_warn (context, ret, "krb5_read_priv_message"); connected = FALSE; continue; } sp = krb5_storage_from_mem (out.data, out.length); krb5_ret_int32 (sp, &tmp); switch (tmp) { case FOR_YOU : receive (context, sp, server_context); ret = ihave (context, auth_context, master_fd, server_context->log_context.version); if (ret) connected = FALSE; break; case TELL_YOU_EVERYTHING : ret = receive_everything (context, master_fd, server_context, auth_context); if (ret) connected = FALSE; break; case ARE_YOU_THERE : send_im_here (context, master_fd, auth_context); break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_storage_free (sp); krb5_data_free (&out); } retry: if (connected == FALSE) krb5_warnx (context, "disconnected for server"); if (exit_flag) krb5_warnx (context, "got an exit signal"); if (master_fd >= 0) close(master_fd); reconnect += backoff; if (reconnect > reconnect_max) reconnect = reconnect_max; } if (0); #ifndef NO_SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); return 0; }
static krb5_error_code receive_everything (krb5_context context, int fd, kadm5_server_context *server_context, krb5_auth_context auth_context) { int ret; krb5_data data; int32_t vno = 0; int32_t opcode; krb5_storage *sp; char *dbname; HDB *mydb; krb5_warnx(context, "receive complete database"); asprintf(&dbname, "%s-NEW", server_context->db->hdb_name); ret = hdb_create(context, &mydb, dbname); if(ret) krb5_err(context,1, ret, "hdb_create"); free(dbname); ret = hdb_set_master_keyfile (context, mydb, server_context->config.stash_file); if(ret) krb5_err(context,1, ret, "hdb_set_master_keyfile"); /* I really want to use O_EXCL here, but given that I can't easily clean up on error, I won't */ ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); sp = NULL; do { ret = krb5_read_priv_message(context, auth_context, &fd, &data); if (ret) { krb5_warn (context, ret, "krb5_read_priv_message"); goto cleanup; } sp = krb5_storage_from_data (&data); if (sp == NULL) krb5_errx (context, 1, "krb5_storage_from_data"); krb5_ret_int32 (sp, &opcode); if (opcode == ONE_PRINC) { krb5_data fake_data; hdb_entry_ex entry; krb5_storage_free(sp); fake_data.data = (char *)data.data + 4; fake_data.length = data.length - 4; memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry (context, &fake_data, &entry.entry); if (ret) krb5_err (context, 1, ret, "hdb_value2entry"); ret = mydb->hdb_store(server_context->context, mydb, 0, &entry); if (ret) krb5_err (context, 1, ret, "hdb_store"); hdb_free_entry (context, &entry); krb5_data_free (&data); } else if (opcode == NOW_YOU_HAVE) ; else krb5_errx (context, 1, "strange opcode %d", opcode); } while (opcode == ONE_PRINC); if (opcode != NOW_YOU_HAVE) krb5_errx (context, 1, "receive_everything: strange %d", opcode); krb5_ret_int32 (sp, &vno); krb5_storage_free(sp); ret = kadm5_log_reinit (server_context); if (ret) krb5_err(context, 1, ret, "kadm5_log_reinit"); ret = kadm5_log_set_version (server_context, vno - 1); if (ret) krb5_err (context, 1, ret, "kadm5_log_set_version"); ret = kadm5_log_nop (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_nop"); ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name); if (ret) krb5_err (context, 1, ret, "db->rename"); cleanup: krb5_data_free (&data); ret = mydb->hdb_close (context, mydb); if (ret) krb5_err (context, 1, ret, "db->close"); ret = mydb->hdb_destroy (context, mydb); if (ret) krb5_err (context, 1, ret, "db->destroy"); krb5_warnx(context, "receive complete database, version %ld", (long)vno); return ret; }
static int proto (int sock, const char *hostname, const char *svc, char *message, size_t len) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_data data; krb5_data data_send; krb5_ccache ccache; krb5_creds creds; krb5_kdc_flags flags; krb5_principal principal; status = krb5_auth_con_init (context, &auth_context); if (status) { krb5_warn (context, status, "krb5_auth_con_init"); return 1; } status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_auth_con_setaddr"); return 1; } status = krb5_sname_to_principal (context, hostname, svc, KRB5_NT_SRV_HST, &server); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_sname_to_principal"); return 1; } status = krb5_sendauth (context, &auth_context, &sock, KF_VERSION_1, NULL, server, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, NULL, NULL, NULL, NULL, NULL); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn(context, status, "krb5_sendauth"); return 1; } if (ccache_name == NULL) ccache_name = ""; data_send.data = (void *)remote_name; data_send.length = strlen(remote_name) + 1; status = krb5_write_priv_message(context, auth_context, &sock, &data_send); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_write_message"); return 1; } data_send.data = (void *)ccache_name; data_send.length = strlen(ccache_name)+1; status = krb5_write_priv_message(context, auth_context, &sock, &data_send); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_write_message"); return 1; } memset (&creds, 0, sizeof(creds)); status = krb5_cc_default (context, &ccache); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_cc_default"); return 1; } status = krb5_cc_get_principal (context, ccache, &principal); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_cc_get_principal"); return 1; } creds.client = principal; status = krb5_make_principal (context, &creds.server, principal->realm, KRB5_TGS_NAME, principal->realm, NULL); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_make_principal"); return 1; } creds.times.endtime = 0; flags.i = 0; flags.b.forwarded = 1; flags.b.forwardable = forwardable; status = krb5_get_forwarded_creds (context, auth_context, ccache, flags.i, hostname, &creds, &data); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_get_forwarded_creds"); return 1; } status = krb5_write_priv_message(context, auth_context, &sock, &data); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_mk_priv"); return 1; } krb5_data_free (&data); status = krb5_read_priv_message(context, auth_context, &sock, &data); krb5_auth_con_free(context, auth_context); if (status) { krb5_warn (context, status, "krb5_mk_priv"); return 1; } if(data.length >= len) { krb5_warnx (context, "returned string is too long, truncating"); memcpy(message, data.data, len); message[len - 1] = '\0'; } else { memcpy(message, data.data, data.length); message[data.length] = '\0'; } krb5_data_free (&data); return(strcmp(message, "ok")); }
static int propagate_database (krb5_context context, int type, const char *database_name, HDB *db, krb5_ccache ccache, int optidx, int argc, char **argv) { krb5_principal server; krb5_error_code ret; int i, failed = 0; for(i = optidx; i < argc; i++){ krb5_auth_context auth_context; int fd; struct prop_data pd; krb5_data data; char *port, portstr[NI_MAXSERV]; char *host = argv[i]; port = strchr(host, ':'); if(port == NULL) { snprintf(portstr, sizeof(portstr), "%u", ntohs(krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT))); port = portstr; } else *port++ = '\0'; fd = open_socket(context, host, port); if(fd < 0) { failed++; krb5_warn (context, errno, "connect %s", host); continue; } ret = krb5_sname_to_principal(context, argv[i], HPROP_NAME, KRB5_NT_SRV_HST, &server); if(ret) { failed++; krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host); close(fd); continue; } if (local_realm) { krb5_realm my_realm; krb5_get_default_realm(context,&my_realm); krb5_principal_set_realm(context,server,my_realm); krb5_xfree(my_realm); } auth_context = NULL; ret = krb5_sendauth(context, &auth_context, &fd, HPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, /* in_data */ NULL, /* in_creds */ ccache, NULL, NULL, NULL); krb5_free_principal(context, server); if(ret) { failed++; krb5_warn(context, ret, "krb5_sendauth (%s)", host); close(fd); goto next_host; } pd.context = context; pd.auth_context = auth_context; pd.sock = fd; ret = iterate (context, database_name, db, type, &pd); if (ret) { krb5_warnx(context, "iterate to host %s failed", host); failed++; goto next_host; } krb5_data_zero (&data); ret = krb5_write_priv_message(context, auth_context, &fd, &data); if(ret) { krb5_warn(context, ret, "krb5_write_priv_message"); failed++; goto next_host; } ret = krb5_read_priv_message(context, auth_context, &fd, &data); if(ret) { krb5_warn(context, ret, "krb5_read_priv_message: %s", host); failed++; goto next_host; } else krb5_data_free (&data); next_host: krb5_auth_con_free(context, auth_context); close(fd); } if (failed) return 1; return 0; }
static int proto (int sock, const char *svc) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_ticket *ticket; char *name; char ret_string[10]; char hostname[MAXHOSTNAMELEN]; krb5_data data; krb5_data remotename; krb5_data tk_file; krb5_ccache ccache; char ccname[MAXPATHLEN]; struct passwd *pwd; status = krb5_auth_con_init (context, &auth_context); if (status) krb5_err(context, 1, status, "krb5_auth_con_init"); status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) krb5_err(context, 1, status, "krb5_auth_con_setaddr"); if(gethostname (hostname, sizeof(hostname)) < 0) krb5_err(context, 1, errno, "gethostname"); status = krb5_sname_to_principal (context, hostname, svc, KRB5_NT_SRV_HST, &server); if (status) krb5_err(context, 1, status, "krb5_sname_to_principal"); status = krb5_recvauth_match_version (context, &auth_context, &sock, kfd_match_version, NULL, server, 0, NULL, &ticket); if (status) krb5_err(context, 1, status, "krb5_recvauth"); status = krb5_unparse_name (context, ticket->client, &name); if (status) krb5_err(context, 1, status, "krb5_unparse_name"); if(protocol_version == 0) { data.data = "old clnt"; /* XXX old clients only had room for 10 bytes of message, and also didn't show it to the user */ data.length = strlen(data.data) + 1; krb5_write_message(context, &sock, &data); sleep(2); /* XXX give client time to finish */ krb5_errx(context, 1, "old client; exiting"); } status=krb5_read_priv_message (context, auth_context, &sock, &remotename); if (status) krb5_err(context, 1, status, "krb5_read_message"); status=krb5_read_priv_message (context, auth_context, &sock, &tk_file); if (status) krb5_err(context, 1, status, "krb5_read_message"); krb5_data_zero (&data); if(((char*)remotename.data)[remotename.length-1] != '\0') krb5_errx(context, 1, "unterminated received"); if(((char*)tk_file.data)[tk_file.length-1] != '\0') krb5_errx(context, 1, "unterminated received"); status = krb5_read_priv_message(context, auth_context, &sock, &data); if (status) { krb5_err(context, 1, errno, "krb5_read_priv_message"); goto out; } pwd = getpwnam ((char *)(remotename.data)); if (pwd == NULL) { status=1; krb5_warnx(context, "getpwnam: %s failed",(char *)(remotename.data)); goto out; } if(!krb5_kuserok (context, ticket->client, (char *)(remotename.data))) { status=1; krb5_warnx(context, "krb5_kuserok: permission denied"); goto out; } if (setgid(pwd->pw_gid) < 0) { krb5_warn(context, errno, "setgid"); goto out; } if (setuid(pwd->pw_uid) < 0) { krb5_warn(context, errno, "setuid"); goto out; } if (tk_file.length != 1) snprintf (ccname, sizeof(ccname), "%s", (char *)(tk_file.data)); else snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%lu", (unsigned long)pwd->pw_uid); status = krb5_cc_resolve (context, ccname, &ccache); if (status) { krb5_warn(context, status, "krb5_cc_resolve"); goto out; } status = krb5_cc_initialize (context, ccache, ticket->client); if (status) { krb5_warn(context, status, "krb5_cc_initialize"); goto out; } status = krb5_rd_cred2 (context, auth_context, ccache, &data); krb5_cc_close (context, ccache); if (status) { krb5_warn(context, status, "krb5_rd_cred"); goto out; } strlcpy(krb5_tkfile,ccname,sizeof(krb5_tkfile)); krb5_warnx(context, "%s forwarded ticket to %s,%s", name, (char *)(remotename.data),ccname); out: if (status) { strlcpy(ret_string, "no", sizeof(ret_string)); krb5_warnx(context, "failed"); } else { strlcpy(ret_string, "ok", sizeof(ret_string)); } krb5_data_free (&tk_file); krb5_data_free (&remotename); krb5_data_free (&data); free(name); data.data = ret_string; data.length = strlen(ret_string) + 1; status = krb5_write_priv_message(context, auth_context, &sock, &data); krb5_auth_con_free(context, auth_context); return status; }
int main(int argc, char **argv) { krb5_error_code ret, ret2; krb5_context context; krb5_auth_context auth_context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; int master_fd; krb5_ccache ccache; krb5_principal server; char **files; int optidx = 0; time_t reconnect_min; time_t backoff; time_t reconnect_max; time_t reconnect; time_t before = 0; int restarter_fd = -1; const char *master; setprogname(argv[0]); if (getarg(args, num_args, argc, argv, &optidx)) usage(1); if (help_flag) usage(0); if (version_flag) { print_version(NULL); exit(0); } if (detach_from_console && daemon_child == -1) roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(NULL); ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); setup_signal(); if (config_file == NULL) { if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1 || config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); argc -= optidx; argv += optidx; if (argc != 1) usage(1); master = argv[0]; if (status_file == NULL) { if (asprintf(&status_file, "%s/ipropd-slave-status", hdb_db_dir(context)) < 0 || status_file == NULL) krb5_errx(context, 1, "can't allocate status file buffer"); } krb5_openlog(context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); slave_status(context, status_file, "bootstrapping"); ret = krb5_kt_register(context, &hdb_get_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); time_before_lost = parse_time (server_time_lost, "s"); if (time_before_lost < 0) krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); slave_status(context, status_file, "getting credentials from keytab/database"); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_password_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; slave_status(context, status_file, "creating log file"); ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close"); get_creds(context, keytab_str, &ccache, master); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) krb5_err (context, 1, ret, "krb5_sname_to_principal"); auth_context = NULL; master_fd = -1; krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 10, &reconnect_min); krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 300, &reconnect_max); krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 10, &backoff); reconnect = reconnect_min; slave_status(context, status_file, "ipropd-slave started"); roken_detach_finish(NULL, daemon_child); restarter_fd = restarter(context, NULL); while (!exit_flag) { struct timeval to; time_t now, elapsed; fd_set readset; int connected = FALSE; #ifndef NO_LIMIT_FD_SETSIZE if (restarter_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); #endif FD_ZERO(&readset); if (restarter_fd > -1) FD_SET(restarter_fd, &readset); now = time(NULL); elapsed = now - before; if (elapsed < reconnect) { time_t left = reconnect - elapsed; krb5_warnx(context, "sleeping %d seconds before " "retrying to connect", (int)left); to.tv_sec = left; to.tv_usec = 0; if (select(restarter_fd + 1, &readset, NULL, NULL, &to) == 1) { exit_flag = SIGTERM; continue; } } before = now; slave_status(context, status_file, "connecting to master: %s\n", master); master_fd = connect_to_master (context, master, port_str); if (master_fd < 0) goto retry; reconnect = reconnect_min; if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; krb5_cc_destroy(context, ccache); get_creds(context, keytab_str, &ccache, master); } if (verbose) krb5_warnx(context, "authenticating to master"); ret = krb5_sendauth (context, &auth_context, &master_fd, IPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, ccache, NULL, NULL, NULL); if (ret) { krb5_warn (context, ret, "krb5_sendauth"); goto retry; } krb5_warnx(context, "ipropd-slave started at version: %ld", (long)server_context->log_context.version); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret) goto retry; connected = TRUE; if (verbose) krb5_warnx(context, "connected to master"); slave_status(context, status_file, "connected to master, waiting instructions"); while (connected && !exit_flag) { krb5_data out; krb5_storage *sp; uint32_t tmp; int max_fd; #ifndef NO_LIMIT_FD_SETSIZE if (master_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); if (restarter_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); max_fd = max(restarter_fd, master_fd); #endif FD_ZERO(&readset); FD_SET(master_fd, &readset); if (restarter_fd != -1) FD_SET(restarter_fd, &readset); to.tv_sec = time_before_lost; to.tv_usec = 0; ret = select (max_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) { krb5_warnx(context, "server didn't send a message " "in %d seconds", time_before_lost); connected = FALSE; continue; } if (restarter_fd > -1 && FD_ISSET(restarter_fd, &readset)) { if (verbose) krb5_warnx(context, "slave restarter exited"); exit_flag = SIGTERM; } if (!FD_ISSET(master_fd, &readset)) continue; if (verbose) krb5_warnx(context, "message from master"); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); if (ret) { krb5_warn(context, ret, "krb5_read_priv_message"); connected = FALSE; continue; } sp = krb5_storage_from_mem (out.data, out.length); if (sp == NULL) krb5_err(context, IPROPD_RESTART, errno, "krb5_storage_from_mem"); ret = krb5_ret_uint32(sp, &tmp); if (ret == HEIM_ERR_EOF) { krb5_warn(context, ret, "master sent zero-length message"); connected = FALSE; continue; } if (ret != 0) { krb5_warn(context, ret, "couldn't read master's message"); connected = FALSE; continue; } ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open while handling a " "message from the master"); ret = kadm5_log_init(server_context); if (ret) { krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while " "handling a message from the master"); } ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close while handling a " "message from the master"); switch (tmp) { case FOR_YOU : if (verbose) krb5_warnx(context, "master sent us diffs"); ret2 = receive(context, sp, server_context); if (ret2) krb5_warn(context, ret2, "receive from ipropd-master had errors"); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret || ret2) connected = FALSE; /* * If it returns an error, receive() may nonetheless * have committed some entries successfully, so we must * update the slave_status even if there were errors. */ is_up_to_date(context, status_file, server_context); break; case TELL_YOU_EVERYTHING : if (verbose) krb5_warnx(context, "master sent us a full dump"); ret = receive_everything(context, master_fd, server_context, auth_context); if (ret == 0) { ret = ihave(context, auth_context, master_fd, server_context->log_context.version); } if (ret) connected = FALSE; else is_up_to_date(context, status_file, server_context); break; case ARE_YOU_THERE : if (verbose) krb5_warnx(context, "master sent us a ping"); is_up_to_date(context, status_file, server_context); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret) connected = FALSE; send_im_here(context, master_fd, auth_context); break; case YOU_HAVE_LAST_VERSION: if (verbose) krb5_warnx(context, "master tells us we are up to date"); is_up_to_date(context, status_file, server_context); break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_storage_free (sp); krb5_data_free (&out); } slave_status(context, status_file, "disconnected from master"); retry: if (connected == FALSE) krb5_warnx (context, "disconnected for server"); if (exit_flag) krb5_warnx (context, "got an exit signal"); if (master_fd >= 0) close(master_fd); reconnect += backoff; if (reconnect > reconnect_max) { slave_status(context, status_file, "disconnected from master for a long time"); reconnect = reconnect_max; } } if (status_file) { /* XXX It'd be better to leave it saying we're not here */ unlink(status_file); } if (0); #ifndef NO_SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); return 0; }