#ifndef lint
#ifndef SABER
-static const char rcsid_kstuff_c[] = "$Id: kstuff.c,v 1.27 2004/02/29 06:34:04 zacheiss Exp $";
+static const char rcsid_kstuff_c[] = "$Id$";
#endif
#endif
#ifdef HAVE_KRB4
-/* Keep a hash table mapping tickets to session keys, so we can do a fast
- * check of the cryptographic checksum without doing and DES decryptions.
- * Also remember the expiry time of the ticket, so that we can sweep the
- * table periodically. */
-
-#define HASHTAB_SIZE 4091
-
-typedef struct hash_entry Hash_entry;
-
-/* The ticket comes at the end, in a variable-length array. */
-struct hash_entry {
- C_Block session_key;
- time_t expires;
- char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
- Hash_entry *next;
- int ticket_len;
- unsigned char ticket[1];
-};
-
-Hash_entry *hashtab[HASHTAB_SIZE];
-
-static int hash_ticket __P((unsigned char *, int));
-static void add_session_key __P((KTEXT, C_Block, char *, time_t));
-static int find_session_key __P((KTEXT, C_Block, char *));
-static ZChecksum_t compute_checksum __P((ZNotice_t *, C_Block));
-static ZChecksum_t compute_rlm_checksum __P((ZNotice_t *, C_Block));
+static ZChecksum_t compute_checksum(ZNotice_t *, C_Block);
+static ZChecksum_t compute_rlm_checksum(ZNotice_t *, C_Block);
+static Code_t ZCheckAuthentication4(ZNotice_t *notice, struct sockaddr_in *from);
/*
* GetKerberosData
* the value of rd_ap_req() applied to the ticket.
*/
int
-GetKerberosData(fd, haddr, kdata, service, srvtab)
- int fd; /* file descr. to read from */
- struct in_addr haddr; /* address of foreign host on fd */
- AUTH_DAT *kdata; /* kerberos data (returned) */
- char *service; /* service principal desired */
- char *srvtab; /* file to get keys from */
+GetKerberosData(int fd, /* file descr. to read from */
+ struct in_addr haddr, /* address of foreign host on fd */
+ AUTH_DAT *kdata, /* kerberos data (returned) */
+ char *service, /* service principal desired */
+ char *srvtab) /* file to get keys from */
{
char p[20];
KTEXT_ST ticket; /* will get Kerberos ticket from client */
* get the ticket and write it to the file descriptor
*/
+#if !defined(krb_err_base) && defined(ERROR_TABLE_BASE_krb)
+#define krb_err_base ERROR_TABLE_BASE_krb
+#endif
+
Code_t
-SendKerberosData(fd, ticket, service, host)
- int fd; /* file descriptor to write onto */
- KTEXT ticket; /* where to put ticket (return) */
- char *service; /* service name, foreign host */
- char *host;
+SendKerberosData(int fd, /* file descriptor to write onto */
+ KTEXT ticket, /* where to put ticket (return) */
+ char *service, /* service name, foreign host */
+ char *host)
+
{
int rem;
char p[32];
#endif /* HAVE_KRB4 */
+#if defined(HAVE_KRB5) || defined(HAVE_KRB4)
+Code_t
+ReadKerberosData(int fd, int *size, char **data, int *proto) {
+ char p[20];
+ int i;
+ char *dst;
+ int len = 0;
+
+ for (i=0; i<20; i++) {
+ if (read(fd, &p[i], 1) != 1) {
+ p[i] = 0;
+ syslog(LOG_WARNING,"ReadKerberosData: bad read reply len @%d (got \"%s\"", i, p);
+ return(KFAILURE);
+ }
+ if (p[i] == ' ') {
+ p[i] = '\0';
+ break;
+ }
+ }
+
+ if (i == 20) {
+ syslog(LOG_WARNING, "ReadKerberosData: read reply len exceeds buffer");
+ return KFAILURE;
+ }
+
+ if (!strncmp(p, "V5-", 3) && (len = atoi(p+3)) > 0)
+ *proto = 5;
+ else if ((len = atoi(p)) > 0)
+ *proto = 4;
+
+ if ((*proto < 4) | (*proto > 5)) {
+ syslog(LOG_WARNING, "ReadKerberosData: error parsing authenticator length (\"%s\")", p);
+ return KFAILURE;
+ }
+
+ if (len <= 0) {
+ syslog(LOG_WARNING, "ReadKerberosData: read reply len = %d", len);
+ return KFAILURE;
+ }
+
+ *data = malloc(len);
+ if (! *data) {
+ syslog(LOG_WARNING, "ReadKerberosData: failure allocating %d bytes: %m", len);
+ return errno;
+ }
+
+ dst=*data;
+ for (i=0; i < len; i++) {
+ if (read(fd, dst++, 1) != 1) {
+ free(*data);
+ *data = NULL;
+ *size = 0;
+ syslog(LOG_WARNING,"ReadKerberosData: bad read reply string");
+ return ZSRV_PKSHORT;
+ }
+ }
+ *size = len;
+ return 0;
+}
+#endif
+
+#ifdef HAVE_KRB5
+Code_t
+GetKrb5Data(int fd, krb5_data *data) {
+ char p[20];
+ int i;
+ char *dst;
+
+ for (i=0; i<20; i++) {
+ if (read(fd, &p[i], 1) != 1) {
+ p[i] = 0;
+ syslog(LOG_WARNING,"bad read reply len @%d (got \"%s\")", i, p);
+ return(KFAILURE);
+ }
+ if (p[i] == ' ') {
+ p[i] = '\0';
+ break;
+ }
+ }
+ if (i == 20 || strncmp(p, "V5-", 3) || !atoi(p+3)) {
+ syslog(LOG_WARNING,"bad reply len");
+ return ZSRV_PKSHORT;
+ }
+ data->length = atoi(p+3);
+ data->data = malloc(data->length);
+ if (! data->data) {
+ data->length = 0;
+ return errno;
+ }
+ dst=data->data;
+ for (i=0; i < data->length; i++) {
+ if (read(fd, dst++, 1) != 1) {
+ free(data->data);
+ memset((char *)data, 0, sizeof(krb5_data));
+ syslog(LOG_WARNING,"bad read reply string");
+ return ZSRV_PKSHORT;
+ }
+ }
+ return 0;
+}
+
+Code_t
+SendKrb5Data(int fd, krb5_data *data) {
+ char p[32];
+ int written, size_to_write;
+ sprintf(p, "V5-%d ", data->length);
+ size_to_write = strlen (p);
+ if (size_to_write != (written = write(fd, p, size_to_write)) ||
+ data->length != (written = write(fd, data->data, data->length))) {
+ return (written < 0) ? errno : ZSRV_PKSHORT;
+ }
+ return 0;
+}
+#endif
+
Code_t
-ZCheckRealmAuthentication(notice, from, realm)
- ZNotice_t *notice;
- struct sockaddr_in *from;
- char *realm;
+ZCheckRealmAuthentication(ZNotice_t *notice,
+ struct sockaddr_in *from,
+ char *realm)
{
-#ifdef HAVE_KRB4
- int result;
- char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
- char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
- KTEXT_ST authent, ticket;
- AUTH_DAT dat;
- ZChecksum_t checksum;
- CREDENTIALS cred;
- C_Block session_key;
+#ifdef HAVE_KRB5
+ char *authbuf;
+ char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4+1024];
+ krb5_principal princ;
+ krb5_data packet;
+ krb5_ticket *tkt;
+ char *name;
+ krb5_error_code result;
+ krb5_principal server;
+ krb5_keytab keytabid = 0;
+ krb5_auth_context authctx;
+ krb5_keyblock *keyblock;
+ krb5_enctype enctype;
+ krb5_cksumtype cksumtype;
+ krb5_data cksumbuf;
+ int valid;
+ char *cksum0_base, *cksum1_base, *cksum2_base;
+ char *x;
+ unsigned char *asn1_data;
+ unsigned char *key_data;
+ int asn1_len, key_len, cksum0_len, cksum1_len, cksum2_len;
+#ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
+ krb5_authenticator *authenticator;
+#define KRB5AUTHENT authenticator
+#else
+ krb5_authenticator authenticator;
+#define KRB5AUTHENT &authenticator
+#endif
+ int len;
if (!notice->z_auth)
return ZAUTH_NO;
if (notice->z_authent_len <= 0)
return ZAUTH_FAILED;
+ len = strlen(notice->z_ascii_authent)+1;
+ authbuf = malloc(len);
+
/* Read in the authentication data. */
- if (ZReadAscii(notice->z_ascii_authent,
- strlen(notice->z_ascii_authent)+1,
- (unsigned char *)authent.dat,
- notice->z_authent_len) == ZERR_BADFIELD) {
+ if (ZReadZcode((unsigned char *)notice->z_ascii_authent,
+ (unsigned char *)authbuf,
+ len, &len) == ZERR_BADFIELD) {
return ZAUTH_FAILED;
}
- authent.length = notice->z_authent_len;
- (void) sprintf(rlmprincipal, "%s.%s@%s", SERVER_SERVICE,
+ (void) sprintf(rlmprincipal, "%s/%s@%s", SERVER_SERVICE,
SERVER_INSTANCE, realm);
- result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
- from->sin_addr.s_addr, &dat, srvtab_file);
- if (result == RD_AP_OK) {
- sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
- dat.pinst, dat.prealm);
- if (strcmp(rlmprincipal, srcprincipal))
- return ZAUTH_FAILED;
- } else {
- return ZAUTH_FAILED; /* didn't decode correctly */
+ packet.length = len;
+ packet.data = authbuf;
+
+ result = krb5_kt_resolve(Z_krb5_ctx,
+ keytab_file, &keytabid);
+ if (result) {
+ free(authbuf);
+ return (result);
}
- /* Check the cryptographic checksum. */
-#ifdef NOENCRYPTION
- checksum = 0;
+ /* HOLDING: authbuf, keytabid */
+ /* Create the auth context */
+ result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
+ if (result) {
+ krb5_kt_close(Z_krb5_ctx, keytabid);
+ free(authbuf);
+ return (result);
+ }
+
+ /* HOLDING: authbuf, authctx */
+ result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm),
+ __Zephyr_realm, SERVER_SERVICE,
+ SERVER_INSTANCE, NULL);
+ if (!result) {
+ result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server,
+ keytabid, 0, &tkt);
+ krb5_free_principal(Z_krb5_ctx, server);
+ }
+ krb5_kt_close(Z_krb5_ctx, keytabid);
+
+ if (result) {
+ if (result == KRB5KRB_AP_ERR_REPEAT)
+ syslog(LOG_DEBUG, "ZCheckRealmAuthentication: k5 auth failed: %s", error_message(result));
+ else
+ syslog(LOG_WARNING,"ZCheckRealmAuthentication: k5 auth failed: %s", error_message(result));
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authbuf, authctx, tkt */
+
+ if (tkt == 0 || !Z_tktprincp(tkt)) {
+ if (tkt)
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+
+ princ = Z_tktprinc(tkt);
+
+ if (princ == 0) {
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authbuf, authctx, tkt */
+ result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
+ if (result) {
+ syslog(LOG_WARNING, "k5 unparse_name failed: %s",
+ error_message(result));
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ return ZAUTH_FAILED;
+ }
+
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+
+ /* HOLDING: authbuf, authctx, name */
+ if (strcmp(name, rlmprincipal)) {
+ syslog(LOG_WARNING, "k5 name mismatch: '%s' vs '%s'",
+ name, rlmprincipal);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ free(name);
+ free(authbuf);
+ return ZAUTH_FAILED;
+ }
+ free(name);
+ free(authbuf);
+
+ /* HOLDING: authctx */
+ /* Get an authenticator so we can get the keyblock */
+ result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
+ &authenticator);
+ if(result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return result;
+ }
+
+ /* HOLDING: authctx, authenticator */
+ result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
+ if (result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+
+ /* HOLDING: authctx, authenticator, keyblock */
+ /* Figure out what checksum type to use */
+ key_data = Z_keydata(keyblock);
+ key_len = Z_keylen(keyblock);
+ result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
+ if (result) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+ /* HOLDING: authctx, authenticator, keyblock */
+
+ /* Assemble the things to be checksummed */
+ /* first part is from start of packet through z_default_format:
+ * - z_version
+ * - z_num_other_fields
+ * - z_kind
+ * - z_uid
+ * - z_port
+ * - z_auth
+ * - z_authent_len
+ * - z_ascii_authent
+ * - z_class
+ * - z_class_inst
+ * - z_opcode
+ * - z_sender
+ * - z_recipient
+ * - z_default_format
+ */
+ cksum0_base = notice->z_packet;
+ x = notice->z_default_format;
+ cksum0_len = x + strlen(x) + 1 - cksum0_base;
+ /* second part is from z_multinotice through other fields:
+ * - z_multinotice
+ * - z_multiuid
+ * - z_other_fields[]
+ */
+ cksum1_base = notice->z_multinotice;
+ if (notice->z_num_other_fields)
+ x = notice->z_other_fields[notice->z_num_other_fields];
+ else
+ x = cksum1_base + strlen(cksum1_base) + 1; /* multiuid */
+ cksum1_len = x + strlen(x) + 1 - cksum1_base;
+
+ /* last part is the message body */
+ cksum2_base = notice->z_message;
+ cksum2_len = notice->z_message_len;
+
+ if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') &&
+ key_len == 8 &&
+ (enctype == ENCTYPE_DES_CBC_CRC ||
+ enctype == ENCTYPE_DES_CBC_MD4 ||
+ enctype == ENCTYPE_DES_CBC_MD5)) {
+ /* try old-format checksum (covers cksum0 only) */
+
+ ZChecksum_t our_checksum;
+
+ our_checksum = compute_rlm_checksum(notice, key_data);
+
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+
+ if (our_checksum == notice->z_checksum) {
+ return ZAUTH_YES;
+ } else
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authctx, authenticator */
+
+ cksumbuf.length = cksum0_len + cksum1_len + cksum2_len;
+ cksumbuf.data = malloc(cksumbuf.length);
+ if (!cksumbuf.data) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: authctx, authenticator, cksumbuf.data */
+
+ memcpy(cksumbuf.data, cksum0_base, cksum0_len);
+ memcpy(cksumbuf.data + cksum0_len, cksum1_base, cksum1_len);
+ memcpy(cksumbuf.data + cksum0_len + cksum1_len,
+ cksum2_base, cksum2_len);
+
+ /* decode zcoded checksum */
+ /* The encoded form is always longer than the original */
+ asn1_len = strlen(notice->z_ascii_checksum) + 1;
+ asn1_data = malloc(asn1_len);
+ if (!asn1_data) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(cksumbuf.data);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
+ result = ZReadZcode((unsigned char *)notice->z_ascii_checksum,
+ asn1_data, asn1_len, &asn1_len);
+ if (result != ZERR_NONE) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(asn1_data);
+ free(cksumbuf.data);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: asn1_data, cksumbuf.data */
+
+ valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, asn1_data, asn1_len);
+
+ free(asn1_data);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ free(cksumbuf.data);
+
+ if (valid)
+ return (ZAUTH_YES);
+ else
+ return (ZAUTH_FAILED);
+#else
+ return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
+#endif
+}
+
+Code_t
+ZCheckAuthentication(ZNotice_t *notice,
+ struct sockaddr_in *from)
+{
+#ifdef HAVE_KRB5
+ unsigned char *authbuf;
+ krb5_principal princ;
+ krb5_data packet;
+ krb5_ticket *tkt;
+ char *name;
+ krb5_error_code result;
+ krb5_principal server;
+ krb5_keytab keytabid = 0;
+ krb5_auth_context authctx;
+ krb5_keyblock *keyblock;
+ krb5_enctype enctype;
+ krb5_cksumtype cksumtype;
+ krb5_data cksumbuf;
+ int valid;
+ char *cksum0_base, *cksum1_base, *cksum2_base;
+ char *x;
+ unsigned char *asn1_data, *key_data;
+ int asn1_len, key_len, cksum0_len, cksum1_len, cksum2_len;
+#ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
+ krb5_authenticator *authenticator;
+#define KRB5AUTHENT authenticator
#else
- checksum = compute_rlm_checksum(notice, dat.session);
+ krb5_authenticator authenticator;
+#define KRB5AUTHENT &authenticator
#endif
- if (checksum != notice->z_checksum) {
-#ifndef NOENCRYPTION
- checksum = compute_checksum(notice, dat.session);
- if (checksum != notice->z_checksum)
+ int len;
+
+ if (!notice->z_auth)
+ return ZAUTH_NO;
+
+ /* Check for bogus authentication data length. */
+ if (notice->z_authent_len <= 1)
+ return ZAUTH_FAILED;
+
+#ifdef HAVE_KRB4
+ if (notice->z_ascii_authent[0] != 'Z')
+ return ZCheckAuthentication4(notice, from);
#endif
+
+ len = strlen(notice->z_ascii_authent)+1;
+ authbuf = malloc(len);
+
+ /* Read in the authentication data. */
+ if (ZReadZcode((unsigned char *)notice->z_ascii_authent,
+ authbuf,
+ len, &len) == ZERR_BADFIELD) {
return ZAUTH_FAILED;
}
- return ZAUTH_YES;
+ packet.length = len;
+ packet.data = (char *)authbuf;
-#else /* !HAVE_KRB4 */
+ result = krb5_kt_resolve(Z_krb5_ctx,
+ keytab_file, &keytabid);
+ if (result) {
+ free(authbuf);
+ return (result);
+ }
+
+ /* HOLDING: authbuf, keytabid */
+ /* Create the auth context */
+ result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
+ if (result) {
+ krb5_kt_close(Z_krb5_ctx, keytabid);
+ free(authbuf);
+ return (result);
+ }
+
+ /* HOLDING: authbuf, authctx */
+ result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm),
+ __Zephyr_realm, SERVER_SERVICE,
+ SERVER_INSTANCE, NULL);
+ if (!result) {
+ result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server,
+ keytabid, 0, &tkt);
+ krb5_free_principal(Z_krb5_ctx, server);
+ }
+ krb5_kt_close(Z_krb5_ctx, keytabid);
+
+ if (result) {
+ if (result == KRB5KRB_AP_ERR_REPEAT)
+ syslog(LOG_DEBUG, "ZCheckAuthentication: k5 auth failed: %s", error_message(result));
+ else
+ syslog(LOG_WARNING,"ZCheckAuthentication: k5 auth failed: %s", error_message(result));
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authbuf, authctx, tkt */
+
+ if (tkt == 0 || !Z_tktprincp(tkt)) {
+ if (tkt)
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+ princ = Z_tktprinc(tkt);
+
+ if (princ == 0) {
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authbuf, authctx, tkt */
+ result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
+ if (result) {
+ syslog(LOG_WARNING, "k5 unparse_name failed: %s",
+ error_message(result));
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ return ZAUTH_FAILED;
+ }
+
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+
+ /* HOLDING: authbuf, authctx, name */
+ if (strcmp(name, notice->z_sender)) {
+ syslog(LOG_WARNING, "k5 name mismatch: '%s' vs '%s'",
+ name, notice->z_sender);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ free(name);
+ free(authbuf);
+ return ZAUTH_FAILED;
+ }
+ free(name);
+ free(authbuf);
+
+ /* HOLDING: authctx */
+ /* Get an authenticator so we can get the keyblock */
+ result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
+ &authenticator);
+ if(result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return result;
+ }
+
+ /* HOLDING: authctx, authenticator */
+ result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
+ if (result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+
+ /* HOLDING: authctx, authenticator, keyblock */
+ /* Figure out what checksum type to use */
+ key_data = Z_keydata(keyblock);
+ key_len = Z_keylen(keyblock);
+ result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
+ if (result) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+ /* HOLDING: authctx, authenticator, keyblock */
+
+ ZSetSession(keyblock);
+
+ /* Assemble the things to be checksummed */
+ /* first part is from start of packet through z_default_format:
+ * - z_version
+ * - z_num_other_fields
+ * - z_kind
+ * - z_uid
+ * - z_port
+ * - z_auth
+ * - z_authent_len
+ * - z_ascii_authent
+ * - z_class
+ * - z_class_inst
+ * - z_opcode
+ * - z_sender
+ * - z_recipient
+ * - z_default_format
+ */
+ cksum0_base = notice->z_packet;
+ x = notice->z_default_format;
+ cksum0_len = x + strlen(x) + 1 - cksum0_base;
+ /* second part is from z_multinotice through other fields:
+ * - z_multinotice
+ * - z_multiuid
+ * - z_other_fields[]
+ */
+ cksum1_base = notice->z_multinotice;
+ if (notice->z_num_other_fields)
+ x = notice->z_other_fields[notice->z_num_other_fields];
+ else
+ x = cksum1_base + strlen(cksum1_base) + 1; /* multiuid */
+ cksum1_len = x + strlen(x) + 1 - cksum1_base;
+
+ /* last part is the message body */
+ cksum2_base = notice->z_message;
+ cksum2_len = notice->z_message_len;
+
+ if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') &&
+ key_len == 8 &&
+ (enctype == ENCTYPE_DES_CBC_CRC ||
+ enctype == ENCTYPE_DES_CBC_MD4 ||
+ enctype == ENCTYPE_DES_CBC_MD5)) {
+ /* try old-format checksum (covers cksum0 only) */
+
+ ZChecksum_t our_checksum;
+
+ our_checksum = compute_checksum(notice, key_data);
+
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+
+ if (our_checksum == notice->z_checksum)
+ return ZAUTH_YES;
+ else
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authctx, authenticator */
+
+ cksumbuf.length = cksum0_len + cksum1_len + cksum2_len;
+ cksumbuf.data = malloc(cksumbuf.length);
+ if (!cksumbuf.data) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: authctx, authenticator, cksumbuf.data */
+
+ memcpy(cksumbuf.data, cksum0_base, cksum0_len);
+ memcpy(cksumbuf.data + cksum0_len, cksum1_base, cksum1_len);
+ memcpy(cksumbuf.data + cksum0_len + cksum1_len,
+ cksum2_base, cksum2_len);
+
+ /* decode zcoded checksum */
+ /* The encoded form is always longer than the original */
+ asn1_len = strlen(notice->z_ascii_checksum) + 1;
+ asn1_data = malloc(asn1_len);
+ if (!asn1_data) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(cksumbuf.data);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
+ result = ZReadZcode((unsigned char *)notice->z_ascii_checksum,
+ asn1_data, asn1_len, &asn1_len);
+ if (result != ZERR_NONE) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(asn1_data);
+ free(cksumbuf.data);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: asn1_data, cksumbuf.data, authctx, authenticator */
+
+ valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, asn1_data, asn1_len);
+
+ free(asn1_data);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ free(cksumbuf.data);
+
+ if (valid)
+ return (ZAUTH_YES);
+ else
+ return (ZAUTH_FAILED);
+#else
return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
#endif
}
-Code_t
-ZCheckAuthentication(notice, from)
- ZNotice_t *notice;
- struct sockaddr_in *from;
+#undef KRB5AUTHENT
+
+static Code_t
+ZCheckAuthentication4(ZNotice_t *notice,
+ struct sockaddr_in *from)
{
#ifdef HAVE_KRB4
int result;
char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
- KTEXT_ST authent, ticket;
+ KTEXT_ST authent;
AUTH_DAT dat;
ZChecksum_t checksum;
- C_Block session_key;
+ char instance[INST_SZ+1];
if (!notice->z_auth)
return ZAUTH_NO;
}
authent.length = notice->z_authent_len;
- result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ strcpy(instance, SERVER_INSTANCE);
+
+ /* We don't have the session key cached; do it the long way. */
+ result = krb_rd_req(&authent, SERVER_SERVICE, instance,
from->sin_addr.s_addr, &dat, srvtab_file);
if (result == RD_AP_OK) {
- memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
+ ZSetSessionDES(&dat.session);
sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
dat.pinst, dat.prealm);
if (strcmp(srcprincipal, notice->z_sender))
#endif
}
-#ifdef HAVE_KRB4
-
-static int hash_ticket(p, len)
- unsigned char *p;
- int len;
-{
- unsigned long hashval = 0, g;
-
- for (; len > 0; p++, len--) {
- hashval = (hashval << 4) + *p;
- g = hashval & 0xf0000000;
- if (g) {
- hashval ^= g >> 24;
- hashval ^= g;
- }
- }
- return hashval % HASHTAB_SIZE;
-}
-
-static void add_session_key(ticket, session_key, srcprincipal, expires)
- KTEXT ticket;
- C_Block session_key;
- char *srcprincipal;
- time_t expires;
-{
- Hash_entry *entry;
- int hashval;
-
- /* If we can't allocate memory for the hash table entry, just forget
- * about it. */
- entry = (Hash_entry *) malloc(sizeof(Hash_entry) - 1 + ticket->length);
- if (!entry)
- return;
-
- /* Initialize the new entry. */
- memcpy(entry->session_key, session_key, sizeof(entry->session_key));
- strcpy(entry->srcprincipal, srcprincipal);
- entry->expires = expires;
- entry->ticket_len = ticket->length;
- memcpy(entry->ticket, ticket->dat, ticket->length * sizeof(unsigned char));
-
- /* Insert the new entry in the hash table. */
- hashval = hash_ticket(ticket->dat, ticket->length);
- entry->next = hashtab[hashval];
- hashtab[hashval] = entry;
-}
-
-static int find_session_key(ticket, key, srcprincipal)
- KTEXT ticket;
- C_Block key;
- char *srcprincipal;
-{
- unsigned char *dat;
- int hashval, len;
- Hash_entry *entry;
-
- dat = ticket->dat;
- len = ticket->length;
- hashval = hash_ticket(dat, len);
-
- for (entry = hashtab[hashval]; entry; entry = entry->next) {
- if (entry->ticket_len == len && memcmp(entry->ticket, dat, len) == 0) {
- memcpy(key, entry->session_key, sizeof(entry->session_key));
- strcpy(srcprincipal, entry->srcprincipal);
- return 0;
- }
- }
- return -1;
-}
-static ZChecksum_t compute_checksum(notice, session_key)
- ZNotice_t *notice;
- C_Block session_key;
+#ifdef HAVE_KRB4
+static ZChecksum_t
+compute_checksum(ZNotice_t *notice,
+ C_Block session_key)
{
#ifdef NOENCRYPTION
return 0;
cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
cend = cstart + strlen(cstart) + 1;
- checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
- checksum ^= des_quad_cksum(cend, NULL, hend - cend, 0, session_key);
- checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
+ checksum = des_quad_cksum((unsigned char *)hstart, NULL, cstart - hstart, 0, session_key);
+ checksum ^= des_quad_cksum((unsigned char *)cend, NULL, hend - cend, 0, session_key);
+ checksum ^= des_quad_cksum((unsigned char *)notice->z_message, NULL, notice->z_message_len,
0, session_key);
return checksum;
#endif
}
-static ZChecksum_t compute_rlm_checksum(notice, session_key)
- ZNotice_t *notice;
- C_Block session_key;
+static ZChecksum_t compute_rlm_checksum(ZNotice_t *notice,
+ C_Block session_key)
{
#ifdef NOENCRYPTION
return 0;
#else
ZChecksum_t checksum;
- char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
+ char *cstart, *cend, *hstart = notice->z_packet;
cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
cend = cstart + strlen(cstart) + 1;
- checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
+ checksum = des_quad_cksum((unsigned char *)hstart, NULL, cstart - hstart, 0, session_key);
return checksum;
#endif
}
-void sweep_ticket_hash_table(arg)
- void *arg;
+#ifdef HAVE_KRB5
+krb5_error_code
+Z_krb5_init_keyblock(krb5_context context,
+ krb5_enctype type,
+ size_t size,
+ krb5_keyblock **key)
{
- int i;
- Hash_entry **ptr, *entry;
-
- for (i = 0; i < HASHTAB_SIZE; i++) {
- ptr = &hashtab[i];
- while (*ptr) {
- entry = *ptr;
- if (entry->expires < NOW) {
- *ptr = entry->next;
- free(entry);
- } else {
- ptr = &(*ptr)->next;
- }
- }
+#ifdef HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
+ return krb5_init_keyblock(context, type, size, key);
+#else
+ krb5_error_code ret;
+ krb5_keyblock *tmp, tmp_ss;
+ tmp = &tmp_ss;
+
+ *key = NULL;
+ Z_enctype(tmp) = type;
+ Z_keylen(tmp) = size;
+ Z_keydata(tmp) = malloc(size);
+ if (!Z_keydata(tmp))
+ return ENOMEM;
+ ret = krb5_copy_keyblock(context, tmp, key);
+ free(Z_keydata(tmp));
+ return ret;
+#endif
+}
+
+#if 0
+void ZLogKey(char *label, krb5_keyblock *keyblock) {
+ char *p, *buf;
+ unsigned char *k;
+ int i;
+ buf = malloc(5 *Z_keylen(keyblock)+1);
+
+ k=Z_keydata(keyblock);
+ for (i=0,p=buf; i < Z_keylen(keyblock); i++,p+=strlen(p))
+ sprintf(p, " 0x%02x", k[i]);
+ syslog(LOG_ERR, "key %s is type %d, %d bytes, %s", label, Z_enctype(keyblock), Z_keylen(keyblock), buf);
+ free(buf);
+}
+#endif
+
+void
+ZSetSession(krb5_keyblock *keyblock) {
+#if 1
+ krb5_error_code result;
+
+ if (__Zephyr_keyblock) {
+ krb5_free_keyblock_contents(Z_krb5_ctx, __Zephyr_keyblock);
+ result = krb5_copy_keyblock_contents(Z_krb5_ctx, keyblock, __Zephyr_keyblock);
+ } else {
+ result = krb5_copy_keyblock(Z_krb5_ctx, keyblock, &__Zephyr_keyblock);
}
- timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
+
+ if (result) /*XXX we're out of memory? */
+ ;
+#else
+ memcpy(__Zephyr_session, Z_keydata(keyblock), sizeof(C_Block));
+#endif
}
+#endif
+#ifdef HAVE_KRB4
+void
+ZSetSessionDES(C_Block *key) {
+#ifdef HAVE_KRB5
+ Code_t result;
+ if (__Zephyr_keyblock) {
+ krb5_free_keyblock(Z_krb5_ctx, __Zephyr_keyblock);
+ __Zephyr_keyblock=NULL;
+ }
+ result = Z_krb5_init_keyblock(Z_krb5_ctx, ENCTYPE_DES_CBC_CRC,
+ sizeof(C_Block),
+ &__Zephyr_keyblock);
+ if (result) /*XXX we're out of memory? */
+ return;
+
+ memcpy(Z_keydata(__Zephyr_keyblock), key, sizeof(C_Block));
+#else
+ memcpy(__Zephyr_session, key, sizeof(C_Block));
+#endif
+}
+#endif
#endif /* HAVE_KRB4 */