diff options
-rw-r--r-- | src/include/kdb_log.h | 2 | ||||
-rw-r--r-- | src/kadmin/dbutil/dump.c | 231 | ||||
-rw-r--r-- | src/kadmin/server/ipropd_svc.c | 68 | ||||
-rw-r--r-- | src/lib/kdb/kdb_log.c | 2 | ||||
-rw-r--r-- | src/lib/kdb/libkdb5.exports | 1 |
5 files changed, 215 insertions, 89 deletions
diff --git a/src/include/kdb_log.h b/src/include/kdb_log.h index 51648c7e84..beecdc1241 100644 --- a/src/include/kdb_log.h +++ b/src/include/kdb_log.h @@ -108,6 +108,8 @@ typedef struct kdb_hlog { uint16_t kdb_block; /* Block size of each element */ } kdb_hlog_t; +extern void ulog_sync_header(kdb_hlog_t *); + typedef struct kdb_ent_header { uint32_t kdb_umagic; /* Update entry magic # */ kdb_sno_t kdb_entry_sno; /* Serial # of entry */ diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c index 9350dc177f..e36610469f 100644 --- a/src/kadmin/dbutil/dump.c +++ b/src/kadmin/dbutil/dump.c @@ -123,6 +123,8 @@ typedef struct _dump_version { char *header; int updateonly; int create_kadm5; + int iprop; + int ipropx; dump_func dump_princ; osa_adb_iter_policy_func dump_policy; load_func load_record; @@ -133,6 +135,8 @@ dump_version old_version = { "kdb5_edit load_dump version 2.0\n", 0, 1, + 0, + 0, dump_k5beta_iterator, NULL, process_k5beta_record, @@ -142,6 +146,8 @@ dump_version beta6_version = { "kdb5_edit load_dump version 3.0\n", 0, 1, + 0, + 0, dump_k5beta6_iterator, NULL, process_k5beta6_record, @@ -151,6 +157,8 @@ dump_version beta7_version = { "kdb5_util load_dump version 4\n", 0, 0, + 0, + 0, dump_k5beta7_princ, dump_k5beta7_policy, process_k5beta7_record, @@ -160,6 +168,8 @@ dump_version iprop_version = { "iprop", 0, 0, + 1, + 0, dump_k5beta7_princ_withpolicy, dump_k5beta7_policy, process_k5beta7_record, @@ -169,6 +179,8 @@ dump_version ov_version = { "OpenV*Secure V1.0\t", 1, 1, + 0, + 0, dump_ov_princ, dump_k5beta7_policy, process_ov_record @@ -179,6 +191,8 @@ dump_version r1_3_version = { "kdb5_util load_dump version 5\n", 0, 0, + 0, + 0, dump_k5beta7_princ_withpolicy, dump_k5beta7_policy, process_k5beta7_record, @@ -188,6 +202,8 @@ dump_version r1_8_version = { "kdb5_util load_dump version 6\n", 0, 0, + 0, + 0, dump_k5beta7_princ_withpolicy, dump_r1_8_policy, process_r1_8_record, @@ -197,6 +213,8 @@ dump_version r1_11_version = { "kdb5_util load_dump version 7\n", 0, 0, + 0, + 0, dump_k5beta7_princ_withpolicy, dump_r1_11_policy, process_r1_11_record, @@ -206,6 +224,8 @@ dump_version ipropx_1_version = { "ipropx", 0, 0, + 1, + 1, dump_k5beta7_princ_withpolicy, dump_r1_11_policy, process_r1_11_record, @@ -281,6 +301,7 @@ static const char oldoption[] = "-old"; static const char b6option[] = "-b6"; static const char b7option[] = "-b7"; static const char ipropoption[] = "-i"; +static const char conditionaloption[] = "-c"; static const char verboseoption[] = "-verbose"; static const char updateoption[] = "-update"; static const char hashoption[] = "-hash"; @@ -349,6 +370,126 @@ krb5_error_code master_key_convert(context, db_entry) return 0; } +/* Create temp file for new dump to be named ofile. */ +static FILE * +create_ofile(char *ofile, char **tmpname) +{ + int fd = -1; + FILE *f; + + *tmpname = NULL; + if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0) + goto error; + + fd = mkstemp(*tmpname); + if (fd == -1) + goto error; + + f = fdopen(fd, "w+"); + if (f != NULL) + return f; + +error: + com_err(progname, errno, + _("while allocating temporary filename dump")); + if (fd >= 0) + unlink(*tmpname); + exit(1); +} + +/* Rename new dump file into place */ +static void +finish_ofile(char *ofile, char **tmpname) +{ + if (rename(*tmpname, ofile) == -1) { + com_err(progname, errno, _("while renaming dump file into place")); + exit(1); + } + free(*tmpname); + *tmpname = NULL; +} + +/* + * Read the dump header. Returns 1 on success, 0 if the file is not a + * recognized iprop dump format. + */ +static int +parse_iprop_header(char *buf, dump_version **dv, uint32_t *last_sno, + uint32_t *last_seconds, uint32_t *last_useconds) +{ + char head[128]; + int nread; + uint32_t u[4]; + uint32_t *up = &u[0]; + + nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]); + if (nread < 1) + return 0; + + if (!strcmp(head, ipropx_1_version.header)) { + if (nread != 5) + return 0; + if (u[0] == IPROPX_VERSION_0) + *dv = &iprop_version; + else if (u[0] == IPROPX_VERSION_1) + *dv = &ipropx_1_version; + else { + fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), + progname, u[0]); + return 0; + } + up = &u[1]; + } else if (!strcmp(head, iprop_version.header)) { + if (nread != 4) + return 0; + *dv = &iprop_version; + } else { + fprintf(stderr, "Invalid iprop header\n"); + return 0; + } + + *last_sno = *(up++); + *last_seconds = *(up++); + *last_useconds = *(up++); + return 1; +} + +/* + * Return 1 if the {sno, timestamp} in an existing dump file is in the + * ulog, else return 0. + */ +static int +current_dump_sno_in_ulog(char *ifile, kdb_hlog_t *ulog) +{ + dump_version *junk; + uint32_t last_sno, last_seconds, last_useconds; + char buf[BUFSIZ]; + FILE *f; + + if (ulog->kdb_last_sno == 0) + return 0; /* nothing in ulog */ + + f = fopen(ifile, "r"); + if (f == NULL) + return 0; /* aliasing other errors to ENOENT here is OK */ + + if (fgets(buf, sizeof(buf), f) == NULL) + return errno ? -1 : 0; + fclose(f); + + if (!parse_iprop_header(buf, &junk, &last_sno, &last_seconds, + &last_useconds)) + return 0; + + if (ulog->kdb_first_sno > last_sno || + ulog->kdb_first_time.seconds > last_seconds || + (ulog->kdb_first_time.seconds == last_seconds && + ulog->kdb_first_time.useconds > last_useconds)) + return 0; + + return 1; +} + /* * Update the "ok" file. */ @@ -1128,10 +1269,11 @@ dump_db(argc, argv) FILE *f; struct dump_args arglist; char *ofile; + char *tmpofile = NULL; krb5_error_code kret, retval; dump_version *dump; int aindex; - krb5_boolean locked; + int conditional = 0; char *new_mkey_file = 0; bool_t dump_sno = FALSE; kdb_log_context *log_ctx; @@ -1187,7 +1329,9 @@ dump_db(argc, argv) exit_status++; return; } - } else if (!strcmp(argv[aindex], verboseoption)) + } else if (!strcmp(argv[aindex], conditionaloption)) + conditional = 1; + else if (!strcmp(argv[aindex], verboseoption)) arglist.flags |= FLAG_VERBOSE; else if (!strcmp(argv[aindex], "-mkey_convert")) mkey_convert = 1; @@ -1214,6 +1358,22 @@ dump_db(argc, argv) } /* + * If a conditional ipropx dump we check if the existing dump is + * good enough. + */ + if (ofile != NULL && conditional) { + if (!dump->iprop) { + com_err(progname, 0, + _("Conditional dump is an undocumented option for " + "use only for iprop dumps")); + exit_status++; + return; + } + if (current_dump_sno_in_ulog(ofile, log_ctx->ulog)) + return; + } + + /* * Make sure the database is open. The policy database only has * to be opened if we try a dump that uses it. */ @@ -1290,40 +1450,20 @@ dump_db(argc, argv) } kret = 0; - locked = 0; + if (ofile && strcmp(ofile, "-")) { /* * Discourage accidental dumping to filenames beginning with '-'. */ if (ofile[0] == '-') usage(); - /* - * Make sure that we don't open and truncate on the fopen, - * since that may hose an on-going kprop process. - * - * We could also control this by opening for read and - * write, doing an flock with LOCK_EX, and then - * truncating the file once we have gotten the lock, - * but that would involve more OS dependencies than I - * want to get into. - */ - unlink(ofile); - if (!(f = fopen(ofile, "w"))) { + f = create_ofile(ofile, &tmpofile); + if (f == NULL) { fprintf(stderr, ofopen_error, progname, ofile, error_message(errno)); exit_status++; return; } - if ((kret = krb5_lock_file(util_context, - fileno(f), - KRB5_LOCKMODE_EXCLUSIVE))) { - fprintf(stderr, oflock_error, - progname, ofile, error_message(kret)); - fclose(f); - exit_status++; - } - else - locked = 1; } else { f = stdout; } @@ -1341,8 +1481,11 @@ dump_db(argc, argv) if (krb5_db_lock(util_context, KRB5_LOCKMODE_SHARED)) { fprintf(stderr, _("%s: Couldn't grab lock\n"), progname); + if (tmpofile != NULL) + unlink(tmpofile); + free(tmpofile); exit_status++; - goto unlock_and_return; + return; } if (ipropx_version) @@ -1373,17 +1516,15 @@ dump_db(argc, argv) exit_status++; } if (ofile && f != stdout && !exit_status) { - if (locked) { - (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK); - locked = 0; - } fclose(f); + finish_ofile(ofile, &tmpofile); update_ok_file(ofile); } + if (tmpofile != NULL) + unlink(tmpofile); + free(tmpofile); + return; } -unlock_and_return: - if (locked) - (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK); } /* @@ -2512,7 +2653,6 @@ load_db(argc, argv) krb5_int32 crflags; int aindex; int db_locked = 0; - char iheader[1024]; kdb_log_context *log_ctx; krb5_boolean add_update = TRUE; uint32_t caller, last_sno, last_seconds, last_useconds; @@ -2738,26 +2878,9 @@ load_db(argc, argv) log_ctx->iproprole = IPROP_NULL; if (!add_update) { - unsigned int ipropx_version = IPROPX_VERSION_0; - - if (!strncmp(buf, "ipropx ", sizeof("ipropx ") - 1)) - sscanf(buf, "%1023s %u %u %u %u", iheader, - &ipropx_version, &last_sno, - &last_seconds, &last_useconds); - else - sscanf(buf, "%1023s %u %u %u", iheader, &last_sno, - &last_seconds, &last_useconds); - - switch (ipropx_version) { - case IPROPX_VERSION_0: - load = &iprop_version; - break; - case IPROPX_VERSION_1: - load = &ipropx_1_version; - break; - default: - fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), - progname, ipropx_version); + if (!parse_iprop_header(buf, &load, &last_sno, + &last_seconds, + &last_useconds)) { exit_status++; goto error; } diff --git a/src/kadmin/server/ipropd_svc.c b/src/kadmin/server/ipropd_svc.c index 0e45f6a17c..19c1bac95b 100644 --- a/src/kadmin/server/ipropd_svc.c +++ b/src/kadmin/server/ipropd_svc.c @@ -246,10 +246,10 @@ static kdb_fullresync_result_t * ipropx_resync(uint32_t vers, struct svc_req *rqstp) { static kdb_fullresync_result_t ret; - char *tmpf = 0; char *ubuf = 0; char clhost[MAXHOSTNAMELEN] = {0}; int pret, fret; + FILE *p; kadm5_server_handle_t handle = global_server_handle; OM_uint32 min_stat; gss_name_t name = NULL; @@ -320,23 +320,21 @@ ipropx_resync(uint32_t vers, struct svc_req *rqstp) } /* - * construct db dump file name; kprop style name + clnt fqdn - */ - if (asprintf(&tmpf, "%s_%s", KPROP_DEFAULT_FILE, clhost) < 0) { - krb5_klog_syslog(LOG_ERR, - _("%s: unable to construct db dump file name; out of memory"), - whoami); - goto out; - } - - /* - * note the -i; modified version of kdb5_util dump format + * Note the -i; modified version of kdb5_util dump format * to include sno (serial number). This argument is now * versioned (-i0 for legacy dump format, -i1 for ipropx - * version 1 format, etc) + * version 1 format, etc). + * + * The -c option ("conditional") causes the dump to dump only if no + * dump already exists or that dump is not in ipropx format, or the + * sno and timestamp in the header of that dump are outside the + * ulog. This allows us to share a single global dump with all + * slaves, since it's OK to share an older dump, as long as its sno + * and timestamp are in the ulog (then the slaves can get the + * subsequent updates very iprop). */ - if (asprintf(&ubuf, "%s dump -i%d %s </dev/null 2>&1", - KPROPD_DEFAULT_KDB5_UTIL, vers, tmpf) < 0) { + if (asprintf(&ubuf, "%s dump -i%d -c %s", + KPROPD_DEFAULT_KDB5_UTIL, vers, KPROP_DEFAULT_FILE) < 0) { krb5_klog_syslog(LOG_ERR, _("%s: cannot construct kdb5 util dump string too long; out of memory"), whoami); @@ -366,8 +364,14 @@ ipropx_resync(uint32_t vers, struct svc_req *rqstp) DPRINT(("%s: run `%s' ...\n", whoami, ubuf)); (void) signal(SIGCHLD, SIG_DFL); /* run kdb5_util(1M) dump for IProp */ - /* XXX popen can return NULL; is pclose(NULL) okay? */ - pret = pclose(popen(ubuf, "w")); + p = popen(ubuf, "w"); + if (p == NULL) { + krb5_klog_syslog(LOG_ERR, + _("%s: popen failed: %s"), + whoami, error_message(errno)); + _exit(1); + } + pret = pclose(p); DPRINT(("%s: pclose=%d\n", whoami, pret)); if (pret != 0) { /* XXX popen/pclose may not set errno @@ -384,25 +388,22 @@ ipropx_resync(uint32_t vers, struct svc_req *rqstp) } DPRINT(("%s: exec `kprop -f %s %s' ...\n", - whoami, tmpf, clhost)); + whoami, KPROP_DEFAULT_FILE, clhost)); /* XXX Yuck! */ - if (getenv("KPROP_PORT")) - pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf, - "-P", getenv("KPROP_PORT"), - clhost, NULL); - else - pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf, + if (getenv("KPROP_PORT")) { + pret = execl(KPROP_DEFAULT_FILE, "kprop", "-f", + KPROP_DEFAULT_FILE, "-P", getenv("KPROP_PORT"), clhost, NULL); - if (pret == -1) { - if (nofork) { - perror(whoami); - } - krb5_klog_syslog(LOG_ERR, - _("%s: exec failed: %s"), - whoami, - error_message(errno)); - _exit(1); + } else { + pret = execl(KPROP_DEFAULT_FILE, "kprop", "-f", + KPROP_DEFAULT_FILE, clhost, NULL); } + perror(whoami); + krb5_klog_syslog(LOG_ERR, + _("%s: exec failed: %s"), + whoami, + error_message(errno)); + _exit(1); default: /* parent */ ret.ret = UPDATE_OK; @@ -427,7 +428,6 @@ out: free(service_name); if (name) gss_release_name(&min_stat, &name); - free(tmpf); free(ubuf); return (&ret); } diff --git a/src/lib/kdb/kdb_log.c b/src/lib/kdb/kdb_log.c index 0153375ea7..dc994dde16 100644 --- a/src/lib/kdb/kdb_log.c +++ b/src/lib/kdb/kdb_log.c @@ -89,7 +89,7 @@ ulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd) /* * Sync memory to disk for the update log header. */ -static void +void ulog_sync_header(kdb_hlog_t *ulog) { diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports index 9aa8d1a4ad..43a361d0c4 100644 --- a/src/lib/kdb/libkdb5.exports +++ b/src/lib/kdb/libkdb5.exports @@ -88,6 +88,7 @@ krb5_db_promote ulog_map ulog_set_role ulog_free_entries +ulog_sync_header xdr_kdb_last_t xdr_kdb_incr_result_t xdr_kdb_fullresync_result_t |