summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--h/internal.h10
-rw-r--r--lib/ZCkZAut.c110
2 files changed, 83 insertions, 37 deletions
diff --git a/h/internal.h b/h/internal.h
index 54c595c..b6e6804 100644
--- a/h/internal.h
+++ b/h/internal.h
@@ -93,6 +93,16 @@ struct _Z_SessionKey {
};
extern struct _Z_SessionKey *Z_keys_head, *Z_keys_tail;
+
+/*
+ * The maximum time we allow for a notice to get delivered. This is used for
+ * two timeouts in key expirey. First, we assume that any subscription notice
+ * was reached the server within that time; this allows us to assume old keys
+ * sent sufficiently long before a newer, verified key are stale. Second, we
+ * assume notices authenticated with an old key reach us in that time; this
+ * allows us to prune stale keys after a timeout.
+*/
+#define KEY_TIMEOUT 60
#endif
extern ZLocations_t *__locate_list;
diff --git a/lib/ZCkZAut.c b/lib/ZCkZAut.c
index 4bc3e2a..84e7959 100644
--- a/lib/ZCkZAut.c
+++ b/lib/ZCkZAut.c
@@ -31,13 +31,13 @@ static const char rcsid_ZCheckAuthentication_c[] =
When not using Kerberos, return true if the notice claims to be authentic.
Only used by clients; the server uses its own routine.
*/
-Code_t ZCheckZcodeAuthentication(ZNotice_t *notice,
- struct sockaddr_in *from)
-{
#ifdef HAVE_KRB5
+static Code_t Z_CheckZcodeAuthentication(ZNotice_t *notice,
+ struct sockaddr_in *from,
+ krb5_keyblock *keyblock)
+{
krb5_error_code result;
- krb5_creds *creds;
- krb5_keyblock *keyblock;
+ krb5_creds *creds = NULL;
krb5_enctype enctype;
krb5_cksumtype cksumtype;
krb5_data cksumbuf;
@@ -46,36 +46,14 @@ Code_t ZCheckZcodeAuthentication(ZNotice_t *notice,
char *x;
unsigned char *asn1_data, *key_data, *cksum_data;
int asn1_len, key_len, cksum0_len = 0, cksum1_len = 0, cksum2_len = 0;
-#endif
-
- /* If the value is already known, return it. */
- if (notice->z_checked_auth != ZAUTH_UNSET)
- return (notice->z_checked_auth);
-
- if (!notice->z_auth)
- return (ZAUTH_NO);
-
- if (!notice->z_ascii_checksum)
- return (ZAUTH_NO);
-
-
-#ifdef HAVE_KRB5
- result = ZGetCreds(&creds);
-
- if (result)
- return (ZAUTH_NO);
- /* HOLDING: creds */
/* Figure out what checksum type to use */
- keyblock = Z_credskey(creds);
key_data = Z_keydata(keyblock);
key_len = Z_keylen(keyblock);
result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
if (result) {
- krb5_free_creds(Z_krb5_ctx, creds);
return (ZAUTH_FAILED);
}
- /* HOLDING: creds */
/* Assemble the things to be checksummed */
/* first part is from start of packet through z_default_format:
@@ -139,19 +117,16 @@ Code_t ZCheckZcodeAuthentication(ZNotice_t *notice,
our_checksum = z_quad_cksum((unsigned char *)cksum0_base, NULL, cksum0_len, 0,
key_data);
if (our_checksum == notice->z_checksum) {
- krb5_free_creds(Z_krb5_ctx, creds);
return ZAUTH_YES;
}
}
- /* HOLDING: creds */
cksumbuf.length = cksum0_len + cksum1_len + cksum2_len;
cksumbuf.data = malloc(cksumbuf.length);
if (!cksumbuf.data) {
- krb5_free_creds(Z_krb5_ctx, creds);
return ZAUTH_NO;
}
- /* HOLDING: creds, cksumbuf.data */
+ /* HOLDING: cksumbuf.data */
cksum_data = (unsigned char *)cksumbuf.data;
memcpy(cksum_data, cksum0_base, cksum0_len);
@@ -165,33 +140,94 @@ Code_t ZCheckZcodeAuthentication(ZNotice_t *notice,
asn1_len = strlen(notice->z_ascii_checksum) + 1;
asn1_data = malloc(asn1_len);
if (!asn1_data) {
- krb5_free_creds(Z_krb5_ctx, creds);
free(cksumbuf.data);
return ZAUTH_FAILED;
}
- /* HOLDING: creds, asn1_data, cksumbuf.data */
+ /* HOLDING: asn1_data, cksumbuf.data */
result = ZReadZcode((unsigned char *)notice->z_ascii_checksum,
asn1_data, asn1_len, &asn1_len);
if (result != ZERR_NONE) {
- krb5_free_creds(Z_krb5_ctx, creds);
free(asn1_data);
free(cksumbuf.data);
return ZAUTH_FAILED;
}
- /* HOLDING: creds, asn1_data, cksumbuf.data */
+ /* HOLDING: asn1_data, cksumbuf.data */
valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype,
Z_KEYUSAGE_SRV_CKSUM, asn1_data, asn1_len);
free(asn1_data);
- krb5_free_creds(Z_krb5_ctx, creds);
free(cksumbuf.data);
if (valid)
return ZAUTH_YES;
else
return ZAUTH_FAILED;
-#else /* HAVE_KRB5 */
+}
+#endif
+
+Code_t ZCheckZcodeAuthentication(ZNotice_t *notice,
+ struct sockaddr_in *from)
+{
+#ifdef HAVE_KRB5
+ Code_t answer;
+ krb5_creds *creds;
+ struct _Z_SessionKey *savedkey, *todelete;
+#endif
+
+ /* If the value is already known, return it. */
+ if (notice->z_checked_auth != ZAUTH_UNSET)
+ return (notice->z_checked_auth);
+
+ if (!notice->z_auth)
+ return (ZAUTH_NO);
+
+ if (!notice->z_ascii_checksum)
+ return (ZAUTH_NO);
+
+#ifdef HAVE_KRB5
+ /* Try each of the saved session keys. */
+ for (savedkey = Z_keys_head; savedkey != NULL; savedkey = savedkey->next) {
+ answer = Z_CheckZcodeAuthentication(notice, from, savedkey->keyblock);
+ if (answer == ZAUTH_YES) {
+ /* Save the time of the first use of each key. */
+ if (!savedkey->first_use) {
+ savedkey->first_use = time(NULL);
+ } else {
+ /*
+ * Any keys sent sufficiently long before this one is stale. If
+ * we know it has been long enough since the server learned of
+ * this key, we can prune keys made stale by this one.
+ */
+ if (time(NULL) > savedkey->first_use + KEY_TIMEOUT) {
+ while (Z_keys_tail &&
+ Z_keys_tail->send_time + KEY_TIMEOUT < savedkey->send_time) {
+ todelete = Z_keys_tail;
+ Z_keys_tail = Z_keys_tail->prev;
+ Z_keys_tail->next = NULL;
+
+ krb5_free_keyblock(Z_krb5_ctx, todelete->keyblock);
+ free(todelete);
+ }
+ }
+ }
+ return answer;
+ }
+ }
+
+ /*
+ * If each of those fails, pull from the ccache. This is to preserve the
+ * behavior of things like zwgc/zctl where another program actually
+ * generates the subscription notices.
+ */
+ if (ZGetCreds(&creds))
+ return ZAUTH_NO;
+
+ answer = Z_CheckZcodeAuthentication(notice, from, Z_credskey(creds));
+
+ krb5_free_creds(Z_krb5_ctx, creds);
+ return answer;
+#else
return (notice->z_auth ? ZAUTH_YES : ZAUTH_NO);
#endif
}