errno_t check_and_export_lifetime(struct dp_option *opts, const int opt_id, const char *env_name) { int ret; char *str; krb5_deltat lifetime; bool free_str = false; str = dp_opt_get_string(opts, opt_id); if (str == NULL || *str == '\0') { DEBUG(5, ("No lifetime configured.\n")); return EOK; } if (isdigit(str[strlen(str)-1])) { str = talloc_asprintf(opts, "%ss", str); if (str == NULL) { DEBUG(1, ("talloc_asprintf failed\n")); return ENOMEM; } free_str = true; ret = dp_opt_set_string(opts, opt_id, str); if (ret != EOK) { DEBUG(1, ("dp_opt_set_string failed\n")); goto done; } } ret = krb5_string_to_deltat(str, &lifetime); if (ret != 0) { DEBUG(1, ("Invalid value [%s] for a lifetime.\n", str)); ret = EINVAL; goto done; } ret = setenv(env_name, str, 1); if (ret != EOK) { ret = errno; DEBUG(2, ("setenv [%s] failed.\n", env_name)); goto done; } ret = EOK; done: if (free_str) { talloc_free(str); } return ret; }
KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_appdefault_time(krb5_context context, const char *appname, krb5_const_realm realm, const char *option, time_t def_val, time_t *ret_val) { krb5_deltat t; char *val; krb5_appdefault_string(context, appname, realm, option, NULL, &val); if (val == NULL) { *ret_val = def_val; return; } if (krb5_string_to_deltat(val, &t)) *ret_val = def_val; else *ret_val = t; free(val); }
/* * krb5_klog_init() - Initialize logging. * * This routine parses the syntax described above to specify destinations for * com_err(3) or krb5_klog_syslog() messages generated by the caller. * * Parameters: * kcontext - Kerberos context. * ename - Entity name as it is to appear in the profile. * whoami - Entity name as it is to appear in error output. * do_com_err - Take over com_err(3) processing. * * Implicit inputs: * stderr - This is where STDERR output goes. * * Implicit outputs: * log_nentries - Number of log entries, both valid and invalid. * log_control - List of entries (log_nentries long) which contains * data for klog_com_err_proc() to use to determine * where/how to send output. */ krb5_error_code krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err) { const char *logging_profent[3]; const char *logging_defent[3]; char **logging_specs; int i, ngood; char *cp, *cp2; char savec = '\0'; int error; int do_openlog, log_facility; FILE *f; mode_t old_umask; /* Initialize */ do_openlog = 0; log_facility = 0; err_context = kcontext; /* * Look up [logging]-><ename> in the profile. If that doesn't * succeed, then look for [logging]->default. */ logging_profent[0] = "logging"; logging_profent[1] = ename; logging_profent[2] = (char *) NULL; logging_defent[0] = "logging"; logging_defent[1] = "default"; logging_defent[2] = (char *) NULL; logging_specs = (char **) NULL; ngood = 0; log_control.log_nentries = 0; if (!profile_get_values(kcontext->profile, logging_profent, &logging_specs) || !profile_get_values(kcontext->profile, logging_defent, &logging_specs)) { /* * We have a match, so we first count the number of elements */ for (log_control.log_nentries = 0; logging_specs[log_control.log_nentries]; log_control.log_nentries++); /* * Now allocate our structure. */ log_control.log_entries = (struct log_entry *) malloc(log_control.log_nentries * sizeof(struct log_entry)); if (log_control.log_entries) { /* * Scan through the list. */ for (i=0; i<log_control.log_nentries; i++) { log_control.log_entries[i].log_type = K_LOG_NONE; log_control.log_entries[i].log_2free = logging_specs[i]; /* * The format is: * <whitespace><data><whitespace> * so, trim off the leading and trailing whitespace here. */ for (cp = logging_specs[i]; isspace((int) *cp); cp++); for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; isspace((int) *cp2); cp2--); cp2++; *cp2 = '\0'; /* * Is this a file? */ if (!strncasecmp(cp, "FILE", 4)) { /* * Check for append/overwrite, then open the file. */ if (cp[4] == ':' || cp[4] == '=') { log_control.log_entries[i].lfu_fopen_mode = (cp[4] == ':') ? "a+F" : "wF"; old_umask = umask(077); f = fopen(&cp[5], log_control.log_entries[i].lfu_fopen_mode); umask(old_umask); if (f) { char rotate_kw[128]; log_control.log_entries[i].lfu_filep = f; log_control.log_entries[i].log_type = K_LOG_FILE; log_control.log_entries[i].lfu_fname = &cp[5]; log_control.log_entries[i].lfu_rotate_period = K_LOG_DEF_FILE_ROTATE_PERIOD; log_control.log_entries[i].lfu_rotate_versions = K_LOG_DEF_FILE_ROTATE_VERSIONS; log_control.log_entries[i].lfu_last_rotated = time(0); /* * Now parse for ename_"rotate" = { * period = XXX * versions = 10 * } */ if (strlen(ename) + strlen("_rotate") < sizeof (rotate_kw)) { char *time; krb5_deltat dt; int vers; strcpy(rotate_kw, ename); strcat(rotate_kw, "_rotate"); if (!profile_get_string(kcontext->profile, "logging", rotate_kw, "period", NULL, &time)) { if (time != NULL) { if (!krb5_string_to_deltat(time, &dt)) { log_control.log_entries[i].lfu_rotate_period = (time_t) dt; } free(time); } } if (!profile_get_integer( kcontext->profile, "logging", rotate_kw, "versions", K_LOG_DEF_FILE_ROTATE_VERSIONS, &vers)) { log_control.log_entries[i].lfu_rotate_versions = vers; } } } else { fprintf(stderr, gettext("Couldn't open log file %s: %s\n"), &cp[5], error_message(errno)); continue; } } } #ifdef HAVE_SYSLOG /* * Is this a syslog? */ else if (!strncasecmp(cp, "SYSLOG", 6)) { error = 0; log_control.log_entries[i].lsu_facility = LOG_AUTH; log_control.log_entries[i].lsu_severity = LOG_ERR; /* * Is there a severify specified? */ if (cp[6] == ':') { /* * Find the end of the severity. */ cp2 = strchr(&cp[7], ':'); if (cp2) { savec = *cp2; *cp2 = '\0'; cp2++; } /* * Match a severity. */ if (!strcasecmp(&cp[7], "ERR")) { log_control.log_entries[i].lsu_severity = LOG_ERR; } #ifdef LOG_EMERG else if (!strcasecmp(&cp[7], "EMERG")) { log_control.log_entries[i].lsu_severity = LOG_EMERG; } #endif /* LOG_EMERG */ #ifdef LOG_ALERT else if (!strcasecmp(&cp[7], "ALERT")) { log_control.log_entries[i].lsu_severity = LOG_ALERT; } #endif /* LOG_ALERT */ #ifdef LOG_CRIT else if (!strcasecmp(&cp[7], "CRIT")) { log_control.log_entries[i].lsu_severity = LOG_CRIT; } #endif /* LOG_CRIT */ #ifdef LOG_WARNING else if (!strcasecmp(&cp[7], "WARNING")) { log_control.log_entries[i].lsu_severity = LOG_WARNING; } #endif /* LOG_WARNING */ #ifdef LOG_NOTICE else if (!strcasecmp(&cp[7], "NOTICE")) { log_control.log_entries[i].lsu_severity = LOG_NOTICE; } #endif /* LOG_NOTICE */ #ifdef LOG_INFO else if (!strcasecmp(&cp[7], "INFO")) { log_control.log_entries[i].lsu_severity = LOG_INFO; } #endif /* LOG_INFO */ #ifdef LOG_DEBUG else if (!strcasecmp(&cp[7], "DEBUG")) { log_control.log_entries[i].lsu_severity = LOG_DEBUG; } #endif /* LOG_DEBUG */ else error = 1; /* * If there is a facility present, then parse that. */ if (cp2) { if (!strcasecmp(cp2, "AUTH")) { log_control.log_entries[i].lsu_facility = LOG_AUTH; } else if (!strcasecmp(cp2, "KERN")) { log_control.log_entries[i].lsu_facility = LOG_KERN; } else if (!strcasecmp(cp2, "USER")) { log_control.log_entries[i].lsu_facility = LOG_USER; } else if (!strcasecmp(cp2, "MAIL")) { log_control.log_entries[i].lsu_facility = LOG_MAIL; } else if (!strcasecmp(cp2, "DAEMON")) { log_control.log_entries[i].lsu_facility = LOG_DAEMON; } else if (!strcasecmp(cp2, "LPR")) { log_control.log_entries[i].lsu_facility = LOG_LPR; } else if (!strcasecmp(cp2, "NEWS")) { log_control.log_entries[i].lsu_facility = LOG_NEWS; } else if (!strcasecmp(cp2, "UUCP")) { log_control.log_entries[i].lsu_facility = LOG_UUCP; } else if (!strcasecmp(cp2, "CRON")) { log_control.log_entries[i].lsu_facility = LOG_CRON; } else if (!strcasecmp(cp2, "LOCAL0")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL0; } else if (!strcasecmp(cp2, "LOCAL1")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL1; } else if (!strcasecmp(cp2, "LOCAL2")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL2; } else if (!strcasecmp(cp2, "LOCAL3")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL3; } else if (!strcasecmp(cp2, "LOCAL4")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL4; } else if (!strcasecmp(cp2, "LOCAL5")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL5; } else if (!strcasecmp(cp2, "LOCAL6")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL6; } else if (!strcasecmp(cp2, "LOCAL7")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL7; } cp2--; *cp2 = savec; } } if (!error) { log_control.log_entries[i].log_type = K_LOG_SYSLOG; do_openlog = 1; log_facility = log_control.log_entries[i].lsu_facility; } } #endif /* HAVE_SYSLOG */ /* * Is this a standard error specification? */ else if (!strcasecmp(cp, "STDERR")) { log_control.log_entries[i].lfu_filep = fdopen(fileno(stderr), "a+F"); if (log_control.log_entries[i].lfu_filep) { log_control.log_entries[i].log_type = K_LOG_STDERR; log_control.log_entries[i].lfu_fname = "standard error"; } } /* * Is this a specification of the console? */ else if (!strcasecmp(cp, "CONSOLE")) { log_control.log_entries[i].ldu_filep = CONSOLE_OPEN("a+F"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_CONSOLE; log_control.log_entries[i].ldu_devname = "console"; } } /* * Is this a specification of a device? */ else if (!strncasecmp(cp, "DEVICE", 6)) { /* * We handle devices very similarly to files. */ if (cp[6] == '=') { log_control.log_entries[i].ldu_filep = DEVICE_OPEN(&cp[7], "wF"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_DEVICE; log_control.log_entries[i].ldu_devname = &cp[7]; } } } /* * See if we successfully parsed this specification. */ if (log_control.log_entries[i].log_type == K_LOG_NONE) { fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_1), whoami, cp); fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_2), whoami); } else ngood++; } } /* * If we didn't find anything, then free our lists. */ if (ngood == 0) { for (i=0; i<log_control.log_nentries; i++) free(logging_specs[i]); } free(logging_specs); } /* * If we didn't find anything, go for the default which is to log to * the system log. */ if (ngood == 0) { if (log_control.log_entries) free(log_control.log_entries); log_control.log_entries = &def_log_entry; log_control.log_entries->log_type = K_LOG_SYSLOG; log_control.log_entries->log_2free = (krb5_pointer) NULL; log_facility = log_control.log_entries->lsu_facility = LOG_AUTH; log_control.log_entries->lsu_severity = LOG_ERR; do_openlog = 1; log_control.log_nentries = 1; } if (log_control.log_nentries) { log_control.log_whoami = (char *) malloc(strlen(whoami)+1); if (log_control.log_whoami) strcpy(log_control.log_whoami, whoami); log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1); if (log_control.log_hostname) { gethostname(log_control.log_hostname, MAXHOSTNAMELEN); log_control.log_hostname[MAXHOSTNAMELEN] = '\0'; } #ifdef HAVE_OPENLOG if (do_openlog) { openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); log_control.log_opened = 1; } #endif /* HAVE_OPENLOG */ if (do_com_err) (void) set_com_err_hook(klog_com_err_proc); } return((log_control.log_nentries) ? 0 : ENOENT); }
errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx, struct be_ctx *bectx) { errno_t ret; time_t renew_intv = 0; krb5_deltat renew_interval_delta; char *renew_interval_str; if (dp_opt_get_bool(krb5_auth_ctx->opts, KRB5_STORE_PASSWORD_IF_OFFLINE)) { ret = init_delayed_online_authentication(krb5_auth_ctx, bectx, bectx->ev); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "init_delayed_online_authentication failed.\n"); goto done; } } renew_interval_str = dp_opt_get_string(krb5_auth_ctx->opts, KRB5_RENEW_INTERVAL); if (renew_interval_str != NULL) { ret = krb5_string_to_deltat(renew_interval_str, &renew_interval_delta); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "Reading krb5_renew_interval failed.\n"); renew_interval_delta = 0; } renew_intv = renew_interval_delta; } if (renew_intv > 0) { ret = init_renew_tgt(krb5_auth_ctx, bectx, bectx->ev, renew_intv); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "init_renew_tgt failed.\n"); goto done; } } ret = check_and_export_options(krb5_auth_ctx->opts, bectx->domain, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "check_and_export_opts failed.\n"); goto done; } ret = krb5_install_offline_callback(bectx, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_install_offline_callback failed.\n"); goto done; } ret = krb5_install_sigterm_handler(bectx->ev, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_install_sigterm_handler failed.\n"); goto done; } krb5_auth_ctx->child_debug_fd = -1; /* -1 means not initialized */ ret = child_debug_init(KRB5_CHILD_LOG_FILE, &krb5_auth_ctx->child_debug_fd); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Could not set krb5_child debugging!\n"); goto done; } ret = parse_krb5_map_user(krb5_auth_ctx, dp_opt_get_cstring(krb5_auth_ctx->opts, KRB5_MAP_USER), bectx->domain->name, &krb5_auth_ctx->name_to_primary); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "parse_krb5_map_user failed: %s:[%d]\n", sss_strerror(ret), ret); goto done; } ret = EOK; done: return ret; }
/* * Parse a restrictions field. Return NULL on failure. * * Allowed restrictions are: * [+-]flagname (recognized by krb5_flagspec_to_mask) * flag is forced to indicated value * -clearpolicy policy is forced clear * -policy pol policy is forced to be "pol" * -{expire,pwexpire,maxlife,maxrenewlife} deltat * associated value will be forced to * MIN(deltat, requested value) */ static struct kadm5_auth_restrictions * parse_restrictions(const char *str, const char *fname) { char *copy = NULL, *token, *arg, *save; const char *delims = "\t\n\f\v\r ,"; krb5_deltat delta; struct kadm5_auth_restrictions *rs; copy = strdup(str); if (copy == NULL) return NULL; rs = calloc(1, sizeof(*rs)); if (rs == NULL) { free(copy); return NULL; } rs->forbid_attrs = ~(krb5_flags)0; for (token = strtok_r(copy, delims, &save); token != NULL; token = strtok_r(NULL, delims, &save)) { if (krb5_flagspec_to_mask(token, &rs->require_attrs, &rs->forbid_attrs) == 0) { rs->mask |= KADM5_ATTRIBUTES; continue; } if (strcmp(token, "-clearpolicy") == 0) { rs->mask |= KADM5_POLICY_CLR; continue; } /* Everything else needs an argument. */ arg = strtok_r(NULL, delims, &save); if (arg == NULL) goto error; if (strcmp(token, "-policy") == 0) { if (rs->policy != NULL) goto error; rs->policy = strdup(arg); if (rs->policy == NULL) goto error; rs->mask |= KADM5_POLICY; continue; } /* All other arguments must be a deltat. */ if (krb5_string_to_deltat(arg, &delta) != 0) goto error; if (strcmp(token, "-expire") == 0) { rs->princ_lifetime = delta; rs->mask |= KADM5_PRINC_EXPIRE_TIME; } else if (strcmp(token, "-pwexpire") == 0) { rs->pw_lifetime = delta; rs->mask |= KADM5_PW_EXPIRATION; } else if (strcmp(token, "-maxlife") == 0) { rs->max_life = delta; rs->mask |= KADM5_MAX_LIFE; } else if (strcmp(token, "-maxrenewlife") == 0) { rs->max_renewable_life = delta; rs->mask |= KADM5_MAX_RLIFE; } else { goto error; } } free(copy); return rs; error: krb5_klog_syslog(LOG_ERR, _("%s: invalid restrictions: %s"), fname, str); free(copy); free(rs->policy); free(rs); return NULL; }
errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx, struct be_ctx *bectx) { errno_t ret; FILE *debug_filep; time_t renew_intv = 0; krb5_deltat renew_interval_delta; char *renew_interval_str; if (dp_opt_get_bool(krb5_auth_ctx->opts, KRB5_STORE_PASSWORD_IF_OFFLINE)) { ret = init_delayed_online_authentication(krb5_auth_ctx, bectx, bectx->ev); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "init_delayed_online_authentication failed.\n"); goto done; } } renew_interval_str = dp_opt_get_string(krb5_auth_ctx->opts, KRB5_RENEW_INTERVAL); if (renew_interval_str != NULL) { ret = krb5_string_to_deltat(renew_interval_str, &renew_interval_delta); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "Reading krb5_renew_interval failed.\n"); renew_interval_delta = 0; } renew_intv = renew_interval_delta; } if (renew_intv > 0) { ret = init_renew_tgt(krb5_auth_ctx, bectx, bectx->ev, renew_intv); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "init_renew_tgt failed.\n"); goto done; } } ret = check_and_export_options(krb5_auth_ctx->opts, bectx->domain, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "check_and_export_opts failed.\n"); goto done; } ret = krb5_install_offline_callback(bectx, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_install_offline_callback failed.\n"); goto done; } ret = krb5_install_sigterm_handler(bectx->ev, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_install_sigterm_handler failed.\n"); goto done; } if (debug_to_file != 0) { ret = open_debug_file_ex(KRB5_CHILD_LOG_FILE, &debug_filep, false); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n", ret, strerror(ret)); goto done; } krb5_auth_ctx->child_debug_fd = fileno(debug_filep); if (krb5_auth_ctx->child_debug_fd == -1) { DEBUG(SSSDBG_FATAL_FAILURE, "fileno failed [%d][%s]\n", errno, strerror(errno)); ret = errno; goto done; } } done: return ret; }
int main (void) { struct { char *string; krb5_deltat expected; int is_error; #define GOOD(STR,VAL) { STR, VAL, 0 } #define BAD(STR) { STR, 0, 1 } #define DAY (24 * 3600) #define HOUR 3600 #ifdef MIN #undef MIN #endif #define MIN 60 } values[] = { /* d-h-m-s patterns */ GOOD ("3d", 3*DAY), GOOD ("3h", 3*HOUR), GOOD ("3m", 3*MIN), GOOD ("3s", 3), BAD ("3dd"), GOOD ("3d4m 42s", 3 * DAY + 4 * MIN + 42), GOOD ("3d-1h", 3 * DAY - 1 * HOUR), GOOD ("3d -1h", 3 * DAY - HOUR), GOOD ("3d4h5m6s", 3 * DAY + 4 * HOUR + 5 * MIN + 6), BAD ("3d4m5h"), GOOD ("12345s", 12345), GOOD ("1m 12345s", MIN + 12345), GOOD ("1m12345s", MIN + 12345), GOOD ("3d 0m", 3 * DAY), GOOD ("3d 0m ", 3 * DAY), GOOD ("3d \n\t 0m ", 3 * DAY), /* colon patterns */ GOOD ("42-13:42:47", 42 * DAY + 13 * HOUR + 42 * MIN + 47), BAD ("3: 4"), BAD ("13:0003"), GOOD ("12:34", 12 * HOUR + 34 * MIN), GOOD ("1:02:03", 1 * HOUR + 2 * MIN + 3), BAD ("3:-4"), /* XX We might want to require exactly two digits after a colon? */ GOOD ("3:4", 3 * HOUR + 4 * MIN), /* misc */ GOOD ("42", 42), BAD ("1-2"), /* Test overflow limitations */ GOOD ("2147483647s", 2147483647), BAD ("2147483648s"), GOOD ("24855d", 24855 * DAY), BAD ("24856d"), BAD ("24855d 100000000h"), GOOD ("24855d 3h", 24855 * DAY + 3 * HOUR), BAD ("24855d 4h"), GOOD ("24855d 11647s", 24855 * DAY + 11647), BAD ("24855d 11648s"), GOOD ("24855d 194m 7s", 24855 * DAY + 194 * MIN + 7), BAD ("24855d 194m 8s"), BAD ("24855d 195m"), BAD ("24855d 19500000000m"), GOOD ("24855d 3h 14m 7s", 24855 * DAY + 3 * HOUR + 14 * MIN + 7), BAD ("24855d 3h 14m 8s"), GOOD ("596523h", 596523 * HOUR), BAD ("596524h"), GOOD ("596523h 847s", 596523 * HOUR + 847), BAD ("596523h 848s"), GOOD ("596523h 14m 7s", 596523 * HOUR + 14 * MIN + 7), BAD ("596523h 14m 8s"), GOOD ("35791394m", 35791394 * MIN), GOOD ("35791394m7s", 35791394 * MIN + 7), BAD ("35791394m8s"), /* Test underflow */ GOOD ("-2147483647s", -2147483647), /* This should be valid, but isn't */ /*BAD ("-2147483648s"),*/ GOOD ("-24855d", -24855 * DAY), BAD ("-24856d"), BAD ("-24855d -100000000h"), GOOD ("-24855d -3h", -24855 * DAY - 3 * HOUR), BAD ("-24855d -4h"), GOOD ("-24855d -11647s", -24855 * DAY - 11647), BAD ("-24855d -11649s"), GOOD ("-24855d -194m -7s", -24855 * DAY - 194 * MIN - 7), BAD ("-24855d -194m -9s"), BAD ("-24855d -195m"), BAD ("-24855d -19500000000m"), GOOD ("-24855d -3h -14m -7s", -24855 * DAY - 3 * HOUR - 14 * MIN - 7), BAD ("-24855d -3h -14m -9s"), GOOD ("-596523h", -596523 * HOUR), BAD ("-596524h"), GOOD ("-596523h -847s", -596523 * HOUR - 847), GOOD ("-596523h -848s", -596523 * HOUR - 848), BAD ("-596523h -849s"), GOOD ("-596523h -14m -8s", -596523 * HOUR - 14 * MIN - 8), BAD ("-596523h -14m -9s"), GOOD ("-35791394m", -35791394 * MIN), GOOD ("-35791394m7s", -35791394 * MIN + 7), BAD ("-35791394m-9s"), }; int fail = 0; size_t i; for (i = 0; i < sizeof(values)/sizeof(values[0]); i++) { krb5_deltat result; krb5_error_code code; code = krb5_string_to_deltat (values[i].string, &result); if (code && !values[i].is_error) { fprintf (stderr, "unexpected error for `%s'\n", values[i].string); fail++; } else if (!code && values[i].is_error) { fprintf (stderr, "expected but didn't get error for `%s'\n", values[i].string); fail++; } else if (code && values[i].is_error) { /* do nothing */ } else if (result != values[i].expected) { fprintf (stderr, "got %ld instead of expected %ld for `%s'\n", (long) result, (long) values[i].expected, values[i].string); fail++; } } if (fail == 0) printf ("Passed all %d tests.\n", (int)i); else printf ("Failed %d of %d tests.\n", fail, (int)i); return fail; }