/* krbck.c -- Test KERBEROS_V4 server configuration * $Id: krbck.c,v 1.15 2003/02/13 20:15:26 rjs3 Exp $ * Copyright (c) 1998-2003 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #define EXIT_CHOKE (int) 10 #if !defined(CONFIG_FILENAME) #define CONFIG_FILENAME "/etc/imapd.conf" #endif #define MAX_FILENAME_LEN 255 static int evalcheck(char*, int, char*); static void evalerrorno(int); extern char* strerror (int); void fatal(const char* string, int choke) { fprintf(stderr,"\tfatal: %s", string); exit(choke); } /* arguments: * m: message to print announcing test * c: a test that should be true * f: a message to print if it's not */ int evalcheck(m, c, f) char *m; int c; char *f; { printf("Test: %s ... ",m); if (c) { printf("Ok.\n"); } else { printf("\n\tfailed: %s\n", f); } return !c; } /* Print message vased on ern (errno passed to fn). */ void evalerrno(ern) int ern; { char *s = NULL; s = strerror((int) ern); printf("errno: %s\n", s); } int main(argc, argv) int argc; char **argv; { struct stat statbuf; struct servent *servent = NULL; char realm[REALM_SZ]; char hostname[REALM_SZ]; char *phost, *pc; char tktstring[MAX_FILENAME_LEN]; /* for reading /etc/imapd.conf */ FILE *infile; int lineno = 0; int alloced = 0; char buf[4096]; char tempbuf[4096]; char *p, *key, *srvtab = NULL; char *configdirectory = NULL; struct hostent *local; int err; KTEXT_ST authent; AUTH_DAT ad; /* getservbyname("kerberos", "udp") returns port 750 * if fails, tell them to add "kerberos 750/udp" to /etc/services */ servent = getservbyname("kerberos", "udp"); if (evalcheck("getservbyname(\"kerberos\", \"udp\")", servent->s_port == htons(750), "Add \"kerberos 750/udp\" to /etc/services.")) { exit(EXIT_CHOKE); } /* Files /etc/krb.conf and /etc/krb.realms exist */ if (evalcheck("looking for /etc/krb.conf", !stat("/etc/krb.conf", &statbuf), "stat of /etc/krb.conf failed.")) { evalerrno(errno); exit(EXIT_CHOKE); } if (evalcheck("looking for /etc/krb.realms", !stat("/etc/krb.realms", &statbuf), "stat of /etc/krb.realms failed.")) { evalerrno(errno); exit(EXIT_CHOKE); } /* Can call krb_get_lrealm(realm, 0) */ krb_get_lrealm(realm, 0); if (evalcheck("krb_get_lrealm(realm, 0)", *realm != '\0', "local realm is not set in /etc/krb.conf")) { printf("\tkrb_get_lrealm returned \"\"\n"); } else { printf("\t[realm == %s]\n", realm); } /* krb_realmofhost(gethostname()) is equal to krb_get_lrealm() */ if (gethostname(hostname, REALM_SZ)) { printf("gethostname() failed!\n"); evalerrno(errno); exit(EXIT_CHOKE); } if (evalcheck("krb_realmofhost(gethostname()) == krb_get_lrealm() above", !strcmp(krb_realmofhost(hostname), realm), "/etc/krb.realms does not agree with /etc/krb.conf\n")) { printf("\thostname=%s krb_realmofhost(hostname)=%s\n", hostname, krb_realmofhost(hostname)); exit(EXIT_CHOKE); } /* srvtab option is set in /etc/imapd.conf */ /* This code is stolen directly from Cyrus and simplified (we only * need two options).*/ infile = fopen(CONFIG_FILENAME, "r"); if (!infile) { snprintf(buf, sizeof(buf), "can't open configuration file %s: %s", CONFIG_FILENAME, strerror(errno)); fatal(buf, EXIT_CHOKE); } while (fgets(buf, sizeof(buf), infile)) { lineno++; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; for (p = buf; *p && isspace(*p); p++); if (!*p || *p == '#') continue; key = p; while (*p && (isalnum((unsigned char) *p) || *p == '-')) { if (isupper((unsigned char) *p)) *p = tolower((unsigned char) *p); p++; } if (*p != ':') { snprintf(buf, sizeof(buf), "invalid option name on line %d of configuration file", lineno); fatal(buf, EXIT_CHOKE); } *p++ = '\0'; while (*p && isspace(*p)) p++; if (!*p) { snprintf(buf, sizeof(buf), "empty option value on line %d of configuration file", lineno); fatal(buf, EXIT_CHOKE); } if (!strcmp(key, "srvtab")) { /* "right thing" is in p */ srvtab = xstrdup(p); } if (!strcmp(key, "configdirectory")) { /* "right thing" is in p */ configdirectory = xstrdup(p); } } fclose(infile); snprintf(tempbuf, sizeof(tempbuf), "configdirectory option not set in %s", CONFIG_FILENAME); if (evalcheck("looking up configdirectory option", (configdirectory != NULL), tempbuf)) { exit(EXIT_CHOKE); } else { printf("\tconfigdirectory is set to \"%s\"\n", configdirectory); } /* configdirectory exists and is readable */ if (evalcheck("looking for configdirectory", !stat(configdirectory, &statbuf), "stat of configdirectory failed")) { evalerrno(errno); exit(EXIT_CHOKE); } /* running as cyrus? */ if (evalcheck("running as cyrus user", (getuid() == statbuf.st_uid), "Run this program as the cyrus user.")) { exit(EXIT_CHOKE); } snprintf(tempbuf, sizeof(tempbuf), "srvtab option not set in %s", CONFIG_FILENAME); if (evalcheck("looking up srvtab option", (srvtab != NULL), tempbuf)) { exit(EXIT_CHOKE); } else { printf("\tsrvtab file set to \"%s\"\n", srvtab); } /* srvtab file exists and is readable */ if (evalcheck("looking for srvtab file", !stat(srvtab, &statbuf), "stat of srvtab file failed")) { evalerrno(errno); exit(EXIT_CHOKE); } if (evalcheck("checking cyrus' read rights on srvtab", (statbuf.st_mode & 256 /*4 + 32 + 256*/), /* either group or owner or everyone, I guess */ "can't read srvtab file")) { printf("\trights -- %d\n", statbuf.st_mode); exit(EXIT_CHOKE); } if (evalcheck("checking everyone else's read rights on srvtab", !((statbuf.st_mode) & 4), "srvtab should not be world readable")) { printf("\trights -- %d\n", statbuf.st_mode); exit(EXIT_CHOKE); } if (evalcheck("checking if cyrus owns srvtab file", (getuid() == statbuf.st_uid), "cyrus should own the srvtab file")) { exit(EXIT_CHOKE); } /* srvtab file has imap.host entry in it (there's some kerberos * lib call to fetch this) */ /* Derrick says code below does that */ /* sprintf(tktstring, "/tmp/tkt_pid_%d", getpid()); * krb_set_tkt_string(tktstring); * krb_get_svc_in_tkt("imap", phost, realm, "krbtgt", realm, 127, * srvtab) succeeds * * If fails, use error message to diagnose: * * no such user --> imap.host entry is not in krb database * * bad passwd --> imap.host passwd in srvtab doesn't * * match krb database */ /* XXX phost is just mach name, right? */ phost = xstrdup(hostname); pc = strchr(phost, '.'); if (pc!=NULL) { *pc = '\0'; } printf("\tserver's Kerberos identity: imap.%s@%s\n", phost, realm); snprintf(tktstring, sizeof(tktstring), "/tmp/tkt_pid_%d", getpid()); krb_set_tkt_string(tktstring); if (evalcheck("attempting to get tickets (krb_get_svc_in_tkt)", (err = krb_get_svc_in_tkt("imap", phost, realm, "krbtgt", realm, 127, srvtab)) == INTK_OK , "couldn't get tickets!")) { switch (err) { case INTK_PROT: printf("\terror code indicates wrong protocol version (INTK_PROT)\n."); break; #ifdef INTK_PW_NULL case INTK_PW_NULL: printf("\terror code indicates password was NULL\n"); break; #endif case INTK_BADPW: printf("\terror code indicates bad password -- imap.%s@%s passwd in srvtab doesn't match kerberos database (INTK_BADPW)\n", phost, realm); break; case INTK_ERR: printf("\tticket is for the wrong server or ticket store can't be initialized (INTK_ERR)\n"); break; case KDC_PR_UNKNOWN: printf("\terror code indicates imap.%s@%s is not in the Kerberos server database\n", phost, realm); break; default: printf("\tReason unknown (error = %d)\n", err); } exit(EXIT_CHOKE); } /* krb_mk_req(buf, "imap", phost, realm, 0) * use error message to diagnose * clock skew between us and kerberos server */ if (evalcheck("krb_mk_request(&authent,\"imap\", phost, realm, 0)", !(err=krb_mk_req(&authent, "imap", phost, realm, 0)), "returned nonzero")) { printf("\terror is: %s\n", error_message(err)); } /* krb_rd_req(...) int krb_rd_req(authent,service,instance,from_addr,ad,fn) KTEXT authent; char *service; char *instance; u_long from_addr; AUTH_DAT *ad; char *fn; */ if (evalcheck("krb_rd_req(&authent, \"imap\", phost, 0L, &ad, srvtab)", !(err=krb_rd_req(&authent, "imap", phost, 0L /* XXX why? */, &ad, srvtab)), "returned nonzero")) { printf("\terror is: %s\n", error_message(err)); exit(EXIT_CHOKE); } printf("Done.\n"); free(phost); return 0; }