1 /*
2  * Copyright (C) 2011, 2012, 2013 Citrix Systems
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the project nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <err.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <getopt.h>
38 #include <stddef.h>
39 
40 #include "ns_turn_utils.h"
41 #include "apputils.h"
42 #include "stun_buffer.h"
43 
44 ////////////////////////////////////////////////////
45 
46 #define OAUTH_TOKEN_SIZE 1000 //TODO: find insted of 1000 the real max of encoded token length
47 #define OAUTH_MAC_KEY_SIZE 32
48 #define OAUTH_LTK_ID_SIZE 32
49 #define OAUTH_LTK_SIZE 32
50 #define OAUTH_LTK_BASE64ENCODED_SIZE 44
51 #define OAUTH_TOKEN_LIFETIME 3600
52 #define OAUTH_AS_RS_ALG_SIZE 7
53 #define OAUTH_SERVER_NAME_SIZE 255
54 #define OAUTH_GCM_NONCE_BASE64ENCODED_SIZE 16
55 #define OAUTH_HMAC_ALG_SIZE 20
56 
57 
setup_ikm_key(const char * kid,const char * ikm_key,const turn_time_t key_timestamp,const turn_time_t key_lifetime,const char * as_rs_alg,oauth_key * key)58 static int setup_ikm_key(const char *kid,
59                         const char *ikm_key,
60                         const turn_time_t key_timestamp,
61                         const turn_time_t key_lifetime,
62                         const char *as_rs_alg,
63                         oauth_key *key) {
64 
65         bzero(key,sizeof(*key));
66 
67         oauth_key_data okd;
68         bzero(&okd,sizeof(okd));
69 
70         {
71                 oauth_key_data_raw okdr;
72                 bzero(&okdr,sizeof(okdr));
73 
74                 STRCPY(okdr.kid,kid);
75                 STRCPY(okdr.ikm_key,ikm_key);
76                 STRCPY(okdr.as_rs_alg,as_rs_alg);
77                 okdr.timestamp = key_timestamp;
78                 okdr.lifetime = key_lifetime;
79 
80                 convert_oauth_key_data_raw(&okdr, &okd);
81         }
82 
83         char err_msg[1025] = "\0";
84         size_t err_msg_size = sizeof(err_msg) - 1;
85 
86         if (convert_oauth_key_data(&okd, key, err_msg, err_msg_size) < 0) {
87                         fprintf(stderr, "%s\n", err_msg);
88                         return -1;
89         }
90 
91         return 0;
92 }
93 
94 
95 
encode_token(const char * server_name,const char * gcm_nonce,const char * mac_key,const uint64_t token_timestamp,const uint32_t token_lifetime,const oauth_key key,char * base64encoded_etoken)96 static int encode_token(const char* server_name,
97                         const char* gcm_nonce,
98                         const char* mac_key,
99                         const uint64_t token_timestamp,
100                         const uint32_t token_lifetime,
101                         const oauth_key key,
102                         char* base64encoded_etoken) {
103 
104 
105         oauth_token ot;
106         bzero(&ot,sizeof(ot));
107 
108         const size_t mac_key_length=strlen(mac_key);
109         ot.enc_block.key_length = (uint16_t)mac_key_length;
110         STRCPY(ot.enc_block.mac_key,mac_key);
111         ot.enc_block.timestamp = token_timestamp;
112         ot.enc_block.lifetime = token_lifetime;
113 
114         encoded_oauth_token etoken;
115         bzero(&etoken,sizeof(etoken));
116 
117         // TODO: avoid this hack
118         if (!*gcm_nonce) gcm_nonce=NULL;
119 
120         if (encode_oauth_token((const uint8_t *) server_name, &etoken, &key, &ot,(const uint8_t *) gcm_nonce) < 0) {
121                 fprintf(stderr, "%s: cannot encode oauth token\n",
122                                 __FUNCTION__);
123                 return -1;
124         }
125 
126         size_t base64encoded_etoken_length;
127         const char *tmp=base64_encode((unsigned char *)(etoken.token), etoken.size, &base64encoded_etoken_length);
128         STRCPY(base64encoded_etoken,tmp);
129 
130         return 0;
131 }
132 
validate_decode_token(const char * server_name,const oauth_key key,const char * base64encoded_etoken,oauth_token * dot)133 static int validate_decode_token(const char* server_name,
134                         const oauth_key key,
135                         const char* base64encoded_etoken, oauth_token* dot) {
136 
137 
138         bzero((dot),sizeof(*dot));
139 
140         encoded_oauth_token etoken;
141         bzero(&etoken,sizeof(etoken));
142 
143         const size_t base64encoded_etoken_length=strlen(base64encoded_etoken);
144         const unsigned char *tmp = base64_decode(base64encoded_etoken,base64encoded_etoken_length,&etoken.size);
145         memcpy(etoken.token,tmp,etoken.size);
146 
147         if (decode_oauth_token((const uint8_t *) server_name, &etoken, &key, dot) < 0) {
148                 fprintf(stderr, "%s: cannot decode oauth token\n",
149                                 __FUNCTION__);
150                 return -1;
151         } else {
152                 return 0;
153         };
154 }
155 
print_token_body(oauth_token * dot)156 static void print_token_body(oauth_token* dot) {
157         printf("\n");
158         printf("Token non-encrpyted body:\n");
159         printf("{\n");
160         size_t base64encoded_nonce_length;
161         const char *base64encoded_nonce = base64_encode((unsigned char *)dot->enc_block.nonce, dot->enc_block.nonce_length,&base64encoded_nonce_length);
162         printf("    nonce: %s\n", base64encoded_nonce);
163         printf("    nonce length: %d\n", (int) dot->enc_block.nonce_length);
164         printf("Token encrpyted body:\n");
165         printf("{\n");
166         printf("    mac key: %s\n", (char*) dot->enc_block.mac_key);
167         printf("    mac key length: %d\n", (int) dot->enc_block.key_length);
168         time_t time=dot->enc_block.timestamp>>16;
169         unsigned msec=(dot->enc_block.timestamp & 0xFFFF)*64;
170         printf("    timestamp:\n");
171         printf("        unixtime: %u (localtime: %s )", (unsigned int)time, ctime(&time));
172         printf("        msec:%u\n", msec);
173         printf("    lifetime: %lu\n", (unsigned long) dot->enc_block.lifetime);
174         printf("}\n");
175 }
176 
177 //////////////// local definitions /////////////////
178 
179 const char Usage[] =
180   "Usage: oauth [ -e / -d ] [options]\n"
181   "Options:\n"
182   "\n"
183   "        -h, --help                       usage\n\n"
184   "        -v, --verbose                    verbose mode\n\n"
185   "        -e, --encrypt                    encrypt token\n"
186   "        -d, --decrypt                    decrypt validate token\n\n"
187   "        -i, --server-name                server name (max. 255 char)\n"
188   "        -j, --auth-key-id                Auth key id (max. 32 char)\n"
189   "        -k, --auth-key                   base64 encoded Auth key\n"
190   "        -l  --auth-key-timestamp         Auth key timestamp (sec since epoch)\n"
191   "        -m, --auth-key-lifetime          Auth key lifetime in sec\n"
192   "        -n, --auth-key-as-rs-alg         Authorization Server(AS) - Resource Server (RS) encryption algorithm\n"
193   "        -o, --token-nonce                base64 encoded nonce base64(12 octet) = 16 char\n"
194   "        -p, --token-mac-key              base64 encoded MAC key base64(32 octet) = 44 char\n"
195   "        -q, --token-timestamp            timestamp in format 64 bit unsigned (Native format - Unix),\n"
196   "                                         so 48 bit for secs since epoch UTC + 16 bit for 1/64000 fractions of a second.\n"
197   "                                         e.g.: the actual unixtimestamp 16 bit left shifted. (Default: actual gmtime)\n"
198   "        -r, --token-lifetime             lifetime in sec (Default: 3600)\n"
199   "        -t, --token                      base64 encoded encrypted token for validation and decryption\n"
200   "        -u, --hmac-alg                   stun client hmac algorithm\n";
201 
202 //////////////////////////////////////////////////
203 
204 
main(int argc,char ** argv)205 int main(int argc, char **argv)
206 {
207 
208   oauth_key key;
209 
210   //init vars with default values
211   char gcm_nonce[OAUTH_GCM_NONCE_SIZE+1]="";
212 
213   char mac_key[OAUTH_MAC_KEY_SIZE+1]="";
214 
215   time_t current_time = time(NULL);
216   struct tm* gmt = gmtime(&current_time);
217   uint64_t token_timestamp = (unsigned long long)mktime(gmt) << 16;
218   uint32_t token_lifetime = OAUTH_TOKEN_LIFETIME;
219 
220   //oauth_key
221   char kid[OAUTH_LTK_ID_SIZE+1] = "";
222   char base64encoded_ltk[OAUTH_LTK_BASE64ENCODED_SIZE+1]="";
223   turn_time_t key_timestamp = 0;
224   turn_time_t key_lifetime = 0;
225   char as_rs_alg[OAUTH_AS_RS_ALG_SIZE+1]="A256GCM";
226   char server_name[OAUTH_SERVER_NAME_SIZE+1] = "";
227 
228   char base64encoded_etoken[OAUTH_TOKEN_SIZE]="";
229 
230   //TODO: replace SHA1 with an option. Actualy both big browser chrome and mozilla supports AFAIU implemented only SHA1.
231   char hmac_alg[OAUTH_HMAC_ALG_SIZE+1]="HMAC-SHA1";
232 
233   static int verbose_flag=0;
234   static int encrypt_flag=0;
235   static int decrypt_flag=0;
236 
237   static struct option long_options[] =
238    {
239      /* These options set a flag. */
240      {"verbose",                  no_argument,       &verbose_flag, 1},
241      {"encrypt",                  no_argument,       &encrypt_flag, 1},
242      {"decrypt",                  no_argument,       &decrypt_flag, 1},
243      {"help",                     no_argument,       0, 'h'},
244      {"server-name",              required_argument, 0, 'i'},
245      {"auth-key-id",         required_argument, 0, 'j'},
246      {"auth-key",            required_argument, 0, 'k'},
247      {"auth-key-timestamp",  required_argument, 0, 'l'},
248      {"auth-key-lifetime",   required_argument, 0, 'm'},
249      {"auth-key-as-rs-alg",  required_argument, 0, 'n'},
250      {"token-nonce",              required_argument, 0, 'o'},
251      {"token-mac-key",            required_argument, 0, 'p'},
252      {"token-timestamp",          required_argument, 0, 'q'},
253      {"token-lifetime",           required_argument, 0, 'r'},
254      {"token",                    required_argument, 0, 't'},
255      {"hmac-alg",                 required_argument, 0, 'u'},
256      {0, 0, 0, 0}
257    };
258   /* getopt_long stores the option index here. */
259   int option_index = 0;
260 
261   //tmp vars
262   size_t nonce_size=0;
263   char *nonce_val;
264 
265   size_t mac_key_size;
266   char *mac_key_val;
267 
268 
269   int i;
270   int c=0;
271 
272   set_logfile("stdout");
273   set_system_parameters(0);
274 
275   while ((c = getopt_long(argc, argv, "hvedi:j:k:l:m:n:o:p:q:r:t:u:",long_options, &option_index)) != -1) {
276     switch(c) {
277     case 'h':
278       fprintf(stderr, "%s\n", Usage);
279       exit(1);
280       break;
281     case 'v':
282       verbose_flag=1;
283       break;
284     case 'e':
285       encrypt_flag=1;
286       break;
287     case 'd':
288       decrypt_flag=1;
289       break;
290     case 'i':
291       //server-name
292       if ( strlen(optarg) <= OAUTH_SERVER_NAME_SIZE ) {
293         STRCPY(server_name,optarg);
294       } else {
295         fprintf(stderr,"Server-name must not exceed %d!\n", OAUTH_LTK_ID_SIZE );
296         exit(1);
297       }
298       break;
299    case 'j':
300       //auth-key-id
301       if ( strlen(optarg) <= OAUTH_LTK_ID_SIZE ) {
302         STRCPY(kid,optarg);
303       } else {
304         fprintf(stderr,"Key ID must not exceed %d!\n", OAUTH_LTK_ID_SIZE );
305         exit(1);
306       }
307       break;
308     case 'k':
309       //auth-key
310       if ( strlen(optarg) <= OAUTH_LTK_BASE64ENCODED_SIZE ) {
311         STRCPY(base64encoded_ltk,optarg);
312       } else {
313         fprintf(stderr,"Key must not exceed %d!\n", OAUTH_LTK_BASE64ENCODED_SIZE );
314         exit(1);
315       }
316       break;
317     case 'l':
318       //auth-key-timestamp
319       key_timestamp = atoi(optarg);
320       break;
321     case 'm':
322       //auth-key-lifetime
323       key_lifetime=atoi(optarg);
324       break;
325     case 'n':
326       //auth-key-as-rs-alg
327       if ( strlen(optarg) <= OAUTH_AS_RS_ALG_SIZE ) {
328         STRCPY(as_rs_alg,optarg);
329       } else {
330         fprintf(stderr,"AS-RS Alg must not exceed %d!\n", OAUTH_AS_RS_ALG_SIZE );
331         exit(1);
332       }
333       break;
334     case 'o':
335       //token-nonce
336       nonce_val = (char*)base64_decode(optarg,strlen(optarg),&nonce_size);
337       if (nonce_size > OAUTH_GCM_NONCE_SIZE){
338         nonce_size=OAUTH_GCM_NONCE_SIZE;
339       }
340       strncpy(gcm_nonce,nonce_val,nonce_size);
341       gcm_nonce[ nonce_size + 1 ]='\0';
342       break;
343     case 'p':
344       //token-mac-key
345       mac_key_val = (char*)base64_decode(optarg,strlen(optarg),&mac_key_size);
346       if (mac_key_size > OAUTH_MAC_KEY_SIZE){
347         mac_key_size=OAUTH_MAC_KEY_SIZE;
348       }
349       strncpy(mac_key,mac_key_val,mac_key_size);
350       mac_key[mac_key_size+1]='\0';
351       break;
352     case 'q':
353       //token-timestamp
354       token_timestamp=strtoull(optarg,0,10);
355       break;
356     case 'r':
357       //token-lifetime
358       token_lifetime=atoi(optarg);
359       break;
360     case 't':
361       if ( strlen(optarg) <= OAUTH_TOKEN_SIZE ) {
362         STRCPY(base64encoded_etoken,optarg);
363       } else {
364         fprintf(stderr,"base64 encoded encrypted token must not exceed %d!\n", OAUTH_TOKEN_SIZE );
365         exit(1);
366       }
367       break;
368     case 'u':
369       //hmac-alg
370       if ( strlen(optarg) <= OAUTH_HMAC_ALG_SIZE ) {
371         STRCPY(hmac_alg,optarg);
372       } else {
373         fprintf(stderr,"STUN client HMAC Alg must not exceed %d!\n", OAUTH_HMAC_ALG_SIZE );
374         exit(1);
375       }
376       break;
377     default:
378       fprintf(stderr,"%s\n", Usage);
379       exit(1);
380       break;
381     }
382   }
383 
384   for (i = optind; i < argc; i++)
385     printf ("Non-option argument %s\n", argv[i]);
386 
387   if(optind>argc) {
388     fprintf(stderr, "%s\n", Usage);
389     exit(-1);
390   }
391 
392   if (!(encrypt_flag || decrypt_flag)){
393         fprintf(stderr, "Use either encrypt or decrypt.\nPlease use -h or --help for the detailed help\n");
394          exit(-1);
395   }
396 
397   //check if we have required params
398   //TODO: more compact warnning handling
399   if (encrypt_flag || decrypt_flag){
400     if (strlen(server_name) == 0) {
401         fprintf(stderr, "For encode/decode  --server-name/-i is mandatory \n");
402          exit(-1);
403     }
404 
405     if (strlen(kid) == 0){
406         fprintf(stderr, "For encode/decode  --auth-key-id/-j is mandatory \n");
407         exit(-1);
408     }
409      if (strlen(base64encoded_ltk) == 0){
410         fprintf(stderr, "For encode/decode  --auth-key/-k is mandatory \n");
411         exit(-1);
412     }
413     if (key_timestamp == 0){
414         fprintf(stderr, "For encode/decode  --auth-key-timestamp/-l is mandatory \n");
415         exit(-1);
416     }
417     if (key_lifetime == 0){
418         fprintf(stderr, "For encode/decode  --auth-key-lifetime/-m is mandatory \n");
419         exit(-1);
420     }
421 
422     if (encrypt_flag && strlen(mac_key) == 0) {
423         fprintf(stderr, "For encode --token-mac-key/-p is mandatory \n");
424         exit(-1);
425     }
426 
427     if (!encrypt_flag && decrypt_flag && strlen(base64encoded_etoken) == 0) {
428         fprintf(stderr, "For decode --token/-t is mandatory \n");
429         exit(-1);
430     }
431 
432     // Expiry warnings
433     if ( (unsigned long long)key_timestamp<<16 > token_timestamp  +((unsigned long long)token_lifetime << 16)  ) {
434         fprintf(stderr,"\nWARNING: Token expiry is earlear then Auth key life time start timestamp!!\n\n");
435     } else {
436         if( (unsigned long long)key_timestamp<<16 > token_timestamp) {
437             fprintf(stderr,"\nWARNING: Token life time start timestamp is earlier then Auth key start timestamp!!\n\n");
438         }
439     }
440     if( (unsigned long long)( key_timestamp + key_lifetime )<<16 < token_timestamp ) {
441         fprintf(stderr,"\nWARNING: Auth key will expire before token lifetime start timestamp!!\n\n");
442     } else {
443         if( (unsigned long long)( key_timestamp + key_lifetime)<<16 < token_timestamp + ((unsigned long long)token_lifetime << 16) ) {
444             fprintf(stderr,"\nWARNING: Auth key will expire before token expiry!!\n\n");
445         }
446     }
447 
448     if ( setup_ikm_key(kid, base64encoded_ltk, key_timestamp, key_lifetime, as_rs_alg, &key) == 0 ) {
449           if(encrypt_flag) {
450           if (encode_token(server_name, gcm_nonce, mac_key, token_timestamp, token_lifetime, key, base64encoded_etoken) == 0 ) {
451             printf("{\n");
452             printf("    \"access_token\":\"%s\",\n",base64encoded_etoken);
453             printf("    \"token_type\":\"pop\",\n");
454             printf("    \"expires_in\":%d,\n",token_lifetime);
455             printf("    \"kid\":\"%s\",\n",kid);
456             printf("    \"key\":\"%s\",\n",mac_key);
457             printf("    \"alg\":\"%s\"\n",hmac_alg);
458             printf("}\n");
459           } else {
460             fprintf(stderr, "Error during token encode\n");
461             exit(-1);
462           }
463         }
464         if (decrypt_flag) {
465           oauth_token dot;
466           if ( validate_decode_token(server_name, key, base64encoded_etoken,&dot) == 0) {
467             printf("-=Valid token!=-\n");
468               if (verbose_flag) print_token_body(&dot);
469           } else {
470             fprintf(stderr, "Error during token validation and decoding\n");
471             exit(-1);
472           }
473         }
474      } else {
475         fprintf(stderr, "Error during key setup\n");
476          exit(-1);
477     }
478 
479   }
480 
481   return 0;
482 }
483