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(¤t_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