Main Page   Class Hierarchy   Compound List   File List   Compound Members  

auth.c

00001 /*
00002  *  This program is free software; you can redistribute it and/or modify
00003  *  it under the terms of the GNU General Public License as published by
00004  *  the Free Software Foundation; either version 2 of the License, or
00005  *  (at your option) any later version.
00006  *
00007  *  This program is distributed in the hope that it will be useful,
00008  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00009  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010  *  GNU General Public License for more details.
00011  *
00012  *  You should have received a copy of the GNU General Public License
00013  *  along with this program; if not, write to the Free Software
00014  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015  *
00016  * (c)Copyright 2006 Hewlett-Packard Development Company, LP.
00017  *
00018  *  Extracted from sipp project : http://sipp.sourceforge.net
00019  *
00020  */
00021 
00022 #ifdef __FreeBSD__
00023 #include <sys/types.h>
00024 #endif
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <ctype.h>
00029 #include <openssl/md5.h>
00030 #include "milenage.h"
00031 
00032 #define MAX_HEADER_LEN  2049
00033 #define MD5_HASH_SIZE 16
00034 #define HASH_HEX_SIZE 2*MD5_HASH_SIZE
00035 
00036 
00037 /* AKA */
00038 
00039 #define KLEN 16
00040 typedef u_char K[KLEN];
00041 #define RANDLEN 16
00042 typedef u_char RAND[RANDLEN];
00043 #define AUTNLEN 16
00044 typedef u_char AUTN[AUTNLEN];
00045 
00046 #define AKLEN 6
00047 typedef u_char AK[AKLEN];
00048 #define AMFLEN 2
00049 typedef u_char AMF[AMFLEN];
00050 #define MACLEN 8
00051 typedef u_char MAC[MACLEN];
00052 #define CKLEN 16
00053 typedef u_char CK[CKLEN];
00054 #define IKLEN 16
00055 typedef u_char IK[IKLEN];
00056 #define SQNLEN 6
00057 typedef u_char SQN[SQNLEN];
00058 #define AUTSLEN 14
00059 typedef char AUTS[AUTSLEN];
00060 #define AUTS64LEN 29
00061 typedef char AUTS64[AUTS64LEN];
00062 #define RESLEN 8
00063 typedef unsigned char RES[RESLEN+1];
00064 #define RESHEXLEN 17
00065 typedef char RESHEX[RESHEXLEN];
00066 #define OPLEN 16
00067 typedef u_char OP[OPLEN];
00068 
00069 AMF amfstar= { '\0' , '\0'} ;
00070 SQN sqn_he={0x00,0x00,0x00,0x00,0x00,0x00};
00071 
00072 /* end AKA */
00073 
00074 
00075 int createAuthHeaderMD5(char * user, char * password, char * method,
00076                      char * uri, char * msgbody, char * auth, 
00077                      char * algo, char * result);
00078 int createAuthHeaderAKAv1MD5(char * user, char * OP,
00079                              char * AMF,
00080                              char * K,
00081                              char * method,
00082                              char * uri, char * msgbody, char * auth, char *algo,
00083                              char * result);
00084 
00085 
00086 /* This function is from RFC 2617 Section 5 */
00087 
00088 void hashToHex (unsigned char *_b, unsigned char *_h)
00089 {
00090   unsigned short i;
00091   unsigned char j;
00092   
00093   for (i = 0; i < MD5_HASH_SIZE; i++) {
00094     j = (_b[i] >> 4) & 0xf;
00095     if (j <= 9) {
00096       _h[i * 2] = (j + '0');
00097     } else {
00098       _h[i * 2] = (j + 'a' - 10);
00099     }
00100     j = _b[i] & 0xf;
00101     if (j <= 9) {
00102       _h[i * 2 + 1] = (j + '0');
00103     } else {
00104       _h[i * 2 + 1] = (j + 'a' - 10);
00105     }
00106   };
00107   _h[HASH_HEX_SIZE] = '\0';
00108 }
00109 
00110 char *stristr (const char *s1, const char *s2) {
00111   char *cp = (char*) s1;
00112   char *p1, *p2, *endp;
00113   char l, r;
00114   
00115   endp = (char*)s1 + (strlen(s1) - strlen(s2)) ;
00116   while (*cp && (cp <= endp)) {
00117     p1 = cp;
00118     p2 = (char*)s2;
00119     while (*p1 && *p2) {
00120       l = toupper(*p1);
00121       r = toupper(*p2); 
00122       if (l != r) {
00123         break;
00124       }
00125       p1++;
00126       p2++;
00127     }
00128     if (*p2 == 0) {
00129       return cp;
00130     }
00131     cp++;
00132   }
00133   return 0;
00134 }
00135 
00136 int createAuthHeader(char * user, char * password, char * method,
00137                      char * uri, char * msgbody, char * auth, 
00138                      char * aka_OP,
00139                      char * aka_AMF,
00140                      char * aka_K,
00141                      char * result) {
00142   
00143   char algo[32]="MD5";
00144   char *start, *end;
00145   
00146   if ((start = stristr(auth, "Digest")) == NULL) {
00147     sprintf(result, "createAuthHeader: authentication must be digest");
00148     return 0;
00149   }
00150   
00151   if ((start = stristr(auth, "algorithm=")) != NULL) {
00152     start = start + strlen("algorithm=");
00153     if (*start == '"') { start++; }
00154     end = start + strcspn(start, " ,\"\r\n");
00155     strncpy(algo, start, end - start);
00156     algo[end - start] ='\0';
00157     
00158   }
00159   
00160   if (strncasecmp(algo, "MD5", 3)==0) {
00161     return createAuthHeaderMD5(user,password,method,uri,msgbody,auth,algo,result);
00162   } else if (strncasecmp(algo, "AKAv1-MD5", 9)==0) {
00163     return createAuthHeaderAKAv1MD5(user, aka_OP,
00164                                     aka_AMF,
00165                                     aka_K,
00166                                     method,uri,msgbody,auth,algo,result);        
00167   }else{
00168     sprintf(result, "createAuthHeader: authentication must use MD5 or AKAv1-MD5");
00169     return 0;
00170   }
00171 }
00172 
00173 
00174 int createAuthHeaderMD5(char * user, char * password, char * method,
00175                         char * uri, char * msgbody, char * auth, 
00176                         char * algo, char * result) {
00177   
00178   unsigned char ha1[MD5_HASH_SIZE], ha2[MD5_HASH_SIZE];
00179   unsigned char resp[MD5_HASH_SIZE], body[MD5_HASH_SIZE]; 
00180   unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1];
00181   unsigned char resp_hex[HASH_HEX_SIZE+1], body_hex[HASH_HEX_SIZE+1];
00182   char tmp[MAX_HEADER_LEN], authtype[16], cnonce[32], nc[32], opaque[64];
00183   char *start, *end;
00184   static unsigned int mync = 1;
00185   int has_opaque = 0;
00186   MD5_CTX Md5Ctx;
00187   
00188   // Extract the Auth Type - If not present, using 'none' 
00189   cnonce[0] = '\0';
00190   authtype[0] = '\0';
00191   if ((start = stristr(auth, "qop=")) != NULL) {
00192     start = start + strlen("qop=");
00193     if (*start == '"') { start++; }
00194     end = start + strcspn(start, " ,\"\r\n");
00195     strncpy(authtype, start, end - start);
00196     authtype[end - start] ='\0';
00197     sprintf(cnonce, "%x", rand());
00198     sprintf(nc, "%08x", mync);
00199   }
00200   
00201   // Extract the Opaque value - if present
00202   opaque[0] = '\0';
00203   if ((start = stristr(auth, "opaque=")) != NULL) {
00204     start = start + strlen("opaque=");
00205     if (*start == '"') { start++; }
00206     end = start + strcspn(start, " ,\"\r\n");
00207     strncpy(opaque, start, end - start);
00208     opaque[end - start] ='\0';
00209     has_opaque = 1;
00210   }
00211   
00212   // Extract the Realm 
00213   if ((start = stristr(auth, "realm=")) == NULL) {
00214     sprintf(result, "createAuthHeaderMD5: couldn't parse realm in '%s'", auth);
00215     return 0;
00216   }
00217   start = start + strlen("realm=");
00218   if (*start == '"') { start++; }       
00219   end = start + strcspn(start, ",\"\r\n");
00220   strncpy(tmp, start, end - start);
00221   tmp[end - start] ='\0';
00222   
00223   // Load in A1 
00224   MD5_Init(&Md5Ctx);
00225   MD5_Update(&Md5Ctx, user, strlen(user));
00226   MD5_Update(&Md5Ctx, ":", 1);
00227   MD5_Update(&Md5Ctx, tmp, strlen(tmp));
00228   MD5_Update(&Md5Ctx, ":", 1);
00229   MD5_Update(&Md5Ctx, password, strlen(password));
00230   MD5_Final(ha1, &Md5Ctx);
00231   hashToHex(&ha1[0], &ha1_hex[0]);
00232   
00233   sprintf(result, "Digest username=\"%s\",realm=\"%s\"",user,tmp);
00234   if (cnonce[0] != '\0') {
00235     sprintf(result, "%s,cnonce=\"%s\",nc=%s,qop=%s",result,cnonce,nc,authtype);
00236   }
00237   
00238   // Construct the URI 
00239 
00240   sprintf(tmp, "%s", uri);
00241   
00242   // If using Auth-Int make a hash of the body - which is NULL for REG 
00243   if (stristr(authtype, "auth-int") != NULL) {
00244     MD5_Init(&Md5Ctx);
00245     MD5_Update(&Md5Ctx, msgbody, strlen(msgbody));
00246     MD5_Final(body, &Md5Ctx);
00247     hashToHex(&body[0], &body_hex[0]);
00248   }
00249   
00250   // Load in A2 
00251   MD5_Init(&Md5Ctx);
00252   MD5_Update(&Md5Ctx, method, strlen(method));
00253   MD5_Update(&Md5Ctx, ":", 1);
00254   MD5_Update(&Md5Ctx, tmp, strlen(tmp));
00255   if (stristr(authtype, "auth-int") != NULL) {
00256     MD5_Update(&Md5Ctx, ":", 1);
00257     MD5_Update(&Md5Ctx, &body_hex, HASH_HEX_SIZE);
00258   }
00259   MD5_Final(ha2, &Md5Ctx);
00260   hashToHex(&ha2[0], &ha2_hex[0]);
00261   
00262   sprintf(result, "%s,uri=\"%s\"",result,tmp);
00263   
00264   // Extract the Nonce 
00265   if ((start = stristr(auth, "nonce=")) == NULL) {
00266     sprintf(result, "createAuthHeader: couldn't parse nonce");
00267     return 0;
00268   }
00269   start = start + strlen("nonce=");
00270   if (*start == '"') { start++; }
00271   end = start + strcspn(start, " ,\"\r\n");
00272   strncpy(tmp, start, end - start);
00273   tmp[end - start] ='\0';
00274   
00275   MD5_Init(&Md5Ctx);
00276   MD5_Update(&Md5Ctx, &ha1_hex, HASH_HEX_SIZE);
00277   MD5_Update(&Md5Ctx, ":", 1);
00278   MD5_Update(&Md5Ctx, tmp, strlen(tmp));
00279   if (cnonce[0] != '\0') {
00280     MD5_Update(&Md5Ctx, ":", 1);
00281     MD5_Update(&Md5Ctx, nc, strlen(nc));
00282     MD5_Update(&Md5Ctx, ":", 1);
00283     MD5_Update(&Md5Ctx, cnonce, strlen(cnonce));
00284     MD5_Update(&Md5Ctx, ":", 1);
00285     MD5_Update(&Md5Ctx, authtype, strlen(authtype));
00286   }
00287   MD5_Update(&Md5Ctx, ":", 1);
00288   MD5_Update(&Md5Ctx, &ha2_hex, HASH_HEX_SIZE);
00289   MD5_Final(resp, &Md5Ctx);
00290   hashToHex(&resp[0], &resp_hex[0]);
00291   
00292   sprintf(result, "%s,nonce=\"%s\",response=\"%s\",algorithm=%s",result,tmp,resp_hex,algo);
00293   
00294   if (has_opaque) {
00295     sprintf(result, "%s,opaque=\"%s\"",result,opaque);
00296   }
00297   
00298   return 1;
00299 }
00300 
00301 
00302 static int base64_val(char x)\
00303 {
00304         switch(x){
00305                 case '=': return -1;
00306                 case 'A': return 0;
00307                 case 'B': return 1;
00308                 case 'C': return 2;
00309                 case 'D': return 3;
00310                 case 'E': return 4;
00311                 case 'F': return 5;
00312                 case 'G': return 6;
00313                 case 'H': return 7;
00314                 case 'I': return 8;
00315                 case 'J': return 9;
00316                 case 'K': return 10;
00317                 case 'L': return 11;
00318                 case 'M': return 12;
00319                 case 'N': return 13;
00320                 case 'O': return 14;
00321                 case 'P': return 15;
00322                 case 'Q': return 16;
00323                 case 'R': return 17;
00324                 case 'S': return 18;
00325                 case 'T': return 19;
00326                 case 'U': return 20;
00327                 case 'V': return 21;
00328                 case 'W': return 22;
00329                 case 'X': return 23;
00330                 case 'Y': return 24;
00331                 case 'Z': return 25;
00332                 case 'a': return 26;
00333                 case 'b': return 27;
00334                 case 'c': return 28;
00335                 case 'd': return 29;
00336                 case 'e': return 30;
00337                 case 'f': return 31;
00338                 case 'g': return 32;
00339                 case 'h': return 33;
00340                 case 'i': return 34;
00341                 case 'j': return 35;
00342                 case 'k': return 36;
00343                 case 'l': return 37;
00344                 case 'm': return 38;
00345                 case 'n': return 39;
00346                 case 'o': return 40;
00347                 case 'p': return 41;
00348                 case 'q': return 42;
00349                 case 'r': return 43;
00350                 case 's': return 44;
00351                 case 't': return 45;
00352                 case 'u': return 46;
00353                 case 'v': return 47;
00354                 case 'w': return 48;
00355                 case 'x': return 49;
00356                 case 'y': return 50;
00357                 case 'z': return 51;
00358                 case '0': return 52;
00359                 case '1': return 53;
00360                 case '2': return 54;
00361                 case '3': return 55;
00362                 case '4': return 56;
00363                 case '5': return 57;
00364                 case '6': return 58;
00365                 case '7': return 59;
00366                 case '8': return 60;
00367                 case '9': return 61;
00368                 case '+': return 62;
00369                 case '/': return 63;
00370         }
00371         return 0;
00372 }
00373 
00374 char * base64_decode_string( const char *buf, unsigned int len, int *newlen )
00375 {
00376   unsigned int i ;
00377   int j,x1,x2,x3,x4;
00378   char *out;
00379   out = (char *)malloc( ( len * 3/4 ) + 8 );
00380   for(i=0,j=0;i+3<len;i+=4){
00381     x1=base64_val(buf[i]);
00382     x2=base64_val(buf[i+1]);
00383     x3=base64_val(buf[i+2]);
00384     x4=base64_val(buf[i+3]);
00385     out[j++]=(x1<<2) | ((x2 & 0x30)>>4);
00386     out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2);
00387     out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F);
00388   }
00389   if (i<len) {
00390     x1 = base64_val(buf[i]);
00391     if (i+1<len)
00392       x2=base64_val(buf[i+1]);
00393     else 
00394       x2=-1;
00395     if (i+2<len)                
00396       x3=base64_val(buf[i+2]);
00397     else
00398       x3=-1;
00399     if(i+3<len) 
00400       x4=base64_val(buf[i+3]);
00401     else x4=-1;
00402     if (x2!=-1) {
00403       out[j++]=(x1<<2) | ((x2 & 0x30)>>4);
00404       if (x3==-1) {
00405         out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2);
00406         if (x4==-1) {
00407           out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F);
00408         }
00409       }
00410     }
00411     
00412   }
00413   
00414   out[j++] = 0;
00415   *newlen=j;
00416   return out;
00417 }
00418 
00419 
00420 char base64[64]= {
00421   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q',
00422   'R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h',
00423   'i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y',
00424   'z','0','1','2','3','4','5','6','7','8','9','+','/' } ;
00425 
00426 char * base64_encode_string( const char *buf, unsigned int len, int *newlen )
00427 {
00428   int i,k;
00429   int triplets,rest;
00430   char *out,*ptr;       
00431   
00432   triplets = len/3;
00433   rest = len%3;
00434   out = (char *)malloc( ( triplets * 4 ) + 8 );
00435   
00436   ptr = out;
00437   for(i=0;i<triplets*3;i+=3){
00438     k = (((unsigned char) buf[i])&0xFC)>>2;
00439     *ptr=base64[k];ptr++;
00440     
00441     k = (((unsigned char) buf[i])&0x03)<<4;
00442     k |=(((unsigned char) buf[i+1])&0xF0)>>4;
00443     *ptr=base64[k];ptr++;
00444     
00445     k = (((unsigned char) buf[i+1])&0x0F)<<2;
00446     k |=(((unsigned char) buf[i+2])&0xC0)>>6;
00447     *ptr=base64[k];ptr++;
00448     
00449     k = (((unsigned char) buf[i+2])&0x3F);
00450     *ptr=base64[k];ptr++;
00451   }
00452   i=triplets*3;
00453   switch(rest){
00454   case 0:
00455     break;
00456   case 1:
00457     k = (((unsigned char) buf[i])&0xFC)>>2;
00458     *ptr=base64[k];ptr++;
00459     
00460     k = (((unsigned char) buf[i])&0x03)<<4;
00461     *ptr=base64[k];ptr++;
00462     
00463     *ptr='=';ptr++;
00464     
00465     *ptr='=';ptr++;
00466     break;
00467   case 2:
00468     k = (((unsigned char) buf[i])&0xFC)>>2;
00469     *ptr=base64[k];ptr++;
00470     
00471     k = (((unsigned char) buf[i])&0x03)<<4;
00472     k |=(((unsigned char) buf[i+1])&0xF0)>>4;
00473     *ptr=base64[k];ptr++;
00474     
00475     k = (((unsigned char) buf[i+1])&0x0F)<<2;
00476     *ptr=base64[k];ptr++;
00477     
00478     *ptr='=';ptr++;
00479     break;
00480   }
00481 
00482   *newlen = ptr-out;
00483   return out;
00484 }
00485 
00486 
00487 
00488 
00489 char hexa[16]= { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' } ;
00490 
00491 int createAuthHeaderAKAv1MD5(char * user, char * aka_OP, 
00492                              char * aka_AMF, 
00493                              char * aka_K, 
00494                              char * method,
00495                              char * uri, char * msgbody, char * auth, char *algo,
00496                              char * result) {
00497                         
00498   char tmp[MAX_HEADER_LEN] ; 
00499   char *start, *end;
00500   int has_auts = 0, resuf = 1;
00501   char *nonce64, *nonce;
00502   int noncelen;
00503   RESHEX resp_hex;
00504   AMF amf;
00505   OP op;
00506   RAND rnd;
00507   AUTS auts_bin;
00508   AUTS64 auts_hex;
00509   MAC mac,xmac;
00510   SQN sqn, sqnxoraka, sqn_ms;
00511   K k;
00512   RES res;
00513   CK ck;
00514   IK ik;
00515   AK ak;
00516   int i;
00517 
00518   // Extract the Nonce 
00519   if ((start = stristr(auth, "nonce=")) == NULL) {
00520       sprintf(result, "createAuthHeaderAKAv1MD5: couldn't parse nonce");
00521       return 0;
00522   }
00523   start = start + strlen("nonce=");
00524   if (*start == '"') { start++; }
00525   end = start + strcspn(start, " ,\"\r\n");
00526   strncpy(tmp, start, end - start);
00527   tmp[end - start] ='\0';
00528 
00529   /* Compute the AKA RES */
00530   resp_hex[0]=0;
00531   nonce64 = tmp;
00532   nonce = base64_decode_string(nonce64,end-start,&noncelen);
00533   if (noncelen<RANDLEN+AUTNLEN) {
00534     sprintf(result,"createAuthHeaderAKAv1MD5 : Nonce is too short %d < %d expected \n",
00535     noncelen,RANDLEN+AUTNLEN);
00536     return 0;
00537   }
00538   memcpy(rnd,nonce,RANDLEN);
00539   memcpy(sqnxoraka,nonce+RANDLEN,SQNLEN);
00540   memcpy(mac,nonce+RANDLEN+SQNLEN+AMFLEN,MACLEN);
00541   memcpy(k,aka_K,KLEN);
00542   memcpy(amf,aka_AMF,AMFLEN);
00543   memcpy(op,aka_OP,OPLEN);
00544 
00545   /* Compute the AK, response and keys CK IK */
00546   f2345(k,rnd,res,ck,ik,ak,op);
00547   res[RESLEN]='\0';
00548 
00549   /* Compute sqn encoded in AUTN */
00550   for (i=0; i < SQNLEN; i++)
00551     sqn[i] = sqnxoraka[i] ^ ak[i];
00552 
00553   /* compute XMAC */
00554   f1(k,rnd,sqn,(unsigned char*)aka_AMF,xmac,op);
00555   if (memcmp(mac,xmac,MACLEN)!=0) {
00556     sprintf(result,"createAuthHeaderAKAv1MD5 : MAC != eXpectedMAC -> Server might not know the secret (man-in-the-middle attack?) \n");
00557     //return 0;
00558   }
00559 
00560   /* Check SQN, compute AUTS if needed and authorization parameter */
00561   /* the condition below is wrong.
00562    * Should trigger synchronization when sqn_ms>>3!=sqn_he>>3 for example.
00563    * Also, we need to store the SQN per user or put it as auth parameter. */
00564   if (1/*sqn[5] > sqn_he[5]*/) {
00565     sqn_he[5] = sqn[5];
00566     has_auts = 0;
00567     /* RES has to be used as password to compute response */
00568     for(i=0;i<RESLEN;i++){
00569       resp_hex[2*i]=hexa[(res[i]&0xF0)>>4];
00570       resp_hex[2*i+1]=hexa[res[i]&0x0F];
00571     }
00572     resp_hex[RESLEN*2]=0;
00573     resuf = createAuthHeaderMD5(user, resp_hex, method, uri, msgbody, auth, algo, result);   
00574   } else {
00575     sqn_ms[5] = sqn_he[5] + 1;
00576     f5star(k, rnd, ak, op);
00577     for(i=0; i<SQNLEN; i++)
00578       auts_bin[i]=sqn_ms[i]^ak[i];
00579     f1star(k, rnd, sqn_ms, amf, (unsigned char*)auts_bin+SQNLEN, op);
00580     has_auts = 1;
00581     /* When re-synchronisation occurs an empty password has to be used */
00582     /* to compute MD5 response (Cf. rfc 3310 section 3.2) */
00583     resuf=createAuthHeaderMD5(user,"",method,uri,msgbody,auth,algo,result);
00584   }
00585   if (has_auts) {
00586     /* Format data for output in the SIP message */
00587     for(i=0;i<AUTSLEN;i++){
00588       auts_hex[2*i]=hexa[(auts_bin[i]&0xF0)>>4];
00589       auts_hex[2*i+1]=hexa[auts_bin[i]&0x0F];
00590     }
00591     auts_hex[AUTS64LEN-1]=0;
00592     
00593     sprintf(result, "%s,auts=\"%s\"",result,auts_hex);
00594   }
00595   
00596   return 1;
00597 }
00598 

Generated on Wed Mar 7 14:44:49 2007 for Seagull by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002