summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2012-11-15 19:32:53 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2012-11-15 19:32:53 +0100
commitb28f60cdd3d7efcb5699cb8360e1e50823238d1f (patch)
tree115da668115f4f10533358e3ad1b92dbe053b3a9
parentdf829e6c07ddc515fa6c00e4baa888dfaecbd26d (diff)
add C-Keens tls-client-auth
This patch adds client TLS authentication to libsec in compliance with rfc 4346. A new -c flag has been introduced for tlsclient allowing the user to specify a certificate in pem(8) format which will be provided to the server upon request. A -D debug flag has been introduced to enable debugging output. The patch has been tested against OpenSSL 0.9.7j 04 May 2006. It exists today because of the great (debugging) help and insight provided by Matthias Bauer. TODOs: - specification of a certain client key in factotum is not possible at the moment - tlssrv should support this too These will get added in another patch. The first try to submit this patch failed due to a network error. Sorry for the duplication! Kind regards, Christian
-rw-r--r--sys/man/8/tlssrv15
-rw-r--r--sys/src/cmd/tlsclient.c44
-rw-r--r--sys/src/libsec/port/tlshand.c81
-rw-r--r--sys/src/libsec/port/x509.c14
4 files changed, 134 insertions, 20 deletions
diff --git a/sys/man/8/tlssrv b/sys/man/8/tlssrv
index bbc74d3c8..e813f150c 100644
--- a/sys/man/8/tlssrv
+++ b/sys/man/8/tlssrv
@@ -24,6 +24,13 @@ logfile
.PP
.B tlsclient
[
+.B -D
+]
+[
+.B -c
+.I cert.pem
+]
+[
.B -t
.I trustedkeys
]
@@ -69,6 +76,14 @@ starts TLS,
and then relays
between the network connection
and standard input and output.
+The
+.B -D
+flag enables some debug output.
+Specifying a certificate in pem(8) format with the
+.B -c
+flag, causes the client to submit this certificate upon
+server's request. A corresponding key has to be present in
+.IR factotum(4).
If the
.B -t
flag
diff --git a/sys/src/cmd/tlsclient.c b/sys/src/cmd/tlsclient.c
index 8bfff5d0f..f4f1a25d8 100644
--- a/sys/src/cmd/tlsclient.c
+++ b/sys/src/cmd/tlsclient.c
@@ -6,7 +6,7 @@
void
usage(void)
{
- fprint(2, "usage: tlsclient [-t /sys/lib/tls/xxx] [-x /sys/lib/tls/xxx.exclude] dialstring\n");
+ fprint(2, "usage: tlsclient [-c lib/tls/clientcert] [-t /sys/lib/tls/xxx] [-x /sys/lib/tls/xxx.exclude] dialstring\n");
exits("usage");
}
@@ -21,18 +21,34 @@ xfer(int from, int to)
break;
}
+static int
+reporter(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprint(2, "%s: tls reports ", argv0);
+ vfprint(2, fmt, ap);
+ fprint(2, "\n");
+
+ va_end(ap);
+ return 0;
+}
+
void
main(int argc, char **argv)
{
- int fd, netfd;
+ int fd, netfd, debug;
uchar digest[20];
- TLSconn conn;
- char *addr, *file, *filex;
+ TLSconn *conn;
+ char *addr, *file, *filex, *ccert;
Thumbprint *thumb;
file = nil;
filex = nil;
thumb = nil;
+ ccert=nil;
+ debug=0;
ARGBEGIN{
case 't':
file = EARGF(usage());
@@ -40,6 +56,12 @@ main(int argc, char **argv)
case 'x':
filex = EARGF(usage());
break;
+ case 'D':
+ debug++;
+ break;
+ case 'c':
+ ccert = EARGF(usage());
+ break;
default:
usage();
}ARGEND
@@ -59,20 +81,24 @@ main(int argc, char **argv)
if((netfd = dial(addr, 0, 0, 0)) < 0)
sysfatal("dial %s: %r", addr);
- memset(&conn, 0, sizeof conn);
- fd = tlsClient(netfd, &conn);
+ conn = (TLSconn*)mallocz(sizeof *conn, 1);
+ if(ccert)
+ conn->cert = readcert(ccert, &conn->certlen);
+ if(debug)
+ conn->trace = reporter;
+ fd = tlsClient(netfd, conn);
if(fd < 0)
sysfatal("tlsclient: %r");
if(thumb){
- if(conn.cert==nil || conn.certlen<=0)
+ if(conn->cert==nil || conn->certlen<=0)
sysfatal("server did not provide TLS certificate");
- sha1(conn.cert, conn.certlen, digest, nil);
+ sha1(conn->cert, conn->certlen, digest, nil);
if(!okThumbprint(digest, thumb)){
fmtinstall('H', encodefmt);
sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
}
}
- free(conn.cert);
+ free(conn->cert);
close(netfd);
rfork(RFNOTEG);
diff --git a/sys/src/libsec/port/tlshand.c b/sys/src/libsec/port/tlshand.c
index 5ebd3d4e5..16a82ef65 100644
--- a/sys/src/libsec/port/tlshand.c
+++ b/sys/src/libsec/port/tlshand.c
@@ -112,6 +112,9 @@ typedef struct Msg{
struct {
Bytes *key;
} clientKeyExchange;
+ struct {
+ Bytes *signature;
+ } certificateVerify;
Finished finished;
} u;
} Msg;
@@ -249,8 +252,7 @@ static uchar compressors[] = {
};
static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chain);
-static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...));
-
+static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, int (*trace)(char*fmt, ...));
static void msgClear(Msg *m);
static char* msgPrint(char *buf, int n, Msg *m);
static int msgRecv(TlsConnection *c, Msg *m);
@@ -301,11 +303,15 @@ static int get24(uchar *p);
static int get16(uchar *p);
static Bytes* newbytes(int len);
static Bytes* makebytes(uchar* buf, int len);
+static Bytes* mptobytes(mpint* big);
static void freebytes(Bytes* b);
static Ints* newints(int len);
static Ints* makeints(int* buf, int len);
static void freeints(Ints* b);
+/* x509.c */
+extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus);
+
//================= client/server ========================
// push TLS onto fd, returning new (application) file descriptor
@@ -398,7 +404,7 @@ tlsClient(int fd, TLSconn *conn)
return -1;
}
fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
- tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->trace);
+ tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->cert, conn->certlen, conn->trace);
close(hand);
close(ctl);
close(fd);
@@ -602,13 +608,14 @@ Err:
}
static TlsConnection *
-tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...))
+tlsClient2(int ctl, int hand, uchar *csid, int ncsid, uchar *cert, int certlen, int (*trace)(char*fmt, ...))
{
TlsConnection *c;
Msg m;
uchar kd[MaxKeyData], *epm;
char *secrets;
int creq, nepm, rv;
+ mpint *signedMP, *paddedHashes;
if(!initCiphers())
return nil;
@@ -719,7 +726,9 @@ tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...
}
if(creq) {
- /* send a zero length certificate */
+ m.u.certificate.ncert = 1;
+ m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes));
+ m.u.certificate.certs[0] = makebytes(cert, certlen);
m.tag = HCertificate;
if(!msgSend(c, &m, AFlush))
goto Err;
@@ -735,10 +744,55 @@ tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...
tlsError(c, EHandshakeFailure, "can't set secret: %r");
goto Err;
}
+
if(!msgSend(c, &m, AFlush))
goto Err;
msgClear(&m);
+ /* CertificateVerify */
+ /*XXX I should only send this when it is not DH right?
+ Also we need to know which TLS key
+ we have to use in case there are more than one*/
+ if(cert) {
+ m.tag = HCertificateVerify;
+ uchar hshashes[MD5dlen+SHA1dlen]; /* content of signature */
+ MD5state hsmd5_save;
+ SHAstate hssha1_save;
+
+ /* save the state for the Finish message */
+
+ hsmd5_save = c->hsmd5;
+ hssha1_save = c->hssha1;
+ md5(nil, 0, hshashes, &c->hsmd5);
+ sha1(nil, 0, hshashes+MD5dlen, &c->hssha1);
+
+ c->hsmd5 = hsmd5_save;
+ c->hssha1 = hssha1_save;
+
+ c->sec->rpc = factotum_rsa_open(cert, certlen);
+ if(c->sec->rpc == nil){
+ tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
+ goto Err;
+ }
+ c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+
+ paddedHashes = pkcs1padbuf(hshashes, 36, c->sec->rsapub->n);
+ signedMP = factotum_rsa_decrypt(c->sec->rpc, paddedHashes);
+ m.u.certificateVerify.signature = mptobytes(signedMP);
+ free(signedMP);
+
+ if(m.u.certificateVerify.signature == nil){
+ msgClear(&m);
+ goto Err;
+ }
+
+ if(!msgSend(c, &m, AFlush)){
+ msgClear(&m);
+ goto Err;
+ }
+ msgClear(&m);
+ }
+
/* change cipher spec */
if(fprint(c->ctl, "changecipher") < 0){
tlsError(c, EInternalError, "can't enable cipher: %r");
@@ -892,6 +946,12 @@ msgSend(TlsConnection *c, Msg *m, int act)
p += m->u.certificate.certs[i]->len;
}
break;
+ case HCertificateVerify:
+ put16(p, m->u.certificateVerify.signature->len);
+ p += 2;
+ memmove(p, m->u.certificateVerify.signature->data, m->u.certificateVerify.signature->len);
+ p += m->u.certificateVerify.signature->len;
+ break;
case HClientKeyExchange:
n = m->u.clientKeyExchange.key->len;
if(c->version != SSL3Version){
@@ -1116,7 +1176,7 @@ msgRecv(TlsConnection *c, Msg *m)
nn = get24(p);
p += 3;
n -= 3;
- if(n != nn)
+ if(nn == 0 && n > 0)
goto Short;
/* certs */
i = 0;
@@ -1142,7 +1202,7 @@ msgRecv(TlsConnection *c, Msg *m)
nn = p[0];
p += 1;
n -= 1;
- if(nn < 1 || nn > n)
+ if(nn > n)
goto Short;
m->u.certificateRequest.types = makebytes(p, nn);
p += nn;
@@ -1250,6 +1310,9 @@ msgClear(Msg *m)
freebytes(m->u.certificateRequest.cas[i]);
free(m->u.certificateRequest.cas);
break;
+ case HCertificateVerify:
+ freebytes(m->u.certificateVerify.signature);
+ break;
case HServerHelloDone:
break;
case HClientKeyExchange:
@@ -1343,6 +1406,10 @@ msgPrint(char *buf, int n, Msg *m)
for(i=0; i<m->u.certificateRequest.nca; i++)
bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n");
break;
+ case HCertificateVerify:
+ bs = seprint(bs, be, "HCertificateVerify\n");
+ bs = bytesPrint(bs, be, "\tsignature: ", m->u.certificateVerify.signature,"\n");
+ break;
case HServerHelloDone:
bs = seprint(bs, be, "ServerHelloDone\n");
break;
diff --git a/sys/src/libsec/port/x509.c b/sys/src/libsec/port/x509.c
index 103ab2933..77c2205f9 100644
--- a/sys/src/libsec/port/x509.c
+++ b/sys/src/libsec/port/x509.c
@@ -2036,27 +2036,33 @@ asn1mpint(Elem *e)
return nil;
}
-static mpint*
-pkcs1pad(Bytes *b, mpint *modulus)
+mpint*
+pkcs1padbuf(uchar *buf, int len, mpint *modulus)
{
int n = (mpsignif(modulus)+7)/8;
int pm1, i;
uchar *p;
mpint *mp;
- pm1 = n - 1 - b->len;
+ pm1 = n - 1 - len;
p = (uchar*)emalloc(n);
p[0] = 0;
p[1] = 1;
for(i = 2; i < pm1; i++)
p[i] = 0xFF;
p[pm1] = 0;
- memcpy(&p[pm1+1], b->data, b->len);
+ memcpy(&p[pm1+1], buf, len);
mp = betomp(p, n, nil);
free(p);
return mp;
}
+static mpint*
+pkcs1pad(Bytes *b, mpint *modulus)
+{
+ return pkcs1padbuf(b->data, b->len, modulus);
+}
+
RSApriv*
asn1toRSApriv(uchar *kd, int kn)
{