/* * Copyright (c) 1999 - 2003 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "krb5_locl.h" #include #include #include RCSID("$Id: verify_krb5_conf.c,v 1.17.2.2 2004/02/13 16:19:44 lha Exp $"); /* verify krb5.conf */ static int dumpconfig_flag = 0; static int version_flag = 0; static int help_flag = 0; static struct getargs args[] = { {"dumpconfig", 0, arg_flag, &dumpconfig_flag, "show the parsed config files", NULL }, {"version", 0, arg_flag, &version_flag, "print version", NULL }, {"help", 0, arg_flag, &help_flag, NULL, NULL } }; static void usage (int ret) { arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "[config-file]"); exit (ret); } static int check_bytes(krb5_context context, const char *path, char *data) { if(parse_bytes(data, NULL) == -1) { krb5_warnx(context, "%s: failed to parse \"%s\" as size", path, data); return 1; } return 0; } static int check_time(krb5_context context, const char *path, char *data) { if(parse_time(data, NULL) == -1) { krb5_warnx(context, "%s: failed to parse \"%s\" as time", path, data); return 1; } return 0; } static int check_numeric(krb5_context context, const char *path, char *data) { long int v; char *end; v = strtol(data, &end, 0); if(*end != '\0') { krb5_warnx(context, "%s: failed to parse \"%s\" as a number", path, data); return 1; } return 0; } static int check_boolean(krb5_context context, const char *path, char *data) { long int v; char *end; if(strcasecmp(data, "yes") == 0 || strcasecmp(data, "true") == 0 || strcasecmp(data, "no") == 0 || strcasecmp(data, "false") == 0) return 0; v = strtol(data, &end, 0); if(*end != '\0') { krb5_warnx(context, "%s: failed to parse \"%s\" as a boolean", path, data); return 1; } if(v != 0 && v != 1) krb5_warnx(context, "%s: numeric value \"%s\" is treated as \"true\"", path, data); return 0; } static int check_524(krb5_context context, const char *path, char *data) { if(strcasecmp(data, "yes") == 0 || strcasecmp(data, "no") == 0 || strcasecmp(data, "2b") == 0 || strcasecmp(data, "local") == 0) return 0; krb5_warnx(context, "%s: didn't contain a valid option `%s'", path, data); return 1; } static int check_host(krb5_context context, const char *path, char *data) { int ret; char hostname[128]; const char *p = data; struct addrinfo *ai; /* XXX data could be a list of hosts that this code can't handle */ /* XXX copied from krbhst.c */ if(strncmp(p, "http://", 7) == 0){ p += 7; } else if(strncmp(p, "http/", 5) == 0) { p += 5; }else if(strncmp(p, "tcp/", 4) == 0){ p += 4; } else if(strncmp(p, "udp/", 4) == 0) { p += 4; } if(strsep_copy(&p, ":", hostname, sizeof(hostname)) < 0) { return 1; } hostname[strcspn(hostname, "/")] = '\0'; ret = getaddrinfo(hostname, "telnet" /* XXX */, NULL, &ai); if(ret != 0) { krb5_warnx(context, "%s: %s (%s)", path, gai_strerror(ret), hostname); return 1; } return 0; } #if 0 static int mit_entry(krb5_context context, const char *path, char *data) { krb5_warnx(context, "%s is only used by MIT Kerberos", path); return 0; } #endif struct s2i { char *s; int val; }; #define L(X) { #X, LOG_ ## X } static struct s2i syslogvals[] = { /* severity */ L(EMERG), L(ALERT), L(CRIT), L(ERR), L(WARNING), L(NOTICE), L(INFO), L(DEBUG), /* facility */ L(AUTH), #ifdef LOG_AUTHPRIV L(AUTHPRIV), #endif #ifdef LOG_CRON L(CRON), #endif L(DAEMON), #ifdef LOG_FTP L(FTP), #endif L(KERN), L(LPR), L(MAIL), #ifdef LOG_NEWS L(NEWS), #endif L(SYSLOG), L(USER), #ifdef LOG_UUCP L(UUCP), #endif L(LOCAL0), L(LOCAL1), L(LOCAL2), L(LOCAL3), L(LOCAL4), L(LOCAL5), L(LOCAL6), L(LOCAL7), { NULL, -1 } }; static int find_value(const char *s, struct s2i *table) { while(table->s && strcasecmp(table->s, s)) table++; return table->val; } static int check_log(krb5_context context, const char *path, char *data) { /* XXX sync with log.c */ int min = 0, max = -1, n; char c; const char *p = data; n = sscanf(p, "%d%c%d/", &min, &c, &max); if(n == 2){ if(c == '/') { if(min < 0){ max = -min; min = 0; }else{ max = min; } } } if(n){ p = strchr(p, '/'); if(p == NULL) { krb5_warnx(context, "%s: failed to parse \"%s\"", path, data); return 1; } p++; } if(strcmp(p, "STDERR") == 0 || strcmp(p, "CONSOLE") == 0 || (strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')) || (strncmp(p, "DEVICE", 6) == 0 && p[6] == '=')) return 0; if(strncmp(p, "SYSLOG", 6) == 0){ int ret = 0; char severity[128] = ""; char facility[128] = ""; p += 6; if(*p != '\0') p++; if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1) strsep_copy(&p, ":", facility, sizeof(facility)); if(*severity == '\0') strlcpy(severity, "ERR", sizeof(severity)); if(*facility == '\0') strlcpy(facility, "AUTH", sizeof(facility)); if(find_value(severity, syslogvals) == -1) { krb5_warnx(context, "%s: unknown syslog facility \"%s\"", path, facility); ret++; } if(find_value(severity, syslogvals) == -1) { krb5_warnx(context, "%s: unknown syslog severity \"%s\"", path, severity); ret++; } return ret; }else{ krb5_warnx(context, "%s: unknown log type: \"%s\"", path, data); return 1; } } typedef int (*check_func_t)(krb5_context, const char*, char*); struct entry { const char *name; int type; void *check_data; }; struct entry all_strings[] = { { "", krb5_config_string, NULL }, { NULL } }; struct entry v4_name_convert_entries[] = { { "host", krb5_config_list, all_strings }, { "plain", krb5_config_list, all_strings }, { NULL } }; struct entry libdefaults_entries[] = { { "accept_null_addresses", krb5_config_string, check_boolean }, { "capath", krb5_config_list, all_strings }, { "clockskew", krb5_config_string, check_time }, { "date_format", krb5_config_string, NULL }, { "default_etypes", krb5_config_string, NULL }, { "default_etypes_des", krb5_config_string, NULL }, { "default_keytab_modify_name", krb5_config_string, NULL }, { "default_keytab_name", krb5_config_string, NULL }, { "default_realm", krb5_config_string, NULL }, { "dns_proxy", krb5_config_string, NULL }, { "dns_lookup_kdc", krb5_config_string, check_boolean }, { "dns_lookup_realm", krb5_config_string, check_boolean }, { "dns_lookup_realm_labels", krb5_config_string, NULL }, { "egd_socket", krb5_config_string, NULL }, { "encrypt", krb5_config_string, check_boolean }, { "extra_addresses", krb5_config_string, NULL }, { "fcache_version", krb5_config_string, check_numeric }, { "forward", krb5_config_string, check_boolean }, { "forwardable", krb5_config_string, check_boolean }, { "http_proxy", krb5_config_string, check_host /* XXX */ }, { "ignore_addresses", krb5_config_string, NULL }, { "kdc_timeout", krb5_config_string, check_time }, { "kdc_timesync", krb5_config_string, check_boolean }, { "log_utc", krb5_config_string, check_boolean }, { "maxretries", krb5_config_string, check_numeric }, { "scan_interfaces", krb5_config_string, check_boolean }, { "srv_lookup", krb5_config_string, check_boolean }, { "srv_try_txt", krb5_config_string, check_boolean }, { "ticket_lifetime", krb5_config_string, check_time }, { "time_format", krb5_config_string, NULL }, { "transited_realms_reject", krb5_config_string, NULL }, { "v4_instance_resolve", krb5_config_string, check_boolean }, { "v4_name_convert", krb5_config_list, v4_name_convert_entries }, { "verify_ap_req_nofail", krb5_config_string, check_boolean }, { NULL } }; struct entry appdefaults_entries[] = { { "afslog", krb5_config_string, check_boolean }, { "afs-use-524", krb5_config_string, check_524 }, { "forwardable", krb5_config_string, check_boolean }, { "proxiable", krb5_config_string, check_boolean }, { "ticket_lifetime", krb5_config_string, check_time }, { "renew_lifetime", krb5_config_string, check_time }, { "no-addresses", krb5_config_string, check_boolean }, { "krb4_get_tickets", krb5_config_string, check_boolean }, #if 0 { "anonymous", krb5_config_string, check_boolean }, #endif { "", krb5_config_list, appdefaults_entries }, { NULL } }; struct entry realms_entries[] = { { "forwardable", krb5_config_string, check_boolean }, { "proxiable", krb5_config_string, check_boolean }, { "ticket_lifetime", krb5_config_string, check_time }, { "renew_lifetime", krb5_config_string, check_time }, { "warn_pwexpire", krb5_config_string, check_time }, { "kdc", krb5_config_string, check_host }, { "admin_server", krb5_config_string, check_host }, { "kpasswd_server", krb5_config_string, check_host }, { "krb524_server", krb5_config_string, check_host }, { "v4_name_convert", krb5_config_list, v4_name_convert_entries }, { "v4_instance_convert", krb5_config_list, all_strings }, { "v4_domains", krb5_config_string, NULL }, { "default_domain", krb5_config_string, NULL }, #if 0 /* MIT stuff */ { "admin_keytab", krb5_config_string, mit_entry }, { "acl_file", krb5_config_string, mit_entry }, { "dict_file", krb5_config_string, mit_entry }, { "kadmind_port", krb5_config_string, mit_entry }, { "kpasswd_port", krb5_config_string, mit_entry }, { "master_key_name", krb5_config_string, mit_entry }, { "master_key_type", krb5_config_string, mit_entry }, { "key_stash_file", krb5_config_string, mit_entry }, { "max_life", krb5_config_string, mit_entry }, { "max_renewable_life", krb5_config_string, mit_entry }, { "default_principal_expiration", krb5_config_string, mit_entry }, { "default_principal_flags", krb5_config_string, mit_entry }, { "supported_enctypes", krb5_config_string, mit_entry }, { "database_name", krb5_config_string, mit_entry }, #endif { NULL } }; struct entry realms_foobar[] = { { "", krb5_config_list, realms_entries }, { NULL } }; struct entry kdc_database_entries[] = { { "realm", krb5_config_string, NULL }, { "dbname", krb5_config_string, NULL }, { "mkey_file", krb5_config_string, NULL }, { NULL } }; struct entry kdc_entries[] = { { "database", krb5_config_list, kdc_database_entries }, { "key-file", krb5_config_string, NULL }, { "logging", krb5_config_string, check_log }, { "max-request", krb5_config_string, check_bytes }, { "require-preauth", krb5_config_string, check_boolean }, { "ports", krb5_config_string, NULL }, { "addresses", krb5_config_string, NULL }, { "enable-kerberos4", krb5_config_string, check_boolean }, { "enable-524", krb5_config_string, check_boolean }, { "enable-http", krb5_config_string, check_boolean }, { "check_ticket-addresses", krb5_config_string, check_boolean }, { "allow-null-addresses", krb5_config_string, check_boolean }, { "allow-anonymous", krb5_config_string, check_boolean }, { "v4_realm", krb5_config_string, NULL }, { "enable-kaserver", krb5_config_string, check_boolean }, { "encode_as_rep_as_tgs_rep", krb5_config_string, check_boolean }, { "kdc_warn_pwexpire", krb5_config_string, check_time }, { NULL } }; struct entry kadmin_entries[] = { { "password_lifetime", krb5_config_string, check_time }, { "default_keys", krb5_config_string, NULL }, { "use_v4_salt", krb5_config_string, NULL }, { NULL } }; struct entry log_strings[] = { { "", krb5_config_string, check_log }, { NULL } }; #if 0 struct entry kdcdefaults_entries[] = { { "kdc_ports", krb5_config_string, mit_entry }, { "v4_mode", krb5_config_string, mit_entry }, { NULL } }; #endif struct entry toplevel_sections[] = { { "libdefaults" , krb5_config_list, libdefaults_entries }, { "realms", krb5_config_list, realms_foobar }, { "domain_realm", krb5_config_list, all_strings }, { "logging", krb5_config_list, log_strings }, { "kdc", krb5_config_list, kdc_entries }, { "kadmin", krb5_config_list, kadmin_entries }, { "appdefaults", krb5_config_list, appdefaults_entries }, #if 0 /* MIT stuff */ { "kdcdefaults", krb5_config_list, kdcdefaults_entries }, #endif { NULL } }; static int check_section(krb5_context context, const char *path, krb5_config_section *cf, struct entry *entries) { int error = 0; krb5_config_section *p; struct entry *e; char *local; for(p = cf; p != NULL; p = p->next) { asprintf(&local, "%s/%s", path, p->name); for(e = entries; e->name != NULL; e++) { if(*e->name == '\0' || strcmp(e->name, p->name) == 0) { if(e->type != p->type) { krb5_warnx(context, "%s: unknown or wrong type", local); error |= 1; } else if(p->type == krb5_config_string && e->check_data != NULL) { error |= (*(check_func_t)e->check_data)(context, local, p->u.string); } else if(p->type == krb5_config_list && e->check_data != NULL) { error |= check_section(context, local, p->u.list, e->check_data); } break; } } if(e->name == NULL) { krb5_warnx(context, "%s: unknown entry", local); error |= 1; } free(local); } return error; } static void dumpconfig(int level, krb5_config_section *top) { krb5_config_section *x; for(x = top; x; x = x->next) { switch(x->type) { case krb5_config_list: if(level == 0) { printf("[%s]\n", x->name); } else { printf("%*s%s = {\n", 4 * level, " ", x->name); } dumpconfig(level + 1, x->u.list); if(level > 0) printf("%*s}\n", 4 * level, " "); break; case krb5_config_string: printf("%*s%s = %s\n", 4 * level, " ", x->name, x->u.string); break; } } } int main(int argc, char **argv) { krb5_context context; krb5_error_code ret; krb5_config_section *tmp_cf; int optind = 0; setprogname (argv[0]); ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed"); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) usage(1); if (help_flag) usage (0); if(version_flag){ print_version(NULL); exit(0); } argc -= optind; argv += optind; tmp_cf = NULL; if(argc == 0) krb5_get_default_config_files(&argv); while(*argv) { ret = krb5_config_parse_file_multi(context, *argv, &tmp_cf); if (ret != 0) krb5_warn (context, ret, "krb5_config_parse_file"); argv++; } if(dumpconfig_flag) dumpconfig(0, tmp_cf); return check_section(context, "", tmp_cf, toplevel_sections); }