diff options
Diffstat (limited to 'auth.c')
-rw-r--r-- | auth.c | 826 |
1 files changed, 826 insertions, 0 deletions
@@ -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; +} |