diff options
Diffstat (limited to 'src/sessions.c')
-rw-r--r-- | src/sessions.c | 221 |
1 files changed, 192 insertions, 29 deletions
diff --git a/src/sessions.c b/src/sessions.c index e3a8785..b09a181 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -41,6 +41,188 @@ static apr_status_t mag_session_set(request_rec *req, session_rec *sess, return DECLINED; } + +/* Format, simple 32bit word based scheme: + * the first byte is the data identifier, + * the next three bytes are the length in big endian for strings, + * or the value for small integers and boolean data. + * + * identifiers: + * + * 0xFF reserved + * 0x00 reserved + * 0x01 established (boolean) + * 0x10 user name (string) + * 0x11 gss name (string) + * 0x20 expiration (large integer) + * */ +#define EST 0x01 +#define USR 0x10 +#define GSS 0x11 +#define AUT 0x12 +#define EXP 0x20 + +#define IDMASK 0x00ffffff + +static apr_status_t mag_unpack_string(apr_pool_t *pool, struct databuf *plain, + uint32_t word, size_t *pos, + const char **dest) +{ + char *data; + uint32_t len; + + len = be32toh(word & IDMASK); + if (len > (plain->length - *pos)) return APR_EINVAL; + + data = (char *)&plain->value[*pos]; + *pos += len; + + *dest = apr_pstrndup(pool, data, len); + if (!*dest) return APR_ENOMEM; + + return APR_SUCCESS; +} + +static apr_status_t mag_unpack_session(struct databuf *plain, + struct mag_conn *mc) +{ + apr_status_t ret = APR_SUCCESS; + size_t pos = 0; + uint32_t word; + uint64_t exp; + uint8_t id; + + while (pos < plain->length) { + memcpy(&word, &plain->value[pos], 4); + pos += 4; + id = word >> 24; + switch (id) { + case EST: + mc->established = (be32toh(word & IDMASK) == 1); + break; + case USR: + ret = mag_unpack_string(mc->parent, plain, word, + &pos, &mc->user_name); + if (ret != APR_SUCCESS) goto done; + break; + case GSS: + ret = mag_unpack_string(mc->parent, plain, word, + &pos, &mc->gss_name); + if (ret != APR_SUCCESS) goto done; + break; + case AUT: + ret = mag_unpack_string(mc->parent, plain, word, + &pos, &mc->auth_type); + if (ret != APR_SUCCESS) goto done; + break; + case EXP: + if (((word & IDMASK) != 8) || + ((plain->length - pos) < 8)) { + ret = APR_EINVAL; + goto done; + } + memcpy(&exp, &plain->value[pos], 8); + mc->expiration = be64toh(exp); + pos += 8; + break; + default: + ret = APR_EINVAL; + goto done; + } + } + +done: + if (ret != APR_SUCCESS) { + /* wipe mc to avoid returning partial data */ + memset(mc, 0, sizeof(struct mag_conn)); + } + return ret; +} + +static apr_status_t mag_pack_string(struct databuf *plain, char id, + size_t maxlen, size_t *pos, + const char *src) +{ + uint32_t len = strlen(src); + uint32_t word; + + /* skip if zero length */ + if (len == 0) return APR_SUCCESS; + if (len > IDMASK) return APR_EINVAL; + if (maxlen < (*pos + len + 4)) return APR_EINVAL; + word = id << 24; + word |= (htobe32(len) & IDMASK); + memcpy(&plain->value[*pos], &word, 4); + *pos += 4; + memcpy(&plain->value[*pos], src, len); + *pos += len; + return APR_SUCCESS; +} + +static apr_status_t mag_pack_session(struct mag_conn *mc, + apr_pool_t *pool, + struct databuf *plain) +{ + apr_status_t ret; + size_t maxlen; + uint32_t word; + uint64_t exp; + size_t pos = 0; + + /* 5 fields */ + maxlen = 5 * sizeof(uint32_t); + /* expiration */ + maxlen += sizeof(uint64_t); + /* string values */ + if (mc->user_name) + maxlen += strlen(mc->user_name); + if (mc->gss_name) + maxlen += strlen(mc->gss_name); + if (mc->auth_type) + maxlen += strlen(mc->auth_type); + + plain->value = apr_pcalloc(pool, maxlen); + if (!plain->value) return APR_ENOMEM; + + /* established */ + if (maxlen < (pos + 4)) return APR_EINVAL; + word = EST << 24; + if (mc->established) { + word |= (htobe32(1) & IDMASK); + } + memcpy(&plain->value[pos], &word, 4); + pos += 4; + + /* expiration */ + if (maxlen < (pos + 12)) return APR_EINVAL; + word = EXP << 24; + word |= (htobe32(8) & IDMASK); + memcpy(&plain->value[pos], &word, 4); + pos += 4; + exp = htobe64((uint64_t)mc->expiration); + memcpy(&plain->value[pos], &exp, 8); + pos += 8; + + /* string values */ + if (mc->user_name) { + ret = mag_pack_string(plain, USR, maxlen, &pos, mc->user_name); + if (ret != APR_SUCCESS) return ret; + } + + if (mc->gss_name) { + ret = mag_pack_string(plain, GSS, maxlen, &pos, mc->gss_name); + if (ret != APR_SUCCESS) return ret; + } + + if (mc->auth_type) { + ret = mag_pack_string(plain, AUT, maxlen, &pos, mc->auth_type); + if (ret != APR_SUCCESS) return ret; + } + + plain->length = pos; + return APR_SUCCESS; +} + #define MAG_BEARER_KEY "MagBearerToken" void mag_check_session(request_rec *req, @@ -53,8 +235,6 @@ void mag_check_session(request_rec *req, int declen; struct databuf ctxbuf = { 0 }; struct databuf cipherbuf = { 0 }; - char *next, *last; - time_t expiration; rc = mag_session_load(req, &sess); if (rc != OK || sess == NULL) { @@ -103,34 +283,17 @@ void mag_check_session(request_rec *req, return; } - /* get time */ - next = apr_strtok((char *)ctxbuf.value, ":", &last); - expiration = (time_t)apr_atoi64(next); - if (expiration < time(NULL)) { - /* credentials fully expired, return nothing */ - return; + rc = mag_unpack_session(&ctxbuf, mc); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, + "Failed to parse session data!"); } - - /* user name is next */ - next = apr_strtok(NULL, ":", &last); - mc->user_name = apr_pstrdup(mc->parent, next); - if (!mc->user_name) return; - - /* gssapi name (often a principal) is last. - * (because it may contain the separator as a valid char we - * just read last as is, without further tokenizing */ - mc->gss_name = apr_pstrdup(mc->parent, last); - if (!mc->gss_name) return; - - /* OK we have a valid token */ - mc->established = true; } void mag_attempt_session(request_rec *req, struct mag_config *cfg, struct mag_conn *mc) { session_rec *sess = NULL; - char *sessval = NULL; struct databuf plainbuf = { 0 }; struct databuf cipherbuf = { 0 }; struct databuf ctxbuf = { 0 }; @@ -157,12 +320,12 @@ void mag_attempt_session(request_rec *req, } } - sessval = apr_psprintf(req->pool, "%ld:%s:%s", - (long)mc->expiration, mc->user_name, mc->gss_name); - if (!sessval) return; - - plainbuf.length = strlen(sessval) + 1; - plainbuf.value = (unsigned char *)sessval; + rc = mag_pack_session(mc, req->pool, &plainbuf); + if (rc != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, + "Failed to package session data!"); + return; + } rc = SEAL_BUFFER(req->pool, cfg->mag_skey, &plainbuf, &cipherbuf); if (rc != OK) { |