...
 
Commits (1)
  • ale's avatar
    Add functionality to encrypt tickets exchanged between endpoints · 0e5a990d
    ale authored
    The communication between the SSO server and the /sso_login endpoint
    of the target service (the 't' query argument) can be encrypted using
    the NaCl cryptobox, with a new set of keypairs for the SSO service and
    the target services.
    0e5a990d
This diff is collapsed.
......@@ -25,6 +25,7 @@
#include "ap_config.h"
#include "apr_strings.h"
#include <sso/sso.h>
#include <sso/sso_encrypt.h>
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(sso);
......@@ -44,6 +45,7 @@ struct request_rec;
struct modsso_params {
char *t;
char *et;
char *d;
};
......@@ -63,6 +65,9 @@ void modsso_set_cookie(request_rec *r, const char *cookie_name,
void modsso_del_cookie(request_rec *r, const char *cookie_name);
int modsso_read_fixed_size_file(apr_pool_t *pool, const char *path,
size_t size, const unsigned char **out);
// session.c
int modsso_session_read_key_from_file(apr_pool_t *pool, const char *path,
unsigned char *out, size_t *outsz);
......
......@@ -22,16 +22,18 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include "ap_config.h"
#include "apr_strings.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "apr_strings.h"
#include "http_protocol.h"
#include "http_main.h"
#include "ap_config.h"
#include "http_protocol.h"
#include "mod_sso.h"
#include <ctype.h>
// Ugly implementation of url decoding. Returns a newly allocated
// string (using malloc).
......@@ -40,48 +42,48 @@ char *modsso_url_decode(apr_pool_t *p, const char *i) {
char state = 'N';
char buf[3], obuf[2];
for (o = output; *i; i++) {
switch(state) {
switch (state) {
case 'N':
if (*i == '%') state = 1;
else *o++ = *i;
if (*i == '%')
state = 1;
else
*o++ = *i;
break;
case 1: {
char c = tolower(*i);
buf[0] = *i;
if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) {
state = 2;
} else {
*o++ = '%';
*o++ = *i;
state = 'N';
}
break;
}
case 2: {
char c = tolower(*i);
if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) {
buf[1] = *i;
buf[2] = 0;
obuf[0] = strtol(buf, 0, 16);
obuf[1] = 0;
if (obuf[0])
*o++ = obuf[0];
} else {
*o++ = '%';
*o++ = buf[0];
*o++ = *i;
}
state = 'N';
break;
case 1:
{
char c = tolower(*i);
buf[0] = *i;
if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) {
state = 2;
} else {
*o++ = '%';
*o++ = *i;
state = 'N';
}
break;
}
case 2:
{
char c = tolower(*i);
if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')) {
buf[1] = *i;
buf[2] = 0;
obuf[0] = strtol(buf, 0, 16);
obuf[1] = 0;
if (obuf[0])
*o++ = obuf[0];
} else {
*o++ = '%';
*o++ = buf[0];
*o++ = *i;
}
state = 'N';
break;
}
}
}
}
if (state != 'N') {
*o++ = buf[0];
if (state > 1)
*o++ = buf[1];
*o++ = buf[1];
}
*o = '\0';
return output;
......@@ -105,11 +107,13 @@ char *modsso_url_encode(apr_pool_t *p, const char *s) {
return out;
}
int modsso_parse_query_string(apr_pool_t *p, const char *str, modsso_params_t params) {
int modsso_parse_query_string(apr_pool_t *p, const char *str,
modsso_params_t params) {
char *tmp = apr_pstrdup(p, str), *strptr = NULL;
params->d = NULL;
params->t = NULL;
params->et = NULL;
if (str == NULL) {
return 0;
......@@ -136,31 +140,30 @@ int modsso_parse_query_string(apr_pool_t *p, const char *str, modsso_params_t pa
params->d = value;
} else if (!strcmp(token, "t")) {
params->t = value;
} else if (!strcmp(token, "et")) {
params->et = value;
}
}
if (!params->d || !params->d[0]
|| !params->t || !params->t[0]) {
// Both 'd' and one of 't' or 'et' must be set.
if (!params->d || !params->d[0] ||
((!params->t || !params->t[0]) && (!params->et || !params->et[0]))) {
return -1;
}
return 0;
}
static char *make_cookie_value(request_rec *r,
const char *name,
const char *session_id,
const char *path,
int cookie_lifespan)
{
static char *make_cookie_value(request_rec *r, const char *name,
const char *session_id, const char *path,
int cookie_lifespan) {
size_t sz = 1024;
char *cookie_value = (char *)apr_palloc(r->pool, sz);
if (cookie_lifespan == 0) {
snprintf(cookie_value, sz,
"%s=%s; path=%s; secure; httpOnly;",
name, session_id, path);
snprintf(cookie_value, sz, "%s=%s; path=%s; secure; httpOnly;", name,
session_id, path);
} else {
char expires[200];
time_t t;
......@@ -168,8 +171,7 @@ static char *make_cookie_value(request_rec *r,
t = time(NULL) + cookie_lifespan;
tmp = gmtime(&t);
strftime(expires, sizeof(expires), "%a, %d-%b-%Y %H:%M:%S GMT", tmp);
snprintf(cookie_value, sz,
"%s=%s; expires=%s; path=%s; secure; httpOnly;",
snprintf(cookie_value, sz, "%s=%s; expires=%s; path=%s; secure; httpOnly;",
name, session_id, expires, path);
}
return cookie_value;
......@@ -185,7 +187,7 @@ static char *make_cookie_value(request_rec *r,
char *modsso_get_cookie(request_rec *r, const char *cookie_name) {
char *rv = NULL, *cookies, *cookie, *tokenizerCtx = NULL;
int cookie_name_len = strlen(cookie_name);
const char *cookies_c = apr_table_get(r->headers_in, "Cookie");
if (cookies_c == NULL) {
return NULL;
......@@ -198,7 +200,8 @@ char *modsso_get_cookie(request_rec *r, const char *cookie_name) {
while (cookie) {
while (*cookie == ' ')
cookie++;
if (strncmp(cookie, cookie_name, cookie_name_len) == 0 && cookie[cookie_name_len] == '=') {
if (strncmp(cookie, cookie_name, cookie_name_len) == 0 &&
cookie[cookie_name_len] == '=') {
cookie += (cookie_name_len + 1);
rv = apr_pstrdup(r->pool, cookie);
break;
......@@ -208,20 +211,43 @@ char *modsso_get_cookie(request_rec *r, const char *cookie_name) {
return rv;
}
void modsso_set_cookie(request_rec *r, const char *cookie_name,
const char *value, const char *path)
{
void modsso_set_cookie(request_rec *r, const char *cookie_name,
const char *value, const char *path) {
char *cookie_value;
cookie_value = make_cookie_value(r, cookie_name, value, path, 0);
apr_table_setn(r->err_headers_out, "Set-Cookie",
apr_table_setn(r->err_headers_out, "Set-Cookie",
apr_pstrdup(r->pool, cookie_value));
}
void modsso_del_cookie(request_rec *r, const char *cookie_name)
{
void modsso_del_cookie(request_rec *r, const char *cookie_name) {
char cookie_value[512];
sprintf(cookie_value, "%s=; expires=Mar, 01-01-1971 00:00:00 GMT",
cookie_name);
apr_table_setn(r->err_headers_out, "Set-Cookie",
apr_pstrdup(r->pool, cookie_value));
}
int modsso_read_fixed_size_file(apr_pool_t *pool, const char *path, size_t size,
const unsigned char **out) {
char *m = NULL;
int status;
apr_file_t *file;
apr_size_t n;
if (apr_file_open(&file, path, APR_FOPEN_READ, 0, pool) != APR_SUCCESS)
goto fail;
n = size;
m = apr_palloc(pool, n);
status = apr_file_read(file, m, &n);
apr_file_close(file);
if (status != APR_SUCCESS || n != size)
goto fail;
*out = (unsigned char *)m;
return 0;
fail:
// apr_pfree(pool, m);
return -1;
}
......@@ -8,6 +8,7 @@ ssotool_LDADD = libsso.la
libsso_la_SOURCES = \
sso.c sso.h \
sso_encrypt.c sso_encrypt.h \
base64.c \
randombytes.c \
tweetnacl.c tweetnacl.h
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sso.h"
#include "tweetnacl.h"
extern void randombytes(unsigned char *, unsigned long long);
int sso_generate_encryption_keys(unsigned char *publicp,
unsigned char *secretp) {
return crypto_box_keypair(publicp, secretp);
}
int sso_serialize_ticket_encryption_public_key(const unsigned char *public_key,
char *out, size_t out_sz) {
size_t sz = out_sz;
return sso_base64_encode((unsigned char *)out, &sz, public_key,
crypto_box_PUBLICKEYBYTES);
}
int sso_ticket_encrypt(const char *serialized_ticket,
const char *serialized_pub_key,
const unsigned char *secret_key, char *out,
size_t out_sz) {
unsigned char pub_key[crypto_box_PUBLICKEYBYTES];
unsigned char n[crypto_box_NONCEBYTES];
unsigned char *m = NULL, *c = NULL;
int serialized_ticket_len;
int mlen;
int ret = -1;
size_t sz, enc_sz, nonce_sz, pub_sz;
// Decode the public key (base64-encoded).
pub_sz = crypto_box_PUBLICKEYBYTES;
ret = sso_base64_decode(pub_key, &pub_sz, (unsigned char *)serialized_pub_key,
strlen(serialized_pub_key));
if (ret < 0)
return ret;
// Encrypt the serialized ticket string.
serialized_ticket_len = strlen(serialized_ticket);
mlen = serialized_ticket_len + crypto_box_ZEROBYTES;
c = malloc(mlen);
m = malloc(mlen);
memset(m, 0, crypto_box_ZEROBYTES);
memcpy(m + crypto_box_ZEROBYTES, serialized_ticket, serialized_ticket_len);
randombytes(n, crypto_box_NONCEBYTES);
crypto_box(c, m, mlen, n, pub_key, secret_key);
// Build the output string by concatenating the nonce and the
// encrypted message, each base64-encoded, separated by a dot.
nonce_sz = out_sz;
ret = sso_base64_encode((unsigned char *)out, &nonce_sz, n,
crypto_box_NONCEBYTES);
if (ret < 0)
goto fail;
out[nonce_sz++] = '.';
sz = out_sz - nonce_sz;
enc_sz = mlen - crypto_box_BOXZEROBYTES;
ret = sso_base64_encode((unsigned char *)(out + nonce_sz), &sz,
c + crypto_box_BOXZEROBYTES, enc_sz);
fail:
free(m);
free(c);
return ret;
}
int sso_ticket_decrypt(const char *encrypted, const unsigned char *public_key,
const unsigned char *secret_key, char *out,
size_t out_sz) {
const char *nonce, *enc_msg;
unsigned char n[crypto_box_NONCEBYTES];
unsigned char *c = NULL, *m = NULL;
int nonce_b64len, enc_msg_b64len, clen;
size_t nonce_sz, enc_msg_sz;
int ret;
nonce = encrypted;
enc_msg = strchr(encrypted, '.');
if (!enc_msg)
return -1;
nonce_b64len = enc_msg - nonce;
enc_msg++;
enc_msg_b64len = strlen(enc_msg);
nonce_sz = crypto_box_NONCEBYTES;
ret = sso_base64_decode(n, &nonce_sz, (unsigned char *)nonce, nonce_b64len);
if (ret < 0)
goto fail;
if (nonce_sz != crypto_box_NONCEBYTES) {
ret = -1;
goto fail;
}
c = malloc(crypto_box_BOXZEROBYTES + enc_msg_b64len);
memset(c, 0, crypto_box_BOXZEROBYTES);
enc_msg_sz = enc_msg_b64len;
ret = sso_base64_decode(c + crypto_box_BOXZEROBYTES, &enc_msg_sz,
(unsigned char *)enc_msg, enc_msg_b64len);
if (ret < 0)
goto fail;
clen = crypto_box_BOXZEROBYTES + enc_msg_sz;
m = malloc(clen);
ret = crypto_box_open(m, c, clen, n, public_key, secret_key);
if (ret < 0)
goto fail;
memcpy(out, m + crypto_box_ZEROBYTES, clen - crypto_box_ZEROBYTES);
out[clen - crypto_box_ZEROBYTES] = '\0';
fail:
if (c)
free(c);
if (m)
free(m);
return ret;
}
#ifndef __sso_encrypt_H
#define __sso_encrypt_H 1
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Must match tweetnacl.h */
#define SSO_TICKET_ENCRYPTION_PUBLIC_KEY_SIZE 32
#define SSO_TICKET_ENCRYPTION_SECRET_KEY_SIZE 32
int sso_generate_encryption_keys(unsigned char *publicp,
unsigned char *secretp);
int sso_serialize_ticket_encryption_public_key(const unsigned char *public_key,
char *out, size_t out_sz);
int sso_ticket_encrypt(const char *serialized_ticket,
const char *serialized_pub_key,
const unsigned char *secret_key, char *out,
size_t out_sz);
int sso_ticket_decrypt(const char *encrypted, const unsigned char *public_key,
const unsigned char *secret_key, char *out,
size_t out_sz);
#ifdef __cplusplus
}
#endif
#endif
......@@ -2,6 +2,7 @@ include $(top_srcdir)/Makefile.defs
check_PROGRAMS = \
sso_unittest \
sso_encrypt_unittest \
tweetnacl_unittest
TESTS = $(check_PROGRAMS)
......@@ -11,4 +12,6 @@ LDADD = $(GTEST_LIBS) ../libsso.la
sso_unittest_SOURCES = sso_unittest.cc
sso_encrypt_unittest_SOURCES = sso_encrypt_unittest.cc
tweetnacl_unittest_SOURCES = tweetnacl_unittest.cc
#include <gtest/gtest.h>
#include <stdlib.h>
#include <string.h>
#include "sso.h"
#include "sso_encrypt.h"
extern "C" {
#include "tweetnacl.h"
}
using std::string;
namespace {
class Encryption : public testing::Test {
protected:
virtual void SetUp() { sso_generate_encryption_keys(public_key, secret_key); }
unsigned char public_key[crypto_box_PUBLICKEYBYTES];
unsigned char secret_key[crypto_box_SECRETKEYBYTES];
};
TEST_F(Encryption, EncryptAndDecrypt) {
const char *serialized_ticket = "hello world I am a ticket";
char serialized_pubkey[64];
char buf[512];
char out[512];
ASSERT_EQ(0, sso_serialize_ticket_encryption_public_key(
public_key, serialized_pubkey, sizeof(serialized_pubkey)));
printf("serialized public key: %s (%lu bytes)\n", serialized_pubkey,
strlen(serialized_pubkey));
ASSERT_EQ(0, sso_ticket_encrypt(serialized_ticket, serialized_pubkey,
secret_key, buf, sizeof(buf)));
printf("encrypted ticket: %s\n", buf);
ASSERT_EQ(0,
sso_ticket_decrypt(buf, public_key, secret_key, out, sizeof(out)));
ASSERT_EQ(string(out), string(serialized_ticket));
}
TEST_F(Encryption, DecryptFailsWithBadData) {
const char *not_valid[] = {
"abcdefgh", "", "something.else", "dGVzdDE=.dGVzdDI=", NULL,
};
char out[1024];
int i;
for (i = 0; not_valid[i]; i++) {
ASSERT_LT(sso_ticket_decrypt(not_valid[i], public_key, secret_key, out,
sizeof(out)),
0)
<< "no error decrypting: " << not_valid[i];
}
}
} // namespace
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}