/* * Authentication helper functions. * * Copyright (C) 2014 Andreas Steinmetz * * 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 #include #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<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; }