diff options
-rw-r--r-- | server/access.h | 59 | ||||
-rw-r--r-- | server/acl_files.c | 584 | ||||
-rw-r--r-- | server/kopt.c | 481 | ||||
-rw-r--r-- | server/new.h | 6 | ||||
-rw-r--r-- | server/unix.h | 113 | ||||
-rw-r--r-- | server/zalloc.c | 125 | ||||
-rw-r--r-- | server/zalloc.h | 5 |
7 files changed, 1373 insertions, 0 deletions
diff --git a/server/access.h b/server/access.h new file mode 100644 index 0000000..ec7903e --- /dev/null +++ b/server/access.h @@ -0,0 +1,59 @@ +/* + * This file is part of the Project Athena Zephyr Notification System. + * + * It contains declarations for use in the server, relating to access + * control. + * + * Created by Ken Raeburn. + * + * $Source$ + * $Author$ + * $Id$ + * + * Copyright (c) 1990 by the Massachusetts Institute of Technology. + * For copying and distribution information, see the file + * "mit-copyright.h". + */ + +#include <zephyr/mit-copyright.h> + +#include <zephyr/acl.h> +#include "ZString.h" +#include "unix.h" + +typedef enum _ZAccess_t { + TRANSMIT, /* use transmission acl */ + SUBSCRIBE, /* use subscription acl */ + INSTWILD, /* use instance wildcard acl */ + INSTUID /* use instance UID identity acl */ +} ZAccess_t; + +class ZAcl_t { + char *acl_filename; + int acl_types; /* Flag field indcating which acls + are present. Used ONLY in access.c */ + public: + int ok (ZString, ZAccess_t); + ZAcl_t (const char *path) { + extern char * strsave (const char *); + acl_filename = strsave (path); + acl_types = 0; + check (); + } + ~ZAcl_t () { + xfree (acl_filename); + } + private: + void check (void); + void check_acl_type (ZAccess_t, int); +}; + +inline int access_check(ZString sender, ZAcl_t *acl, ZAccess_t accesstype) { + return acl->ok (sender, accesstype); +} + +/* found in access.c */ +extern void access_init (void), access_reinit (void); + +/* external data relevant */ +extern int zdebug; diff --git a/server/acl_files.c b/server/acl_files.c new file mode 100644 index 0000000..203274c --- /dev/null +++ b/server/acl_files.c @@ -0,0 +1,584 @@ +/* This file is part of the Project Athena Zephyr Notification System. + * It contains functions for maintaining Access Control Lists. + * + * Created by: John T. Kohl + * + * $Source$ + * $Author$ + * + * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology. + * For copying and distribution information, see the file + * "mit-copyright.h". + */ + +/* Define this if you really want the ACL-writing code included. */ +/* #define WRITE_ACL */ + +/* + * Stolen from lib/acl_files.c because acl_load needs to be externally + * declared and not statically declared. + */ + +#include <zephyr/mit-copyright.h> + +#ifndef lint +static char rcsid_acl_files_c[] = "$Id$"; +#endif lint + +/*** Routines for manipulating access control list files ***/ + +#include <zephyr/zephyr.h> +#include <strings.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <ctype.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN */ + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 64 /* How many acls to cache */ +#define ACL_LEN 256 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = index(principal, INST_SEP); + atsign = index(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } +#ifdef KERBEROS + else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +#endif +} + +#ifdef notdef +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ +#ifdef WRITE_ACL + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +#else + abort (); +#endif +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ +#ifdef WRITE_ACL + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +#else + abort (); +#endif +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +#endif /* notdef */ + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static void +destroy_hash(h) + struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + +#if 0 + fprintf (stderr, "adding %s to acl hash %08X\n", el, h); +#endif + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + +#if 0 + fprintf (stderr, "looking for %s in acl %08X\n", el, h); +#endif + for(hv = hashval(el) % h->size; h->tbl[hv]; hv = (hv + 1) % h->size) { +#if 0 + fprintf (stderr, "\tstrcmp (%s,...)\n", h->tbl[hv]); +#endif + if (!strcmp(h->tbl[hv], el)) { +#if 0 + fprintf (stderr, "success!\n"); +#endif + return 1; + } + } +#if 0 + fprintf (stderr, "failure\n"); +#endif + return 0; +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +int acl_load(name) +char *name; +{ + int i,fd; + FILE *f; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if (!strcmp(acl_cache[i].filename, name)) + goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if we need to reload the ACL + */ + if (acl_cache[i].acl == (struct hashtbl *) 0) { + /* Gotta reload */ +#if 0 + fprintf (stderr, "attempting to load %s\n", name); +#endif + if ((f = fopen(name, "r")) == NULL) { +#if 0 + perror (name); +#endif + return -1; + } + if (acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + } + return(i); +} + +/* + * This destroys all cached ACL's so that new ones will be loaded in + * the next time they are requested. + */ +acl_cache_reset() +{ + int i; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) + if (acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + acl_cache_count = 0; + acl_cache_next = 0; +} + + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + +#if 0 + fprintf (stderr, "checking for %s in %s\n", principal, acl); +#endif + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if (acl_exact_match(acl, canon)) + return 1; + + /* Try the wildcards */ + realm = index(canon, REALM_SEP); + *index(canon, INST_SEP) = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return 1; + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +#ifdef notdef +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if(fputs(acl_cache[idx].acl->tbl[i], new) == NULL + || putc('\n', new) != '\n') { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} +#endif /* notdef */ diff --git a/server/kopt.c b/server/kopt.c new file mode 100644 index 0000000..64b7417 --- /dev/null +++ b/server/kopt.c @@ -0,0 +1,481 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1985, 1986, 1987, 1988, 1990 by the Massachusetts Institute + * of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + */ + +/* + * This includes code taken from: + * $Kerberos: rd_req.c,v 4.16 89/03/22 14:52:06 jtkohl Exp $ + * $Kerberos: prot.h,v 4.13 89/01/24 14:27:22 jtkohl Exp $ + * $Kerberos: krb_conf.h,v 4.0 89/01/23 09:59:27 jtkohl Exp $ + */ + +#ifdef KERBEROS +#ifndef NOENCRYPTION + +#ifndef lint +static char *rcsid_rd_req_c = + "$Header$"; +#endif /* lint */ + +#include <zephyr/mit-copyright.h> +#include <stdio.h> +#include <krb.h> + +/* Byte ordering */ +extern int krbONE; +#define HOST_BYTE_ORDER (* (char *) &krbONE) + +#define KRB_PROT_VERSION 4 + +/* Message types , always leave lsb for byte order */ + +#define AUTH_MSG_KDC_REQUEST 1<<1 +#define AUTH_MSG_KDC_REPLY 2<<1 +#define AUTH_MSG_APPL_REQUEST 3<<1 +#define AUTH_MSG_APPL_REQUEST_MUTUAL 4<<1 +#define AUTH_MSG_ERR_REPLY 5<<1 +#define AUTH_MSG_PRIVATE 6<<1 +#define AUTH_MSG_SAFE 7<<1 +#define AUTH_MSG_APPL_ERR 8<<1 +#define AUTH_MSG_DIE 63<<1 + +/* values for kerb error codes */ + +#define KERB_ERR_OK 0 +#define KERB_ERR_NAME_EXP 1 +#define KERB_ERR_SERVICE_EXP 2 +#define KERB_ERR_AUTH_EXP 3 +#define KERB_ERR_PKT_VER 4 +#define KERB_ERR_NAME_MAST_KEY_VER 5 +#define KERB_ERR_SERV_MAST_KEY_VER 6 +#define KERB_ERR_BYTE_ORDER 7 +#define KERB_ERR_PRINCIPAL_UNKNOWN 8 +#define KERB_ERR_PRINCIPAL_NOT_UNIQUE 9 +#define KERB_ERR_NULL_KEY 10 + +#include <sys/time.h> +#include <strings.h> + +extern int krb_ap_req_debug; + +/* + * Keep the following information around for subsequent calls + * to this routine by the same server using the same key. + */ + +/* Kerberos shouldn't stick us with array types... */ +typedef struct { + des_key_schedule s; +} Sched; + +static Sched serv_key; /* Key sched to decrypt ticket */ +static des_cblock ky; /* Initialization vector */ +static int st_kvno; /* version number for this key */ +static char st_rlm[REALM_SZ]; /* server's realm */ +static char st_nam[ANAME_SZ]; /* service name */ +static char st_inst[INST_SZ]; /* server's instance */ + +/* + * Cache of key schedules + */ +#define HASH_SIZE_1 255 /* not a power of 2 */ +#define HASH_SIZE_2 3 +static unsigned long last_use; +typedef struct { + unsigned long last_time_used; + des_cblock key; + Sched schedule; +} KeySchedRec; +static KeySchedRec scheds[HASH_SIZE_1][HASH_SIZE_2]; + +static Sched* check_key_sched_cache (des_cblock key) { + unsigned int hash_value = key[0] + key[1] * 256; + KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1]; + int i; + + for (i = HASH_SIZE_2 - 1; i >= 0; i--) + if (rec[i].last_time_used + && key[0] == rec[i].key[0] + && !bcmp (key, rec[i].key, sizeof (des_cblock))) { + rec[i].last_time_used = last_use++; + return &rec[i].schedule; + } + return 0; +} + +static void add_to_key_sched_cache (des_cblock key, Sched* sched) { + unsigned int hash_value = key[0] + key[1] * 256; + KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1]; + int i, oldest = HASH_SIZE_2 - 1; + + for (i = HASH_SIZE_2 - 1; i >= 0; i--) { + if (rec[i].last_time_used == 0) { + oldest = i; + break; + } + if (rec[i].last_time_used < rec[oldest].last_time_used) + oldest = i; + } + bcopy (key, rec[oldest].key, sizeof (des_cblock)); + rec[oldest].schedule = *sched; + rec[oldest].last_time_used = last_use++; +} + +/* + * This file contains two functions. krb_set_key() takes a DES + * key or password string and returns a DES key (either the original + * key, or the password converted into a DES key) and a key schedule + * for it. + * + * krb_rd_req() reads an authentication request and returns information + * about the identity of the requestor, or an indication that the + * identity information was not authentic. + */ + +/* + * krb_set_key() takes as its first argument either a DES key or a + * password string. The "cvt" argument indicates how the first + * argument "key" is to be interpreted: if "cvt" is null, "key" is + * taken to be a DES key; if "cvt" is non-null, "key" is taken to + * be a password string, and is converted into a DES key using + * string_to_key(). In either case, the resulting key is returned + * in the external static variable "ky". A key schedule is + * generated for "ky" and returned in the external static variable + * "serv_key". + * + * This routine returns the return value of des_key_sched. + * + * krb_set_key() needs to be in the same .o file as krb_rd_req() so that + * the key set by krb_set_key() is available in private storage for + * krb_rd_req(). + */ + +int +krb_set_key(key,cvt) + char *key; + int cvt; +{ +#ifdef NOENCRYPTION + bzero(ky, sizeof(ky)); + return KSUCCESS; +#else /* Encrypt */ + Sched *s; + int ret; + + if (cvt) + string_to_key(key,ky); + else + bcopy(key,(char *)ky,8); + + s = check_key_sched_cache (ky); + if (s) { + serv_key = *s; + return 0; + } + ret = des_key_sched (ky, serv_key.s); + add_to_key_sched_cache (ky, &serv_key); + return ret; +#endif /* NOENCRYPTION */ +} + + +/* + * krb_rd_req() takes an AUTH_MSG_APPL_REQUEST or + * AUTH_MSG_APPL_REQUEST_MUTUAL message created by krb_mk_req(), + * checks its integrity and returns a judgement as to the requestor's + * identity. + * + * The "authent" argument is a pointer to the received message. + * The "service" and "instance" arguments name the receiving server, + * and are used to get the service's ticket to decrypt the ticket + * in the message, and to compare against the server name inside the + * ticket. "from_addr" is the network address of the host from which + * the message was received; this is checked against the network + * address in the ticket. If "from_addr" is zero, the check is not + * performed. "ad" is an AUTH_DAT structure which is + * filled in with information about the sender's identity according + * to the authenticator and ticket sent in the message. Finally, + * "fn" contains the name of the file containing the server's key. + * (If "fn" is NULL, the server's key is assumed to have been set + * by krb_set_key(). If "fn" is the null string ("") the default + * file KEYFILE, defined in "krb.h", is used.) + * + * krb_rd_req() returns RD_AP_OK if the authentication information + * was genuine, or one of the following error codes (defined in + * "krb.h"): + * + * RD_AP_VERSION - wrong protocol version number + * RD_AP_MSG_TYPE - wrong message type + * RD_AP_UNDEC - couldn't decipher the message + * RD_AP_INCON - inconsistencies found + * RD_AP_BADD - wrong network address + * RD_AP_TIME - client time (in authenticator) + * too far off server time + * RD_AP_NYV - Kerberos time (in ticket) too + * far off server time + * RD_AP_EXP - ticket expired + * + * For the message format, see krb_mk_req(). + * + * Mutual authentication is not implemented. + */ + +krb_rd_req(authent,service,instance,from_addr,ad,fn) + register KTEXT authent; /* The received message */ + char *service; /* Service name */ + char *instance; /* Service instance */ + long from_addr; /* Net address of originating host */ + AUTH_DAT *ad; /* Structure to be filled in */ + char *fn; /* Filename to get keys from */ +{ + KTEXT_ST ticket; /* Temp storage for ticket */ + KTEXT tkt = &ticket; + KTEXT_ST req_id_st; /* Temp storage for authenticator */ + register KTEXT req_id = &req_id_st; + + struct timeval t_local; + + char realm[REALM_SZ]; /* Realm of issuing kerberos */ + Sched seskey_sched, *sched; /* Key sched for session key */ + unsigned char skey[KKEY_SZ]; /* Session key from ticket */ + char sname[SNAME_SZ]; /* Service name from ticket */ + char iname[INST_SZ]; /* Instance name from ticket */ + char r_aname[ANAME_SZ]; /* Client name from authenticator */ + char r_inst[INST_SZ]; /* Client instance from authenticator */ + char r_realm[REALM_SZ]; /* Client realm from authenticator */ + unsigned int r_time_ms; /* Fine time from authenticator */ + unsigned long r_time_sec; /* Coarse time from authenticator */ + register char *ptr; /* For stepping through */ + unsigned long delta_t; /* Time in authenticator - local time */ + long tkt_age; /* Age of ticket */ + int swap_bytes; /* Need to swap bytes? */ + int mutual; /* Mutual authentication requested? */ + unsigned char s_kvno; /* Version number of the server's key + * Kerberos used to encrypt ticket */ + int status; + + if (authent->length <= 0) + return(RD_AP_MODIFIED); + + ptr = (char *) authent->dat; + + /* get msg version, type and byte order, and server key version */ + + /* check version */ + if (KRB_PROT_VERSION != (unsigned int) *ptr++) + return(RD_AP_VERSION); + + /* byte order */ + swap_bytes = 0; + if ((*ptr & 1) != HOST_BYTE_ORDER) + swap_bytes++; + + /* check msg type */ + mutual = 0; + switch (*ptr++ & ~1) { + case AUTH_MSG_APPL_REQUEST: + break; + case AUTH_MSG_APPL_REQUEST_MUTUAL: + mutual++; + break; + default: + return(RD_AP_MSG_TYPE); + } + +#ifdef lint + /* XXX mutual is set but not used; why??? */ + /* this is a crock to get lint to shut up */ + if (mutual) + mutual = 0; +#endif /* lint */ + s_kvno = *ptr++; /* get server key version */ + (void) strcpy(realm,ptr); /* And the realm of the issuing KDC */ + ptr += strlen(ptr) + 1; /* skip the realm "hint" */ + + /* + * If "fn" is NULL, key info should already be set; don't + * bother with ticket file. Otherwise, check to see if we + * already have key info for the given server and key version + * (saved in the static st_* variables). If not, go get it + * from the ticket file. If "fn" is the null string, use the + * default ticket file. + */ + if (fn && (strcmp(st_nam,service) || strcmp(st_inst,instance) || + strcmp(st_rlm,realm) || (st_kvno != s_kvno))) { + if (*fn == 0) fn = KEYFILE; + st_kvno = s_kvno; +#ifndef NOENCRYPTION + if (read_service_key(service,instance,realm,(int) s_kvno, + fn,(char *)skey)) + return(RD_AP_UNDEC); + if (status = krb_set_key((char *)skey,0)) + return(status); +#endif /* !NOENCRYPTION */ + (void) strcpy(st_rlm,realm); + (void) strcpy(st_nam,service); + (void) strcpy(st_inst,instance); + } + + /* Get ticket from authenticator */ + tkt->length = (int) *ptr++; + if ((tkt->length + (ptr+1 - (char *) authent->dat)) > authent->length) + return(RD_AP_MODIFIED); + bcopy(ptr+1,(char *)(tkt->dat),tkt->length); + + if (krb_ap_req_debug) + log("ticket->length: %d",tkt->length); + +#ifndef NOENCRYPTION + /* Decrypt and take apart ticket */ +#endif + + if (decomp_ticket(tkt,&ad->k_flags,ad->pname,ad->pinst,ad->prealm, + &(ad->address),ad->session, &(ad->life), + &(ad->time_sec),sname,iname,ky,serv_key.s)) + return(RD_AP_UNDEC); + + if (krb_ap_req_debug) { + log("Ticket Contents."); + log(" Aname: %s.%s",ad->pname, + ((int)*(ad->prealm) ? ad->prealm : "Athena")); + log(" Service: %s%s%s",sname,((int)*iname ? "." : ""),iname); + } + + /* Extract the authenticator */ + req_id->length = (int) *(ptr++); + if ((req_id->length + (ptr + tkt->length - (char *) authent->dat)) > + authent->length) + return(RD_AP_MODIFIED); + bcopy(ptr + tkt->length, (char *)(req_id->dat),req_id->length); + +#ifndef NOENCRYPTION + /* And decrypt it with the session key from the ticket */ + if (krb_ap_req_debug) log("About to decrypt authenticator"); + sched = check_key_sched_cache (ad->session); + if (!sched) { + sched = &seskey_sched; + key_sched (ad->session, seskey_sched.s); + add_to_key_sched_cache (ad->session, &seskey_sched); + } + /* can't do much to optimize this... */ + pcbc_encrypt((C_Block *)req_id->dat,(C_Block *)req_id->dat, + (long) req_id->length, sched->s, ad->session,DES_DECRYPT); + if (krb_ap_req_debug) log("Done."); +#endif /* NOENCRYPTION */ + +#define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED); + + ptr = (char *) req_id->dat; + (void) strcpy(r_aname,ptr); /* Authentication name */ + ptr += strlen(r_aname)+1; + check_ptr(); + (void) strcpy(r_inst,ptr); /* Authentication instance */ + ptr += strlen(r_inst)+1; + check_ptr(); + (void) strcpy(r_realm,ptr); /* Authentication name */ + ptr += strlen(r_realm)+1; + check_ptr(); + bcopy(ptr,(char *)&ad->checksum,4); /* Checksum */ + ptr += 4; + check_ptr(); + if (swap_bytes) swap_u_long(ad->checksum); + r_time_ms = *(ptr++); /* Time (fine) */ +#ifdef lint + /* XXX r_time_ms is set but not used. why??? */ + /* this is a crock to get lint to shut up */ + if (r_time_ms) + r_time_ms = 0; +#endif /* lint */ + check_ptr(); + /* assume sizeof(r_time_sec) == 4 ?? */ + bcopy(ptr,(char *)&r_time_sec,4); /* Time (coarse) */ + if (swap_bytes) swap_u_long(r_time_sec); + + /* Check for authenticity of the request */ + if (krb_ap_req_debug) + log("Pname: %s %s",ad->pname,r_aname); + if (strcmp(ad->pname,r_aname) != 0) + return(RD_AP_INCON); + if (strcmp(ad->pinst,r_inst) != 0) + return(RD_AP_INCON); + if (krb_ap_req_debug) + log("Realm: %s %s",ad->prealm,r_realm); + if ((strcmp(ad->prealm,r_realm) != 0)) + return(RD_AP_INCON); + + if (krb_ap_req_debug) + log("Address: %d %d",ad->address,from_addr); + if (from_addr && (ad->address != from_addr)) + return(RD_AP_BADD); + + (void) gettimeofday(&t_local,(struct timezone *) 0); + delta_t = abs((int)(t_local.tv_sec - r_time_sec)); + if (delta_t > CLOCK_SKEW) { + if (krb_ap_req_debug) + log("Time out of range: %d - %d = %d", + t_local.tv_sec,r_time_sec,delta_t); + return(RD_AP_TIME); + } + + /* Now check for expiration of ticket */ + + tkt_age = t_local.tv_sec - ad->time_sec; + if (krb_ap_req_debug) + log("Time: %d Issue Date: %d Diff: %d Life %x", + t_local.tv_sec,ad->time_sec,tkt_age,ad->life); + + if (t_local.tv_sec < ad->time_sec) { + if ((ad->time_sec - t_local.tv_sec) > CLOCK_SKEW) + return(RD_AP_NYV); + } + else if ((t_local.tv_sec - ad->time_sec) > 5 * 60 * ad->life) + return(RD_AP_EXP); + + /* All seems OK */ + ad->reply.length = 0; + + return(RD_AP_OK); +} +#endif /* NOENCRYPTION */ + +static char local_realm_buffer[REALM_SZ+1]; + +krb_get_lrealm(r,n) + char *r; + int n; +{ + FILE *cnffile, *fopen(); + + if (n > 1) + return(KFAILURE); /* Temporary restriction */ + + if (local_realm_buffer[0]) { + strcpy (r, local_realm_buffer); + return KSUCCESS; + } + + if ((cnffile = fopen(KRB_CONF, "r")) == NULL) { + if (n == 1) { + (void) strcpy(r, KRB_REALM); + return(KSUCCESS); + } + else + return(KFAILURE); + } + + if (fscanf(cnffile,"%s",r) != 1) { + (void) fclose(cnffile); + return(KFAILURE); + } + (void) fclose(cnffile); + return(KSUCCESS); +} + +#endif /* KERBEROS */ diff --git a/server/new.h b/server/new.h new file mode 100644 index 0000000..62783c6 --- /dev/null +++ b/server/new.h @@ -0,0 +1,6 @@ +-*- text -*- + +This file is here just to confuse makedepend. Normally, makedepend +will not search the directories used for C++ header files; thus, +anything specific to C++ won't be found unless we fake it out by +putting a file in the working directory for it to find. diff --git a/server/unix.h b/server/unix.h new file mode 100644 index 0000000..b5b09a5 --- /dev/null +++ b/server/unix.h @@ -0,0 +1,113 @@ +/* This file is part of the Project Athena Zephyr Notification System. + * It contains declarations for many standard UNIX library functions, + * and macros for aiding in interfacing to them. + * + * Created by Ken Raeburn. + * + * $Source$ + * $Author$ + * $Id$ + * + * Copyright (c) 1990 by the Massachusetts Institute of Technology. + * For copying and distribution information, see the file + * "mit-copyright.h". + */ + +#include <zephyr/mit-copyright.h> + +#ifndef ZSERVER_UNIX_H__ + +extern "C" { + /* found in libc.a */ +#ifndef __GNUG__ + void *malloc(unsigned), *realloc(void *, unsigned), free (void *); +#endif + long random(void); + + /* + * Queue-handling functions. This structure is basically a dummy; + * as long as the start of another structure looks like this, + * we're okay. + */ + struct qelem { + struct qelem *q_forw; + struct qelem *q_back; + char *q_data; + }; + void insque (qelem*, qelem*); + void remque (qelem *); +#ifdef __GNUG__ +#if defined (ultrix) + void openlog (char *, int); +#undef LOG_DEBUG +#define LOG_DEBUG LOG_ERR +#else + void openlog (char *, int, int); /* ??? */ +#endif +#endif /* G++? */ + void syslog (int, const char *, ...); + int setsockopt (int, int, int, const char *, int); + extern int strcasecmp (const char*, const char*); +#ifdef __GNUG__ + extern void setservent (int); + extern void endservent (void); +#endif + extern void moncontrol (int); + + /* From the Error table library */ + char *error_message(long); + + /* Kerberos */ + extern int krb_get_lrealm (...); + extern int dest_tkt (...); + extern int krb_get_svc_in_tkt (...); + extern int krb_rd_req (...); + extern int krb_mk_req (...); + extern int krb_get_cred (...); + extern int des_quad_cksum (...); + + /* hacked acl code */ + extern void acl_cache_reset (void); +} + +#ifdef vax +#define HAVE_ALLOCA +#endif + +#if defined (__GNUC__) || defined (__GNUG__) + +/* GCC/G++ has a built-in function for allocating automatic storage. */ +#define LOCAL_ALLOC(X) __builtin_alloca(X) +#define LOCAL_FREE(X) + +#else /* not gcc or g++ */ + +#if defined (ibm032) +/* + * Unfortunately, there's no way to get cfront to access _Alloca. So + * we compile with -ma and call alloca. Sigh. + */ +#define LOCAL_ALLOC(X) alloca(X) +#define LOCAL_FREE(X) +extern "C" void * alloca (unsigned int); + +#else /* none of above */ +#ifdef HAVE_ALLOCA +#define LOCAL_ALLOC(X) alloca(X) +#define LOCAL_FREE(X) +#endif +#endif +#endif + +#ifndef LOCAL_ALLOC +#define LOCAL_ALLOC(X) malloc(X) +#define LOCAL_FREE(X) free(X) +#endif + +#define xfree(foo) free((caddr_t) (foo)) +#define xinsque(a,b) insque((struct qelem *)(a), (struct qelem *)(b)) +#define xremque(a) remque((struct qelem *)(a)) +#define xmalloc(a) malloc((unsigned)(a)) + +#define ZSERVER_UNIX_H__ +#endif diff --git a/server/zalloc.c b/server/zalloc.c new file mode 100644 index 0000000..783cde8 --- /dev/null +++ b/server/zalloc.c @@ -0,0 +1,125 @@ +/* + * Memory allocator for Zephyr server. + */ + +#include <stdio.h> +#include "zalloc.h" + +/* + * Pick some size here that will help keep down the number of calls to + * malloc, but doesn't waste too much space. To avoid waste of space, + * we leave some overhead before the next power of two. + */ +const int alloc_size = 16368; + +/* + * What's the maximum number of words to expect to allocate through + * this mechanism? (Larger requests will be fed to malloc.) + */ +const int max_size = 32; + +static void *free_space; +static int free_space_size; +static void *buckets[max_size]; + +const unsigned int sz = sizeof (void *); +inline unsigned int round (unsigned int size) { + size += sz - 1; + size -= (size % sz); + return size; +} +#define ROUND(size) (size = round (size)) +inline int BUCKET (unsigned int size) { + ROUND (size); + return size / sz - 1; +} + +extern "C" { + void * malloc (unsigned int); + void free (void *); + void abort (); + void bzero (void *, unsigned int); +} + +static inline void memset (void *ptr, int size, int fill) { +#ifdef ZALLOC_DEBUG + char *cptr = (char *) ptr; + while (size--) + cptr[size] = fill; +#endif +} + +void *zalloc (unsigned int size) { + ROUND (size); + + int bucket = BUCKET (size); + if (bucket < 0 || bucket >= max_size) + return malloc (size); + + void *ret; + void **ptr = &buckets[bucket]; +#ifdef ZALLOC_DEBUG + fprintf (stderr, "zalloc(%d); looking in bucket %d, found %08X\n", + size, bucket, *ptr); +#endif + if (*ptr) { + ret = *ptr; + *ptr = *(void**)ret; + memset (ret, size, 0xe5); + return ret; + } + + if (free_space_size < size) { + if (free_space_size > 0) { + ptr = &buckets[BUCKET (free_space_size)]; + *(void**)free_space = *ptr; + *ptr = free_space; +#ifdef ZALLOC_DEBUG + fprintf (stderr, "tossing %08X into bucket %d\n", + free_space, bucket); +#endif + } + + free_space_size = (4080 / sizeof (void *)) * sizeof (void *); + free_space = malloc (free_space_size); + if (!free_space) + abort (); +#ifdef ZALLOC_DEBUG + fprintf (stderr, "grabbing more free store at %08X\n", free_space); +#endif + } + + ret = free_space; + free_space = (char *) free_space + size; + free_space_size -= size; +#ifdef ZALLOC_DEBUG + fprintf (stderr, "returning %08X\n", ret); +#endif + memset (ret, size, 0xe5); + return ret; +} + +void zfree (void *ptr, unsigned int size) { + ROUND (size); + + int bucket = BUCKET (size); + if (bucket < 0 || bucket >= max_size) { + free (ptr); + return; + } + + void **b = &buckets[bucket]; + memset (ptr, size, 0xe5); + *(void **) ptr = *b; + *b = ptr; +#ifdef ZALLOC_DEBUG + fprintf (stderr, "putting %08X into bucket %d\n", + ptr, bucket); +#if 0 + fprintf (stderr, "bucket %d:"); + for (ptr = buckets[bucket]; ptr; ptr=*(void**)ptr) + fprintf (stderr, " %X", ptr); + fprintf (stderr, "\n"); +#endif +#endif +} diff --git a/server/zalloc.h b/server/zalloc.h new file mode 100644 index 0000000..3e25f3f --- /dev/null +++ b/server/zalloc.h @@ -0,0 +1,5 @@ +/* + */ + +extern void * zalloc (unsigned int); +extern void zfree (void *, unsigned int); |