diff options
-rw-r--r-- | GNUmakefile | 17 | ||||
-rw-r--r-- | amtterm.c | 18 | ||||
-rw-r--r-- | auth.c | 826 | ||||
-rw-r--r-- | auth.h | 6 | ||||
-rw-r--r-- | gamt.c | 22 | ||||
-rw-r--r-- | redir.c | 101 | ||||
-rw-r--r-- | redir.h | 4 | ||||
-rw-r--r-- | ssl.c | 452 | ||||
-rw-r--r-- | ssl.h | 7 |
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 ################################################################# @@ -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); @@ -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; +} @@ -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); @@ -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); @@ -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; } @@ -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); @@ -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 @@ -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); |