summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2022-04-11 10:03:47 +0200
committerGerd Hoffmann <kraxel@redhat.com>2022-04-22 14:52:30 +0200
commit67c81a2a3490f53ba26411b031c164e583d8c695 (patch)
treea00a07052ca196b518817d94ea6b9ec080a8d80f
parentc264e4319195e2cc7b2b77194abc610ff08baafd (diff)
downloadamtterm-67c81a2a3490f53ba26411b031c164e583d8c695.tar.gz
Merge branch 'ssl'
-rw-r--r--GNUmakefile17
-rw-r--r--amtterm.c18
-rw-r--r--auth.c826
-rw-r--r--auth.h6
-rw-r--r--gamt.c22
-rw-r--r--redir.c101
-rw-r--r--redir.h4
-rw-r--r--ssl.c452
-rw-r--r--ssl.h7
9 files changed, 1445 insertions, 8 deletions
diff --git a/GNUmakefile b/GNUmakefile
index c05b5b3..fb5684b 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -1,11 +1,24 @@
# config
+#USE_OPENSSL=1
+#USE_GNUTLS=1
srcdir = .
VPATH = $(srcdir)
-include Make.config
include $(srcdir)/mk/Variables.mk
+ifdef USE_OPENSSL
+SSL_DEFS=-DUSE_OPENSSL
+pkglst+=openssl
+endif
+
+ifdef USE_GNUTLS
+SSL_DEFS=-DUSE_GNUTLS
+pkglst+=gnutls
+endif
+
CFLAGS += -Wall -Wno-pointer-sign
CFLAGS += -DVERSION='"$(VERSION)"'
+CFLAGS += $(SSL_DEFS)
TARGETS := amtterm
DESKTOP := $(wildcard *.desktop)
@@ -60,8 +73,8 @@ distclean: clean
#################################################################
-amtterm: amtterm.o redir.o tcp.o
-gamt: gamt.o redir.o tcp.o parseconfig.o
+amtterm: amtterm.o redir.o tcp.o auth.o ssl.o
+gamt: gamt.o redir.o tcp.o parseconfig.o auth.o ssl.o
#################################################################
diff --git a/amtterm.c b/amtterm.c
index b6c7538..a69f58c 100644
--- a/amtterm.c
+++ b/amtterm.c
@@ -179,10 +179,18 @@ static void usage(FILE *fp)
" -h print this text\n"
" -v verbose (default)\n"
" -q quiet\n"
+ " -L use legacy authentication\n"
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
+ " -C cacert enable SSL and use PEM cacert file\n"
+#endif
" -u user username (default: admin)\n"
" -p pass password (default: $AMT_PASSWORD)\n"
"\n"
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
+ "By default port 16994 (SSL: 16995) is used.\n"
+#else
"By default port 16994 is used.\n"
+#endif
"If no password is given " APPNAME " will ask for one.\n"
"\n"
"-- \n"
@@ -209,7 +217,7 @@ int main(int argc, char *argv[])
snprintf(r.pass, sizeof(r.pass), "%s", h);
for (;;) {
- if (-1 == (c = getopt(argc, argv, "hvqu:p:")))
+ if (-1 == (c = getopt(argc, argv, "hvqu:p:LC:")))
break;
switch (c) {
case 'v':
@@ -225,6 +233,14 @@ int main(int argc, char *argv[])
snprintf(r.pass, sizeof(r.pass), "%s", optarg);
memset(optarg,'*',strlen(optarg)); /* rm passwd from ps list */
break;
+ case 'L':
+ r.legacy = 1;
+ break;
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
+ case 'C':
+ r.cacert = optarg;
+ break;
+#endif
case 'h':
usage(stdout);
diff --git a/auth.c b/auth.c
new file mode 100644
index 0000000..d3ac1e0
--- /dev/null
+++ b/auth.c
@@ -0,0 +1,826 @@
+/*
+ * Authentication helper functions.
+ *
+ * Copyright (C) 2014 Andreas Steinmetz <ast@domdv.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include "auth.h"
+
+#define MD5_SIZE 16
+#define MD5_HEX (MD5_SIZE<<1)
+#define MD5(a) uint8_t a[MD5_SIZE]
+
+#define AUTH_SESS 0x13
+#define AUTH_SESS_REPLY 0x14
+
+#define AUTH_SUCCESS 0x00
+#define AUTH_FAIL 0x01
+
+#define TYPE_METHODS 0x00
+#define TYPE_PLAIN 0x01
+#define TYPE_RFC2069 0x03
+#define TYPE_RFC2617 0x04
+
+#define MAX_USER 32
+#define MAX_PASS 32
+#define MAX_REALM 64
+#define MAX_NONCE MD5_HEX
+#define MAX_CNONCE MD5_HEX
+#define MAX_RESPONSE MD5_HEX
+#define MAX_QOP 8
+
+#define URI "/RedirectionService"
+#define NC "00000002"
+#define QOP "auth"
+#define METHOD "POST"
+
+#define put_header(data,length,type) \
+do { \
+ *data++=AUTH_SESS; \
+ *data++=AUTH_SUCCESS; \
+ *data++=0; \
+ *data++=0; \
+ *data++=type; \
+ *data++=(unsigned char)(length); \
+ *data++=(unsigned char)(length>>8); \
+ *data++=(unsigned char)(length>>16); \
+ *data++=(unsigned char)(length>>24); \
+} while(0)
+
+#define put_element(data,element) \
+do { \
+ int length=strlen(element); \
+ *data++=(unsigned char)length; \
+ memcpy(data,element,length); \
+ data+=length; \
+} while(0)
+
+#define get_header(data,len,method,status) \
+do { \
+ if(data[0]!=AUTH_SESS_REPLY)goto out; \
+ status=data[1]; \
+ if(data[4]!=method)goto out; \
+ len=data[8]; \
+ len<<=8; \
+ len|=data[7]; \
+ len<<=8; \
+ len|=data[6]; \
+ len<<=8; \
+ len|=data[5]; \
+} while(0)
+
+#define get_element(data,len,element,max) \
+do { \
+ int blen=*data++; \
+ if(blen>--len||blen>max)goto out; \
+ memcpy(element,data,blen); \
+ element[blen]=0; \
+ data+=blen; \
+ len-=blen; \
+} while(0)
+
+typedef struct
+{
+ uint32_t md5[4];
+ uint32_t total;
+ uint8_t bfr[64];
+ uint8_t size;
+} md5_ctx;
+
+static void md5block(uint32_t *md5,uint8_t *block)
+{
+ register uint32_t a;
+ register uint32_t b;
+ register uint32_t c;
+ register uint32_t d;
+ uint32_t bfr[16];
+
+ a=block[3];
+ a<<=8;
+ a+=block[2];
+ a<<=8;
+ a+=block[1];
+ a<<=8;
+ bfr[0]=a+block[0];
+
+ a=block[7];
+ a<<=8;
+ a+=block[6];
+ a<<=8;
+ a+=block[5];
+ a<<=8;
+ bfr[1]=a+block[4];
+
+ a=block[11];
+ a<<=8;
+ a+=block[10];
+ a<<=8;
+ a+=block[9];
+ a<<=8;
+ bfr[2]=a+block[8];
+
+ a=block[15];
+ a<<=8;
+ a+=block[14];
+ a<<=8;
+ a+=block[13];
+ a<<=8;
+ bfr[3]=a+block[12];
+
+ a=block[19];
+ a<<=8;
+ a+=block[18];
+ a<<=8;
+ a+=block[17];
+ a<<=8;
+ bfr[4]=a+block[16];
+
+ a=block[23];
+ a<<=8;
+ a+=block[22];
+ a<<=8;
+ a+=block[21];
+ a<<=8;
+ bfr[5]=a+block[20];
+
+ a=block[27];
+ a<<=8;
+ a+=block[26];
+ a<<=8;
+ a+=block[25];
+ a<<=8;
+ bfr[6]=a+block[24];
+
+ a=block[31];
+ a<<=8;
+ a+=block[30];
+ a<<=8;
+ a+=block[29];
+ a<<=8;
+ bfr[7]=a+block[28];
+
+ a=block[35];
+ a<<=8;
+ a+=block[34];
+ a<<=8;
+ a+=block[33];
+ a<<=8;
+ bfr[8]=a+block[32];
+
+ a=block[39];
+ a<<=8;
+ a+=block[38];
+ a<<=8;
+ a+=block[37];
+ a<<=8;
+ bfr[9]=a+block[36];
+
+ a=block[43];
+ a<<=8;
+ a+=block[42];
+ a<<=8;
+ a+=block[41];
+ a<<=8;
+ bfr[10]=a+block[40];
+
+ a=block[47];
+ a<<=8;
+ a+=block[46];
+ a<<=8;
+ a+=block[45];
+ a<<=8;
+ bfr[11]=a+block[44];
+
+ a=block[51];
+ a<<=8;
+ a+=block[50];
+ a<<=8;
+ a+=block[49];
+ a<<=8;
+ bfr[12]=a+block[48];
+
+ a=block[55];
+ a<<=8;
+ a+=block[54];
+ a<<=8;
+ a+=block[53];
+ a<<=8;
+ bfr[13]=a+block[52];
+
+ a=block[59];
+ a<<=8;
+ a+=block[58];
+ a<<=8;
+ a+=block[57];
+ a<<=8;
+ bfr[14]=a+block[56];
+
+ a=block[63];
+ a<<=8;
+ a+=block[62];
+ a<<=8;
+ a+=block[61];
+ a<<=8;
+ bfr[15]=a+block[60];
+
+ a=md5[0];
+ b=md5[1];
+ c=md5[2];
+ d=md5[3];
+
+ a+=bfr[0];
+ a+=0xd76aa478;
+ a+=d^(b&(c^d));
+ a=(a<<7)|(a>>25);
+ a+=b;
+ d+=bfr[1];
+ d+=0xe8c7b756;
+ d+=c^(a&(b^c));
+ d=(d<<12)|(d>>20);
+ d+=a;
+ c+=bfr[2];
+ c+=0x242070db;
+ c+=b^(d&(a^b));
+ c=(c<<17)|(c>>15);
+ c+=d;
+ b+=bfr[3];
+ b+=0xc1bdceee;
+ b+=a^(c&(d^a));
+ b=(b<<22)|(b>>10);
+ b+=c;
+ a+=bfr[4];
+ a+=0xf57c0faf;
+ a+=d^(b&(c^d));
+ a=(a<<7)|(a>>25);
+ a+=b;
+ d+=bfr[5];
+ d+=0x4787c62a;
+ d+=c^(a&(b^c));
+ d=(d<<12)|(d>>20);
+ d+=a;
+ c+=bfr[6];
+ c+=0xa8304613;
+ c+=b^(d&(a^b));
+ c=(c<<17)|(c>>15);
+ c+=d;
+ b+=bfr[7];
+ b+=0xfd469501;
+ b+=a^(c&(d^a));
+ b=(b<<22)|(b>>10);
+ b+=c;
+ a+=bfr[8];
+ a+=0x698098d8;
+ a+=d^(b&(c^d));
+ a=(a<<7)|(a>>25);
+ a+=b;
+ d+=bfr[9];
+ d+=0x8b44f7af;
+ d+=c^(a&(b^c));
+ d=(d<<12)|(d>>20);
+ d+=a;
+ c+=bfr[10];
+ c+=0xffff5bb1;
+ c+=b^(d&(a^b));
+ c=(c<<17)|(c>>15);
+ c+=d;
+ b+=bfr[11];
+ b+=0x895cd7be;
+ b+=a^(c&(d^a));
+ b=(b<<22)|(b>>10);
+ b+=c;
+ a+=bfr[12];
+ a+=0x6b901122;
+ a+=d^(b&(c^d));
+ a=(a<<7)|(a>>25);
+ a+=b;
+ d+=bfr[13];
+ d+=0xfd987193;
+ d+=c^(a&(b^c));
+ d=(d<<12)|(d>>20);
+ d+=a;
+ c+=bfr[14];
+ c+=0xa679438e;
+ c+=b^(d&(a^b));
+ c=(c<<17)|(c>>15);
+ c+=d;
+ b+=bfr[15];
+ b+=0x49b40821;
+ b+=a^(c&(d^a));
+ b=(b<<22)|(b>>10);
+ b+=c;
+
+ a+=bfr[1];
+ a+=0xf61e2562;
+ a+=c^(d&(b^c));
+ a=(a<<5)|(a>>27);
+ a+=b;
+ d+=bfr[6];
+ d+=0xc040b340;
+ d+=b^(c&(a^b));
+ d=(d<<9)|(d>>23);
+ d+=a;
+ c+=bfr[11];
+ c+=0x265e5a51;
+ c+=a^(b&(d^a));
+ c=(c<<14)|(c>>18);
+ c+=d;
+ b+=bfr[0];
+ b+=0xe9b6c7aa;
+ b+=d^(a&(c^d));
+ b=(b<<20)|(b>>12);
+ b+=c;
+ a+=bfr[5];
+ a+=0xd62f105d;
+ a+=c^(d&(b^c));
+ a=(a<<5)|(a>>27);
+ a+=b;
+ d+=bfr[10];
+ d+=0x02441453;
+ d+=b^(c&(a^b));
+ d=(d<<9)|(d>>23);
+ d+=a;
+ c+=bfr[15];
+ c+=0xd8a1e681;
+ c+=a^(b&(d^a));
+ c=(c<<14)|(c>>18);
+ c+=d;
+ b+=bfr[4];
+ b+=0xe7d3fbc8;
+ b+=d^(a&(c^d));
+ b=(b<<20)|(b>>12);
+ b+=c;
+ a+=bfr[9];
+ a+=0x21e1cde6;
+ a+=c^(d&(b^c));
+ a=(a<<5)|(a>>27);
+ a+=b;
+ d+=bfr[14];
+ d+=0xc33707d6;
+ d+=b^(c&(a^b));
+ d=(d<<9)|(d>>23);
+ d+=a;
+ c+=bfr[3];
+ c+=0xf4d50d87;
+ c+=a^(b&(d^a));
+ c=(c<<14)|(c>>18);
+ c+=d;
+ b+=bfr[8];
+ b+=0x455a14ed;
+ b+=d^(a&(c^d));
+ b=(b<<20)|(b>>12);
+ b+=c;
+ a+=bfr[13];
+ a+=0xa9e3e905;
+ a+=c^(d&(b^c));
+ a=(a<<5)|(a>>27);
+ a+=b;
+ d+=bfr[2];
+ d+=0xfcefa3f8;
+ d+=b^(c&(a^b));
+ d=(d<<9)|(d>>23);
+ d+=a;
+ c+=bfr[7];
+ c+=0x676f02d9;
+ c+=a^(b&(d^a));
+ c=(c<<14)|(c>>18);
+ c+=d;
+ b+=bfr[12];
+ b+=0x8d2a4c8a;
+ b+=d^(a&(c^d));
+ b=(b<<20)|(b>>12);
+ b+=c;
+
+ a+=bfr[5];
+ a+=0xfffa3942;
+ a+=b^c^d;
+ a=(a<<4)|(a>>28);
+ a+=b;
+ d+=bfr[8];
+ d+=0x8771f681;
+ d+=a^b^c;
+ d=(d<<11)|(d>>21);
+ d+=a;
+ c+=bfr[11];
+ c+=0x6d9d6122;
+ c+=d^a^b;
+ c=(c<<16)|(c>>16);
+ c+=d;
+ b+=bfr[14];
+ b+=0xfde5380c;
+ b+=c^d^a;
+ b=(b<<23)|(b>>9);
+ b+=c;
+ a+=bfr[1];
+ a+=0xa4beea44;
+ a+=b^c^d;
+ a=(a<<4)|(a>>28);
+ a+=b;
+ d+=bfr[4];
+ d+=0x4bdecfa9;
+ d+=a^b^c;
+ d=(d<<11)|(d>>21);
+ d+=a;
+ c+=bfr[7];
+ c+=0xf6bb4b60;
+ c+=d^a^b;
+ c=(c<<16)|(c>>16);
+ c+=d;
+ b+=bfr[10];
+ b+=0xbebfbc70;
+ b+=c^d^a;
+ b=(b<<23)|(b>>9);
+ b+=c;
+ a+=bfr[13];
+ a+=0x289b7ec6;
+ a+=b^c^d;
+ a=(a<<4)|(a>>28);
+ a+=b;
+ d+=bfr[0];
+ d+=0xeaa127fa;
+ d+=a^b^c;
+ d=(d<<11)|(d>>21);
+ d+=a;
+ c+=bfr[3];
+ c+=0xd4ef3085;
+ c+=d^a^b;
+ c=(c<<16)|(c>>16);
+ c+=d;
+ b+=bfr[6];
+ b+=0x04881d05;
+ b+=c^d^a;
+ b=(b<<23)|(b>>9);
+ b+=c;
+ a+=bfr[9];
+ a+=0xd9d4d039;
+ a+=b^c^d;
+ a=(a<<4)|(a>>28);
+ a+=b;
+ d+=bfr[12];
+ d+=0xe6db99e5;
+ d+=a^b^c;
+ d=(d<<11)|(d>>21);
+ d+=a;
+ c+=bfr[15];
+ c+=0x1fa27cf8;
+ c+=d^a^b;
+ c=(c<<16)|(c>>16);
+ c+=d;
+ b+=bfr[2];
+ b+=0xc4ac5665;
+ b+=c^d^a;
+ b=(b<<23)|(b>>9);
+ b+=c;
+
+ a+=bfr[0];
+ a+=0xf4292244;
+ a+=c^(b|~d);
+ a=(a<<6)|(a>>26);
+ a+=b;
+ d+=bfr[7];
+ d+=0x432aff97;
+ d+=b^(a|~c);
+ d=(d<<10)|(d>>22);
+ d+=a;
+ c+=bfr[14];
+ c+=0xab9423a7;
+ c+=a^(d|~b);
+ c=(c<<15)|(c>>17);
+ c+=d;
+ b+=bfr[5];
+ b+=0xfc93a039;
+ b+=d^(c|~a);
+ b=(b<<21)|(b>>11);
+ b+=c;
+ a+=bfr[12];
+ a+=0x655b59c3;
+ a+=c^(b|~d);
+ a=(a<<6)|(a>>26);
+ a+=b;
+ d+=bfr[3];
+ d+=0x8f0ccc92;
+ d+=b^(a|~c);
+ d=(d<<10)|(d>>22);
+ d+=a;
+ c+=bfr[10];
+ c+=0xffeff47d;
+ c+=a^(d|~b);
+ c=(c<<15)|(c>>17);
+ c+=d;
+ b+=bfr[1];
+ b+=0x85845dd1;
+ b+=d^(c|~a);
+ b=(b<<21)|(b>>11);
+ b+=c;
+ a+=bfr[8];
+ a+=0x6fa87e4f;
+ a+=c^(b|~d);
+ a=(a<<6)|(a>>26);
+ a+=b;
+ d+=bfr[15];
+ d+=0xfe2ce6e0;
+ d+=b^(a|~c);
+ d=(d<<10)|(d>>22);
+ d+=a;
+ c+=bfr[6];
+ c+=0xa3014314;
+ c+=a^(d|~b);
+ c=(c<<15)|(c>>17);
+ c+=d;
+ b+=bfr[13];
+ b+=0x4e0811a1;
+ b+=d^(c|~a);
+ b=(b<<21)|(b>>11);
+ b+=c;
+ a+=bfr[4];
+ a+=0xf7537e82;
+ a+=c^(b|~d);
+ a=(a<<6)|(a>>26);
+ a+=b;
+ d+=bfr[11];
+ d+=0xbd3af235;
+ d+=b^(a|~c);
+ d=(d<<10)|(d>>22);
+ d+=a;
+ c+=bfr[2];
+ c+=0x2ad7d2bb;
+ c+=a^(d|~b);
+ c=(c<<15)|(c>>17);
+ c+=d;
+ b+=bfr[9];
+ b+=0xeb86d391;
+ b+=d^(c|~a);
+ b=(b<<21)|(b>>11);
+ b+=c;
+
+ md5[0]+=a;
+ md5[1]+=b;
+ md5[2]+=c;
+ md5[3]+=d;
+}
+
+static void md5init(register md5_ctx *ptr)
+{
+ ptr->total=ptr->size=0;
+ ptr->md5[0]=0x67452301;
+ ptr->md5[1]=0xefcdab89;
+ ptr->md5[2]=0x98badcfe;
+ ptr->md5[3]=0x10325476;
+}
+
+static void md5next(register uint8_t *data,register uint32_t length,
+ register md5_ctx *ptr)
+{
+ register uint32_t i;
+
+ ptr->total+=length;
+
+ for(i=ptr->size;(i&63)&&length;length--)ptr->bfr[i++]=*data++;
+
+ if(i==64)
+ {
+ i=0;
+ md5block(ptr->md5,ptr->bfr);
+ }
+
+ for(;length>63;data+=64,length-=64)
+ md5block(ptr->md5,data);
+
+ for(;length;length--)ptr->bfr[i++]=*data++;
+
+ ptr->size=(uint8_t)(i);
+}
+
+static void md5end(register uint8_t *result,register md5_ctx *ptr)
+{
+ register uint32_t i=ptr->size;
+
+ ptr->bfr[i++]=0x80;
+ if(i>56)
+ {
+ for(;i<64;i++)ptr->bfr[i]=0;
+ i=0;
+ md5block(ptr->md5,ptr->bfr);
+ }
+ for(;i<56;i++)ptr->bfr[i]=0;
+
+ ptr->bfr[56]=(uint8_t)((ptr->total)<<3);
+ ptr->bfr[57]=(uint8_t)((ptr->total)>>5);
+ ptr->bfr[58]=(uint8_t)((ptr->total)>>13);
+ ptr->bfr[59]=(uint8_t)((ptr->total)>>21);
+ ptr->bfr[60]=(uint8_t)((ptr->total)>>29);
+ ptr->bfr[61]=0;
+ ptr->bfr[62]=0;
+ ptr->bfr[63]=0;
+
+ md5block(ptr->md5,ptr->bfr);
+
+ result[ 0]=(uint8_t) (ptr->md5[0]);
+ result[ 1]=(uint8_t)((ptr->md5[0])>>8);
+ result[ 2]=(uint8_t)((ptr->md5[0])>>16);
+ result[ 3]=(uint8_t)((ptr->md5[0])>>24);
+ result[ 4]=(uint8_t) (ptr->md5[1]);
+ result[ 5]=(uint8_t)((ptr->md5[1])>>8);
+ result[ 6]=(uint8_t)((ptr->md5[1])>>16);
+ result[ 7]=(uint8_t)((ptr->md5[1])>>24);
+ result[ 8]=(uint8_t) (ptr->md5[2]);
+ result[ 9]=(uint8_t)((ptr->md5[2])>>8);
+ result[10]=(uint8_t)((ptr->md5[2])>>16);
+ result[11]=(uint8_t)((ptr->md5[2])>>24);
+ result[12]=(uint8_t) (ptr->md5[3]);
+ result[13]=(uint8_t)((ptr->md5[3])>>8);
+ result[14]=(uint8_t)((ptr->md5[3])>>16);
+ result[15]=(uint8_t)((ptr->md5[3])>>24);
+}
+
+static void bin2hex(unsigned char *bin,int len,char *hex)
+{
+ while(len--)
+ {
+ *hex=*bin>>4;
+ if(*hex>9)*hex+++='a'-10;
+ else *hex+++='0';
+ *hex=*bin++&0xf;
+ if(*hex>9)*hex+++='a'-10;
+ else *hex+++='0';
+ }
+ *hex=0;
+}
+
+int authenticate(int mode,char *user,char *pass,
+ int (*io)(void *parm,unsigned char *data,int len,int mode),void *parm)
+{
+ int r=-1;
+ int len;
+ int status;
+ int method;
+ unsigned char *ptr;
+ unsigned char bfr[512];
+ char realm[MAX_REALM+1];
+ char nonce[MAX_NONCE+1];
+ char cnonce[MAX_CNONCE+1];
+ char response[MAX_RESPONSE+1];
+ char qop[MAX_QOP+1];
+ char tmp[MD5_HEX+1];
+ MD5(md5);
+ md5_ctx ctx;
+
+ if(strlen(user)>MAX_USER||strlen(pass)>MAX_PASS)return -1;
+
+ ptr=bfr;
+ put_header(ptr,0,TYPE_METHODS);
+ if(io(parm,bfr,9,WRITE))goto out;
+
+ if(io(parm,bfr,9,READ))goto out;
+ get_header(bfr,len,TYPE_METHODS,status);
+ if(status!=AUTH_SUCCESS)goto out;
+ if(len>sizeof(bfr))goto out;
+ if(io(parm,bfr,len,READ))goto out;
+
+ for(method=0,ptr=bfr;len;len--)
+ {
+ if(!*ptr||*ptr>=sizeof(method)*8)return -1;
+ method|=1<<*ptr++;
+ }
+
+ if(method&(1<<TYPE_RFC2617))method=TYPE_RFC2617;
+ else if(method&(1<<TYPE_RFC2069))method=TYPE_RFC2069;
+ else if(method&(1<<TYPE_PLAIN))method=TYPE_PLAIN;
+ else goto out;
+
+ switch(method)
+ {
+ case TYPE_PLAIN:
+ len=strlen(user)+strlen(pass)+2;
+ ptr=bfr;
+ put_header(ptr,len,method);
+ put_element(ptr,user);
+ put_element(ptr,pass);
+ break;
+
+ case TYPE_RFC2069:
+ case TYPE_RFC2617:
+ len=strlen(user)+strlen(URI)+(method==TYPE_RFC2617?8:7);
+ ptr=bfr;
+ put_header(ptr,len,method);
+ put_element(ptr,user);
+ *ptr++=0;
+ *ptr++=0;
+ put_element(ptr,URI);
+ *ptr++=0;
+ *ptr++=0;
+ *ptr++=0;
+ *ptr=0;
+ if(io(parm,bfr,len+9,WRITE))goto out;
+
+ if(io(parm,bfr,9,READ))goto out;
+ get_header(bfr,len,method,status);
+ if(status!=AUTH_FAIL)goto out;
+ if(len>sizeof(bfr))goto out;
+ if(io(parm,bfr,len,READ))goto out;
+ ptr=bfr;
+ if(!len)goto out;
+ get_element(ptr,len,realm,MAX_REALM);
+ if(!len)goto out;
+ get_element(ptr,len,nonce,MAX_NONCE);
+ if(!len)*qop=0;
+ else get_element(ptr,len,qop,MAX_QOP);
+
+ if(method==TYPE_RFC2617)
+ {
+ if(strcmp(qop,QOP))goto out;
+ }
+ else if(*qop)goto out;
+
+ if(io(parm,bfr,16,RANDOM))goto out;
+ md5init(&ctx);
+ md5next(bfr,16,&ctx);
+ md5end(md5,&ctx);
+ bin2hex(md5,MD5_SIZE,cnonce);
+
+ md5init(&ctx);
+ md5next((uint8_t *)user,strlen(user),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ md5next((uint8_t *)realm,strlen(realm),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ md5next((uint8_t *)pass,strlen(pass),&ctx);
+ md5end(md5,&ctx);
+ bin2hex(md5,MD5_SIZE,tmp);
+
+ md5init(&ctx);
+ md5next((uint8_t *)METHOD,strlen(METHOD),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ md5next((uint8_t *)URI,strlen(URI),&ctx);
+ md5end(md5,&ctx);
+ bin2hex(md5,MD5_SIZE,response);
+
+ md5init(&ctx);
+ md5next((uint8_t *)tmp,strlen(tmp),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ md5next((uint8_t *)nonce,strlen(nonce),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ if(method==TYPE_RFC2617)
+ {
+ md5next((uint8_t *)NC,strlen(NC),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ md5next((uint8_t *)cnonce,strlen(cnonce),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ md5next((uint8_t *)qop,strlen(qop),&ctx);
+ md5next((uint8_t *)":",1,&ctx);
+ }
+ md5next((uint8_t *)response,strlen(response),&ctx);
+ md5end(md5,&ctx);
+ bin2hex(md5,MD5_SIZE,response);
+
+ len=strlen(user)+strlen(realm)+strlen(nonce)
+ +strlen(URI)+strlen(cnonce)+strlen(NC)
+ +strlen(response)+strlen(qop)+
+ (method==TYPE_RFC2617?8:7);
+ ptr=bfr;
+ put_header(ptr,len,method);
+ put_element(ptr,user);
+ put_element(ptr,realm);
+ put_element(ptr,nonce);
+ put_element(ptr,URI);
+ put_element(ptr,cnonce);
+ put_element(ptr,NC);
+ put_element(ptr,response);
+ put_element(ptr,qop);
+ break;
+
+ default:goto out;
+ }
+
+ if(io(parm,bfr,len+9,WRITE))goto out;
+
+ if(mode)
+ {
+ if(io(parm,bfr,9,READ))goto out;
+ get_header(bfr,len,method,status);
+ if(len>sizeof(bfr))goto out;
+ if(io(parm,bfr,len,READ))goto out;
+
+ if(status==AUTH_SUCCESS)r=0;
+ else r=-2;
+ }
+ else r=0;
+
+out: memset(bfr,0,sizeof(bfr));
+
+ return r;
+}
diff --git a/auth.h b/auth.h
new file mode 100644
index 0000000..e511146
--- /dev/null
+++ b/auth.h
@@ -0,0 +1,6 @@
+#define READ 0
+#define WRITE 1
+#define RANDOM 2
+
+extern int authenticate(int mode,char *user,char *pass,
+ int (*io)(void *parm,unsigned char *data,int len,int mode),void *parm);
diff --git a/gamt.c b/gamt.c
index 401fa83..30de52b 100644
--- a/gamt.c
+++ b/gamt.c
@@ -84,6 +84,8 @@ static char amt_user[32] = "admin";
static char amt_pass[32];
static int amt_trace;
static int amt_debug;
+static int amt_legacy;
+static char *amt_cacert;
static int gamt_getstring(GtkWidget *window, char *title, char *message,
char *dest, int dlen, int hide);
@@ -768,6 +770,8 @@ static int gamt_connect(struct gamt_window *gamt)
gamt->redir.cb_data = gamt;
gamt->redir.cb_recv = recv_gtk;
gamt->redir.cb_state = state_gtk;
+ gamt->redir.legacy = amt_legacy;
+ gamt->redir.cacert = amt_cacert;
if (-1 == redir_connect(&gamt->redir))
return -1;
@@ -905,8 +909,16 @@ static void usage(FILE *fp)
" -c color text color\n"
" -b color background color\n"
" -l file logfile\n"
+ " -L use legacy authentication\n"
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
+ " -C cacert enable SSL and use PEM cacert file\n"
+#endif
"\n"
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
+ "By default port 16994 (SSL: 16995) is used.\n"
+#else
"By default port 16994 is used.\n"
+#endif
"If no password is given " APPNAME " will ask for one.\n"
"\n"
"-- \n"
@@ -941,7 +953,7 @@ main(int argc, char *argv[])
fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC);
for (;;) {
- if (-1 == (c = getopt(argc, argv, "hdtu:p:f:c:b:l:")))
+ if (-1 == (c = getopt(argc, argv, "hdtu:p:f:c:b:l:LC:")))
break;
switch (c) {
case 'd':
@@ -960,6 +972,14 @@ main(int argc, char *argv[])
case 'l':
log = optarg;
break;
+ case 'L':
+ amt_legacy = 1;
+ break;
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
+ case 'C':
+ amt_cacert = optarg;
+ break;
+#endif
case 'f':
cfg_set_str(CFG_FONT, optarg);
diff --git a/redir.c b/redir.c
index ad7426f..7943343 100644
--- a/redir.c
+++ b/redir.c
@@ -29,9 +29,12 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <sys/time.h>
#include "tcp.h"
+#include "ssl.h"
#include "redir.h"
+#include "auth.h"
static const char *state_name[] = {
[ REDIR_NONE ] = "NONE",
@@ -100,7 +103,7 @@ static ssize_t redir_write(struct redir *r, const char *buf, size_t count)
if (r->trace)
hexdump("out", buf, count);
- rc = write(r->sock, buf, count);
+ rc = sslwrite(r->ctx, buf, count);
if (-1 == rc)
snprintf(r->err, sizeof(r->err), "write(socket): %s", strerror(errno));
return rc;
@@ -142,8 +145,11 @@ const char *redir_state_desc(enum redir_state state)
int redir_connect(struct redir *r)
{
static unsigned char *defport = "16994";
+ static unsigned char *sslport = "16995";
struct addrinfo ai;
+ if (r->cacert)
+ defport = sslport;
memset(&ai, 0, sizeof(ai));
ai.ai_socktype = SOCK_STREAM;
ai.ai_family = PF_UNSPEC;
@@ -151,6 +157,11 @@ int redir_connect(struct redir *r)
redir_state(r, REDIR_CONNECT);
r->sock = tcp_connect(&ai, NULL, NULL, r->host,
strlen(r->port) ? r->port : defport);
+ r->ctx = sslinit(r->sock, r->cacert);
+ if(r->ctx == NULL) {
+ close(r->sock);
+ r->sock = -1;
+ }
if (-1 == r->sock) {
redir_state(r, REDIR_ERROR);
/* FIXME: better error message */
@@ -179,11 +190,13 @@ int redir_stop(struct redir *r)
redir_state(r, REDIR_CLOSED);
redir_write(r, request, sizeof(request));
+ sslexit(r->ctx);
+ r->ctx = NULL;
close(r->sock);
return 0;
}
-int redir_auth(struct redir *r)
+static int redir_auth_old(struct redir *r)
{
int ulen = strlen(r->user);
int plen = strlen(r->pass);
@@ -205,6 +218,79 @@ int redir_auth(struct redir *r)
return rc;
}
+static int io(void *parm, unsigned char *data, int len, int mode)
+{
+ int rc;
+ struct redir *r;
+ struct timeval tv;
+ fd_set set;
+
+ switch(mode)
+ {
+ case READ:
+ r = (struct redir *)parm;
+ while (len) {
+ FD_ZERO(&set);
+ FD_SET(r->sock,&set);
+ if (!sslready(r->ctx)) {
+ tv.tv_sec = HEARTBEAT_INTERVAL * 4 / 1000;
+ tv.tv_usec = 0;
+ switch (select(r->sock+1,&set,NULL,NULL,&tv)) {
+ case -1:
+ perror("select");
+ return -1;
+ case 0:
+ fprintf(stderr,"select: timeout\n");
+ return -1;
+ }
+ }
+ rc = sslread(r->ctx, data, len);
+ switch (rc) {
+ case -1:
+ fprintf(stderr, "read(socket): %s", strerror(errno));
+ return -1;
+ case 0:
+ fprintf(stderr, "EOF from socket");
+ return -1;
+ default:
+ if (r->trace)
+ hexdump("in ", data, rc);
+ data += rc;
+ len -= rc;
+ }
+ }
+ return 0;
+
+ case WRITE:
+ r = (struct redir *)parm;
+ if (redir_write(r, data, len) != len)
+ return -1;
+ return 0;
+
+ case RANDOM:
+ gettimeofday(&tv, NULL);
+ if (sizeof(tv) <= len)
+ memcpy(data, &tv, sizeof(tv));
+ else
+ memcpy(data, &tv, len);
+ return 0;
+ }
+
+ return -1;
+}
+
+int redir_auth(struct redir *r)
+{
+ int rc;
+
+ if (r->legacy)
+ return redir_auth_old(r);
+
+ redir_state(r, REDIR_AUTH);
+ rc = authenticate(0, r->user, r->pass, io, r);
+ return rc;
+}
+
int redir_sol_start(struct redir *r)
{
unsigned char request[START_SOL_REDIRECTION_LENGTH] = {
@@ -280,7 +366,7 @@ int redir_sol_recv(struct redir *r)
ready yet, but should be here Real Soon Now. */
flags = fcntl(r->sock,F_GETFL);
fcntl(r->sock,F_SETFL, flags & (~O_NONBLOCK));
- count = read(r->sock, msg, count);
+ count = sslread(r->ctx, msg, count);
fcntl(r->sock,F_SETFL, flags);
switch (count) {
@@ -309,12 +395,13 @@ int redir_data(struct redir *r)
{
int rc, bshift;
+repeat:
if (r->trace) {
fprintf(stderr, "in --\n");
if (r->blen)
fprintf(stderr, "in : already have %d\n", r->blen);
}
- rc = read(r->sock, r->buf + r->blen, sizeof(r->buf) - r->blen);
+ rc = sslread(r->ctx, r->buf + r->blen, sizeof(r->buf) - r->blen);
switch (rc) {
case -1:
snprintf(r->err, sizeof(r->err), "read(socket): %s", strerror(errno));
@@ -456,18 +543,24 @@ int redir_data(struct redir *r)
memmove(r->buf, r->buf + bshift, r->blen - bshift);
r->blen -= bshift;
}
+ if (r->ctx && sslready(r->ctx))
+ goto repeat;
return 0;
again:
/* need more data, jump back into poll/select loop */
if (r->trace)
fprintf(stderr, "in : need more data\n");
+ if (sslready(r->ctx))
+ goto repeat;
return 0;
err:
if (r->trace)
fprintf(stderr, "in : ERROR (%s)\n", r->err);
redir_state(r, REDIR_ERROR);
+ sslexit(r->ctx);
+ r->ctx = NULL;
close(r->sock);
return -1;
}
diff --git a/redir.h b/redir.h
index 070559d..8520d58 100644
--- a/redir.h
+++ b/redir.h
@@ -26,6 +26,7 @@ struct redir {
unsigned char type[4];
int verbose;
int trace;
+ int legacy;
enum redir_state state;
unsigned char err[128]; // state == REDIR_ERROR
@@ -33,6 +34,9 @@ struct redir {
unsigned char buf[64];
unsigned int blen;
+ void *cacert;
+ void *ctx;
+
/* callbacks */
void *cb_data;
void (*cb_state)(void *cb_data, enum redir_state old, enum redir_state new);
diff --git a/ssl.c b/ssl.c
new file mode 100644
index 0000000..122a157
--- /dev/null
+++ b/ssl.c
@@ -0,0 +1,452 @@
+/*
+ * SSL helper functions.
+ *
+ * Copyright (C) 2014 Andreas Steinmetz <ast@domdv.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(USE_OPENSSL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#elif defined(USE_GNUTLS)
+#include <gnutls/gnutls.h>
+#endif
+
+#include "ssl.h"
+
+struct ctx
+{
+ int fd;
+#if defined(USE_OPENSSL)
+ SSL *ssl;
+ SSL_CTX *ctx;
+#elif defined(USE_GNUTLS)
+ gnutls_session_t ssl;
+ gnutls_certificate_credentials_t cred;
+#endif
+};
+
+static struct ctx *newctx(int fd)
+{
+ struct ctx *ctx;
+
+ if(!(ctx=malloc(sizeof(struct ctx))))
+ {
+ perror("malloc");
+ return NULL;
+ }
+
+ memset(ctx,0,sizeof(struct ctx));
+
+ ctx->fd=fd;
+
+ return ctx;
+}
+
+#if defined(USE_OPENSSL)
+
+struct ctx *sslinit(int fd,char *cacert)
+{
+ int r;
+ int c=0;
+ struct ctx *ctx;
+
+ if(!(ctx=newctx(fd)))return NULL;
+
+ if(!cacert)return ctx;
+
+ SSL_load_error_strings();
+ SSL_library_init();
+
+ if(!(ctx->ctx=SSL_CTX_new(SSLv23_client_method())))
+ {
+ ERR_print_errors_fp(stderr);
+ goto err1;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ SSL_CTX_set_options(ctx->ctx,
+ SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+#endif
+
+ if(!SSL_CTX_load_verify_locations(ctx->ctx,cacert,NULL))
+ {
+ ERR_print_errors_fp(stderr);
+ goto err2;
+ }
+
+ SSL_CTX_set_verify_depth(ctx->ctx,5);
+ SSL_CTX_set_verify(ctx->ctx,SSL_VERIFY_PEER,NULL);
+
+ if(!(ctx->ssl=SSL_new(ctx->ctx)))
+ {
+ ERR_print_errors_fp(stderr);
+ goto err2;
+ }
+
+ if(!SSL_set_fd(ctx->ssl,ctx->fd))
+ {
+ ERR_print_errors_fp(stderr);
+ goto err3;
+ }
+
+repeat: if((r=SSL_connect(ctx->ssl))!=1)
+ {
+ switch(SSL_get_error(ctx->ssl,r))
+ {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if(++c<100)
+ {
+ usleep(10000);
+ goto repeat;
+ }
+ }
+ ERR_print_errors_fp(stderr);
+ goto err3;
+ }
+
+ return ctx;
+
+err3: SSL_free(ctx->ssl);
+err2: SSL_CTX_free(ctx->ctx);
+err1: free(ctx);
+ return NULL;
+}
+
+void sslexit(struct ctx *ctx)
+{
+ if(ctx->ssl)
+ {
+ SSL_shutdown(ctx->ssl);
+ SSL_free(ctx->ssl);
+ SSL_CTX_free(ctx->ctx);
+ }
+ free(ctx);
+}
+
+int sslready(struct ctx *ctx)
+{
+ if(ctx->ssl)return SSL_pending(ctx->ssl);
+ else return 0;
+}
+
+ssize_t sslread(struct ctx *ctx,void *buf,size_t count)
+{
+ int l;
+ int c=0;
+
+ if(!ctx->ssl)return read(ctx->fd,buf,count);
+ if(!count)return 0;
+
+repeat: if((l=SSL_read(ctx->ssl,buf,count))>0)return l;
+
+ switch(SSL_get_error(ctx->ssl,l))
+ {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if(++c<100)
+ {
+ usleep(10000);
+ goto repeat;
+ }
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return -1;
+ case SSL_ERROR_ZERO_RETURN:
+ return 0;
+ case SSL_ERROR_SSL:
+ ERR_print_errors_fp(stderr);
+ }
+
+ return -1;
+}
+
+ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count)
+{
+ int l;
+ int c=0;
+
+ if(!ctx->ssl)return write(ctx->fd,buf,count);
+ if(!count)return 0;
+
+repeat: if((l=SSL_write(ctx->ssl,buf,count))>0)return l;
+
+ switch(SSL_get_error(ctx->ssl,l))
+ {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if(++c<100)
+ {
+ usleep(10000);
+ goto repeat;
+ }
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return -1;
+ case SSL_ERROR_ZERO_RETURN:
+ return 0;
+ case SSL_ERROR_SSL:
+ ERR_print_errors_fp(stderr);
+ }
+
+ return -1;
+}
+
+#elif defined(USE_GNUTLS)
+
+static int vrycb(gnutls_session_t ssl)
+{
+ int r;
+ int type;
+ unsigned int status;
+ gnutls_datum_t msg;
+
+ if((r=gnutls_certificate_verify_peers3(ssl,NULL,&status))<0)
+ {
+ fprintf(stderr,"gnutls_certificate_verify_peers3: %s\n",
+ gnutls_strerror(r));
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ if(status)
+ {
+ type=gnutls_certificate_type_get(ssl);
+ if((r=gnutls_certificate_verification_status_print(status,type,
+ &msg,0))<0)
+ {
+ fprintf(stderr,"gnutls_certificate_verification_"
+ "status_print %s\n",gnutls_strerror(r));
+ }
+ else
+ {
+ fprintf(stderr,"certificate status: %s\n",msg.data);
+ gnutls_free(msg.data);
+ }
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ return 0;
+}
+
+struct ctx *sslinit(int fd,char *cacert)
+{
+ int r;
+ const char *e;
+ struct ctx *ctx;
+
+ if(!(ctx=newctx(fd)))return NULL;
+
+ if(!cacert)return ctx;
+
+ if((r=gnutls_global_init()))
+ {
+ fprintf(stderr,"gnutls_global_init: %s\n",gnutls_strerror(r));
+ goto err1;
+ }
+
+ if((r=gnutls_certificate_allocate_credentials(&ctx->cred)))
+ {
+ fprintf(stderr,"gnutls_certificate_allocate_credentials: "
+ "%s\n",gnutls_strerror(r));
+ goto err2;
+ }
+
+ if((r=gnutls_certificate_set_x509_trust_file(ctx->cred,cacert,
+ GNUTLS_X509_FMT_PEM))<0)
+ {
+ fprintf(stderr,"gnutls_certificate_set_x509_trust_file: "
+ "%s\n",gnutls_strerror(r));
+ goto err3;
+ }
+
+ gnutls_certificate_set_verify_function(ctx->cred,vrycb);
+
+ if((r=gnutls_init(&ctx->ssl,GNUTLS_CLIENT)))
+ {
+ fprintf(stderr,"gnutls_init: %s\n",gnutls_strerror(r));
+ goto err3;
+ }
+
+ /* oh well, isn't _that_ easy ?!? :-( ... compare to openssl ... */
+ if((r=gnutls_priority_set_direct(ctx->ssl,"NONE:+AES-256-CBC:"
+ "+AES-128-CBC:+3DES-CBC:+COMP-NULL:+CTYPE-X509:+VERS-SSL3.0:"
+ "+SHA256:+SHA1:+RSA:%UNSAFE_RENEGOTIATION",&e)))
+ {
+ fprintf(stderr,"gnutls_priority_set_direct: %s\n",
+ gnutls_strerror(r));
+ if(r==GNUTLS_E_INVALID_REQUEST)
+ fprintf(stderr,"additional info: %s\n",e);
+ goto err4;
+ }
+
+ if((r=gnutls_credentials_set(ctx->ssl,GNUTLS_CRD_CERTIFICATE,
+ ctx->cred)))
+ {
+ fprintf(stderr,"gnutls_credentials_set: %s\n",
+ gnutls_strerror(r));
+ goto err4;
+ }
+
+ gnutls_transport_set_int(ctx->ssl,ctx->fd);
+
+ gnutls_handshake_set_timeout(ctx->ssl,GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+ do
+ {
+ r=gnutls_handshake(ctx->ssl);
+ } while(r<0&&!gnutls_error_is_fatal(r));
+ if(r<0)
+ {
+ fprintf(stderr,"gnutls_handshake: %s\n",gnutls_strerror(r));
+ goto err4;
+ }
+
+ return ctx;
+
+err4: gnutls_deinit(ctx->ssl);
+err3: gnutls_certificate_free_credentials(ctx->cred);
+err2: gnutls_global_deinit();
+err1: free(ctx);
+ return NULL;
+}
+
+void sslexit(struct ctx *ctx)
+{
+ if(ctx->ssl)
+ {
+ gnutls_deinit(ctx->ssl);
+ gnutls_certificate_free_credentials(ctx->cred);
+ gnutls_global_deinit();
+ }
+ free(ctx);
+}
+
+int sslready(struct ctx *ctx)
+{
+ if(ctx->ssl)return gnutls_record_check_pending(ctx->ssl);
+ else return 0;
+}
+
+ssize_t sslread(struct ctx *ctx,void *buf,size_t count)
+{
+ ssize_t l;
+ int c=0;
+ int r;
+
+ if(!ctx->ssl)return read(ctx->fd,buf,count);
+ if(!count)return 0;
+
+repeat: if((l=gnutls_record_recv(ctx->ssl,buf,count))>0)return l;
+
+ switch(l)
+ {
+ case GNUTLS_E_REHANDSHAKE:
+ do
+ {
+ r=gnutls_handshake(ctx->ssl);
+ } while(r<0&&!gnutls_error_is_fatal(r));
+ if(r<0)
+ {
+ fprintf(stderr,"gnutls_handshake: %s\n",
+ gnutls_strerror(r));
+ return -1;
+ }
+ case GNUTLS_E_INTERRUPTED:
+ case GNUTLS_E_AGAIN:
+ if(++c<100)
+ {
+ usleep(10000);
+ goto repeat;
+ }
+ default:fprintf(stderr,"gnutls_record_recv: %s\n",gnutls_strerror(l));
+ case GNUTLS_E_PUSH_ERROR:
+ case GNUTLS_E_PULL_ERROR:
+ return -1;
+ }
+}
+
+ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count)
+{
+ ssize_t l;
+ int c=0;
+ int r;
+
+ if(!ctx->ssl)return write(ctx->fd,buf,count);
+ if(!count)return 0;
+
+repeat: if((l=gnutls_record_send(ctx->ssl,buf,count))>0)return l;
+
+ switch(l)
+ {
+ case GNUTLS_E_REHANDSHAKE:
+ do
+ {
+ r=gnutls_handshake(ctx->ssl);
+ } while(r<0&&!gnutls_error_is_fatal(r));
+ if(r<0)
+ {
+ fprintf(stderr,"gnutls_handshake: %s\n",
+ gnutls_strerror(r));
+ return -1;
+ }
+ case GNUTLS_E_INTERRUPTED:
+ case GNUTLS_E_AGAIN:
+ if(++c<100)
+ {
+ usleep(10000);
+ goto repeat;
+ }
+ default:fprintf(stderr,"gnutls_record_send: %s\n",gnutls_strerror(l));
+ case GNUTLS_E_PUSH_ERROR:
+ case GNUTLS_E_PULL_ERROR:
+ return -1;
+ }
+}
+
+#else
+
+struct ctx *sslinit(int fd,char *cacert)
+{
+ return newctx(fd);
+}
+
+void sslexit(struct ctx *ctx)
+{
+ free(ctx);
+}
+
+int sslready(struct ctx *ctx)
+{
+ return 0;
+}
+
+ssize_t sslread(struct ctx *ctx,void *buf,size_t count)
+{
+ return read(ctx->fd,buf,count);
+}
+
+ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count)
+{
+ return write(ctx->fd,buf,count);
+}
+
+#endif
diff --git a/ssl.h b/ssl.h
new file mode 100644
index 0000000..19c0d58
--- /dev/null
+++ b/ssl.h
@@ -0,0 +1,7 @@
+struct ctx;
+
+extern struct ctx *sslinit(int fd,char *cacert);
+extern void sslexit(struct ctx *ctx);
+extern int sslready(struct ctx *ctx);
+extern ssize_t sslread(struct ctx *ctx,void *buf,size_t count);
+extern ssize_t sslwrite(struct ctx *ctx,const void *buf,size_t count);