gPass

gPass Git Source Tree

Root/cli/main.c

1/*
2 Copyright (C) 2013-2017 Grégory Soutadé
3
4 This file is part of gPass.
5
6 gPass is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 gPass is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with gPass. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include <stdio.h>
21#include <unistd.h>
22#include <stdlib.h>
23#include <string.h>
24#include <signal.h>
25
26#include <curl/curl.h>
27#include <openssl/opensslv.h>
28#include <openssl/evp.h>
29
30#include "ini.h"
31
32#define STRNCMP(a, b) strncmp(a, b, sizeof(b)-1)
33
34#define DEFAULT_CONFIG_FILE ".local/share/gpass/gpass.ini"
35
36#define DEFAULT_PBKDF2_LEVEL 1000
37#define MASTER_KEY_LENGTH (256/8)
38#define GLOBAL_IV_LENGTH 16
39#define BLOCK_SIZE (128/8)
40#define DEFAULT_SERVER_PORT 443
41#define SERVER_PROTOCOL 4
42#define RESPONSE_SIZE 2048
43#define MAX_SUBDOMAINS 10
44#define DISPLAY_TIME 30 // 30 seconds
45
46struct gpass_parameters {
47 unsigned pbkdf2_level;
48 char *server;
49 char *salt;
50 char *domain;
51 char *username;
52 char *orig_master_key;
53 unsigned char *derived_master_key;
54 unsigned server_port;
55 unsigned verbose;
56 char *ca_path;
57 unsigned verify_ssl_peer;
58 unsigned port_set;
59 unsigned crypto_v1_compatible;
60 unsigned char *global_iv;
61} ;
62
63#if OPENSSL_VERSION_NUMBER >= 0x10010000
64// OpenSSL >= 1.1
65static EVP_MD_CTX * s_md_ctx;
66#else
67static EVP_MD_CTX * s_md_ctx;
68static EVP_MD_CTX ss_md_ctx;
69#define EVP_MD_CTX_new(...) &ss_md_ctx
70#define EVP_MD_CTX_free(...)
71#endif
72static const EVP_MD * s_md_256;
73
74static EVP_CIPHER_CTX * s_cipher_ctx;
75static int s_stop_display = 0;
76
77static void signal_handler(int signum)
78{
79 s_stop_display = 1;
80}
81
82static void display_password(char* password, int time)
83{
84 int print_len = 0;
85
86 for (; time && !s_stop_display; time--)
87 {
88 print_len = printf("\r(%02d) Password found: %s", time, password);
89 fflush(stdout);
90 sleep(1);
91 }
92
93 // Clear line
94 print_len++; // For C or Z
95 printf("\r");
96 while (print_len--)
97 printf(" ");
98 printf("\n");
99}
100
101static int digest(unsigned char** out, unsigned char* in, unsigned size)
102{
103 *out = NULL;
104 EVP_DigestInit(s_md_ctx, s_md_256);
105 EVP_DigestUpdate(s_md_ctx, in, size);
106 *out = malloc(32);
107 return EVP_DigestFinal(s_md_ctx, *out, NULL);
108}
109
110static void derive_master_key(struct gpass_parameters* params)
111{
112 if (!params->derived_master_key)
113 params->derived_master_key = malloc(MASTER_KEY_LENGTH);
114
115 if (!params->global_iv)
116 params->global_iv = malloc(GLOBAL_IV_LENGTH);
117
118 PKCS5_PBKDF2_HMAC(params->orig_master_key, strlen(params->orig_master_key),
119 (unsigned char*)params->salt, strlen(params->salt),
120 params->pbkdf2_level, EVP_sha256(),
121 MASTER_KEY_LENGTH, params->derived_master_key);
122
123 PKCS5_PBKDF2_HMAC(params->salt, strlen(params->salt),
124 (unsigned char*)params->orig_master_key, strlen(params->orig_master_key),
125 params->pbkdf2_level, EVP_sha256(),
126 GLOBAL_IV_LENGTH, params->global_iv);
127}
128
129static void bin_to_hex(unsigned char* bin, unsigned char* hex, unsigned bin_size)
130{
131 unsigned char tmp;
132
133 for (; bin_size--; bin++)
134 {
135 tmp = (*bin >> 4) & 0xf;
136 if (tmp <= 9)
137 *hex++ = '0' + tmp;
138 else
139 *hex++ = 'a' + (tmp-10);
140
141 tmp = *bin & 0xf;
142 if (tmp <= 9)
143 *hex++ = '0' + tmp;
144 else
145 *hex++ = 'a' + (tmp-10);
146 }
147}
148
149static void hex_to_bin(unsigned char* bin, unsigned char* hex, long hex_size)
150{
151 unsigned char tmp;
152
153 // Round to 2
154 hex_size &= ~1;
155
156 for (; hex_size; hex_size-=2, bin++)
157 {
158 tmp = *hex++;
159 if (tmp >= '0' && tmp <= '9')
160 *bin = (tmp - '0') << 4;
161 else if (tmp >= 'a' && tmp <= 'f')
162 *bin = ((tmp - 'a')+10) << 4;
163 else
164 *bin = ((tmp - 'A')+10) << 4;
165
166 tmp = *hex++;
167 if (tmp >= '0' && tmp <= '9')
168 *bin |= (tmp - '0');
169 else if (tmp >= 'a' && tmp <= 'f')
170 *bin |= ((tmp - 'a')+10);
171 else
172 *bin |= ((tmp - 'A')+10);
173 }
174}
175
176static void encrypt_domain_v1(struct gpass_parameters* params, char* domain,
177 unsigned char** res, unsigned* out_size)
178{
179 unsigned size = 2+strlen(domain)+1+strlen(params->username);
180 unsigned char* buffer, *tmp;
181
182 if (params->verbose)
183 printf("%s: %s\n", __func__, domain);
184
185 if ((size % BLOCK_SIZE))
186 size = ((size/BLOCK_SIZE)+1)*BLOCK_SIZE;
187
188 buffer = malloc(size+1); // Cause snprintf() add a final \0
189 memset(buffer, 0, size+1);
190
191 snprintf((char*)buffer, size+1, "@@%s;%s", domain, params->username);
192
193 tmp = malloc(size);
194 *res = malloc(size*2);
195
196 EVP_EncryptInit(s_cipher_ctx, EVP_aes_256_ecb(), params->derived_master_key, NULL);
197 EVP_CipherUpdate(s_cipher_ctx, tmp, (int*)out_size, buffer, size);
198
199 bin_to_hex(tmp, *res, size);
200
201 *out_size *= 2;
202
203 free(buffer);
204 free(tmp);
205}
206
207static void encrypt_domain(struct gpass_parameters* params, char* domain,
208 unsigned char** res, unsigned* out_size)
209{
210 unsigned size = strlen(domain)+1+strlen(params->username);
211 unsigned padded_size;
212 unsigned char* buffer, *tmp;
213
214 if (params->verbose)
215 printf("%s: %s\n", __func__, domain);
216
217 if ((size % BLOCK_SIZE))
218 size = ((size/BLOCK_SIZE)+1)*BLOCK_SIZE;
219 padded_size = size;
220
221 size += 16; // For digest
222
223 buffer = malloc(size);
224 memset(buffer, 0, size);
225
226 snprintf((char*)buffer, size, "%s;%s", domain, params->username);
227
228 // Append digest
229 digest(&tmp, buffer, padded_size);
230 memcpy(&buffer[padded_size], &tmp[8], 16);
231 free(tmp);
232
233 tmp = malloc(size);
234 *res = malloc(size*2);
235
236 EVP_EncryptInit(s_cipher_ctx, EVP_aes_256_cbc(), params->derived_master_key, params->global_iv);
237 EVP_CipherUpdate(s_cipher_ctx, tmp, (int*)out_size, buffer, size);
238
239 bin_to_hex(tmp, *res, size);
240
241 *out_size *= 2;
242
243 free(buffer);
244 free(tmp);
245}
246
247static void append_to_request(char** request, char* new_req, unsigned new_req_size)
248{
249 static int cur_req_idx = 0;
250 int size_added;
251
252 if (!cur_req_idx)
253 {
254 *request = malloc(3+new_req_size+1);
255 snprintf(*request, 3+new_req_size+1, "k0=%s", new_req);
256 }
257 else
258 {
259 size_added = 4+new_req_size;
260 if (cur_req_idx >= 10)
261 size_added++;
262
263 *request = realloc(*request, strlen(*request)+1+size_added);
264
265 snprintf(&((*request)[strlen(*request)]), size_added+1, "&k%d=%s",
266 cur_req_idx, new_req);
267 }
268
269 cur_req_idx++;
270}
271
272static char* wildcard_domain(char* domain)
273{
274 int cur_level = 1;
275 char* level_ptr[MAX_SUBDOMAINS], *tmp, *res = NULL;
276 int level_length[MAX_SUBDOMAINS];
277
278 memset(level_ptr, 0, sizeof(level_ptr));
279 memset(level_length, 0, sizeof(level_length));
280 level_ptr[0] = domain;
281
282 for (tmp=domain; *tmp && cur_level < MAX_SUBDOMAINS; tmp++)
283 {
284 if (*tmp == '.')
285 {
286 level_ptr[cur_level] = tmp+1;
287 level_length[cur_level-1] = tmp - level_ptr[cur_level-1];
288 cur_level++;
289 }
290 }
291
292 // Too much levels
293 if (cur_level >= MAX_SUBDOMAINS)
294 {
295 fprintf(stderr, "Error: Too much levels for domain %s\n", domain);
296 return NULL;
297 }
298
299 // Final level
300 level_length[cur_level-1] = tmp - level_ptr[cur_level-1];
301
302 tmp = NULL;
303 if (cur_level > 2)
304 {
305 // Standard root domain (zzz.xxx.com) or more
306 tmp = level_ptr[1];
307 }
308 // Simple xxx.com
309 else if (cur_level == 2)
310 tmp = level_ptr[0];
311
312 if (tmp)
313 {
314 res = malloc(2+strlen(tmp)+1);
315 sprintf(res, "*.%s", tmp);
316 }
317
318 return res;
319}
320
321static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
322{
323 if ((size*nmemb) > RESPONSE_SIZE)
324 {
325 fprintf(stderr, "Error curl response is too big (%d bytes, max %d bytes)\n",
326 (int)(size*nmemb), RESPONSE_SIZE);
327 }
328 else
329 memcpy(userdata, ptr, size*nmemb);
330
331 return size*nmemb;
332}
333
334static int ask_server(struct gpass_parameters* params)
335{
336 char* wc_domain, *saveptr, *token, *cur_ptr;
337 unsigned char* enc_domain;
338 unsigned enc_size, matched_key = 0, crypto_v1_index = 1;
339 char* request = NULL;
340 int ret = -1, res, len;
341 CURL *curl;
342 char response[RESPONSE_SIZE];
343 unsigned char password[256];
344
345 if (params->verbose)
346 printf("Username: %s\n", params->username);
347
348 encrypt_domain(params, params->domain, &enc_domain, &enc_size);
349 append_to_request(&request, (char*)enc_domain, enc_size);
350 free(enc_domain);
351
352 wc_domain = wildcard_domain(params->domain);
353 if (wc_domain)
354 {
355 crypto_v1_index++;
356 encrypt_domain(params, wc_domain, &enc_domain, &enc_size);
357 append_to_request(&request, (char*)enc_domain, enc_size);
358 free(enc_domain);
359 }
360
361 if (params->crypto_v1_compatible)
362 {
363 encrypt_domain_v1(params, params->domain, &enc_domain, &enc_size);
364 append_to_request(&request, (char*)enc_domain, enc_size);
365 free(enc_domain);
366 if (wc_domain)
367 {
368 encrypt_domain_v1(params, wc_domain, &enc_domain, &enc_size);
369 append_to_request(&request, (char*)enc_domain, enc_size);
370 free(enc_domain);
371 }
372
373 }
374
375 if (params->verbose)
376 printf("Request: %s\n", request);
377
378 curl = curl_easy_init();
379 curl_easy_setopt(curl, CURLOPT_URL, params->server);
380 curl_easy_setopt(curl, CURLOPT_PORT, params->server_port);
381 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, params->verify_ssl_peer);
382 if (params->ca_path)
383 curl_easy_setopt(curl, CURLOPT_CAINFO, params->ca_path);
384 curl_easy_setopt(curl, CURLOPT_POST, 1);
385 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
386 if (params->verbose)
387 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
388 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
389 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)response);
390
391 res = curl_easy_perform(curl);
392 curl_easy_cleanup(curl);
393
394 if (res != CURLE_OK)
395 {
396 fprintf(stderr, "curl_easy_perform() failed: %s\n",
397 curl_easy_strerror(res));
398 goto end;
399 }
400
401 token = strtok_r(response, "\n", &saveptr);
402
403 while (token)
404 {
405 if (params->verbose)
406 printf("Parse %s\n", token);
407 cur_ptr = token;
408 if (!strcmp(token, "<end>"))
409 break;
410 else if (!STRNCMP(token, "protocol"))
411 {
412 cur_ptr += sizeof("protocol"); // includes "="
413 if (STRNCMP(cur_ptr, "gpass-"))
414 {
415 fprintf(stderr, "Error: Unknown server protocol %s\n", token);
416 break;
417 }
418 else
419 {
420 cur_ptr += sizeof("gpass-")-1;
421 if (atoi(cur_ptr) > SERVER_PROTOCOL)
422 {
423 fprintf(stderr, "Error: Cannot handle server protocol %s\n", token);
424 break;
425 }
426 }
427 }
428 else if (!STRNCMP(token, "pass"))
429 {
430 cur_ptr += sizeof("pass"); // includes "="
431
432 if ((strlen(cur_ptr)/2) > sizeof(password))
433 {
434 fprintf(stderr, "Error: retrieved password is too big !\n");
435 goto end;
436 }
437
438 hex_to_bin(password, (unsigned char*)cur_ptr, strlen(cur_ptr));
439
440 if (matched_key >= crypto_v1_index)
441 {
442 // Crypto v1
443 EVP_DecryptInit(s_cipher_ctx, EVP_aes_256_ecb(), params->derived_master_key, NULL);
444 EVP_CipherUpdate(s_cipher_ctx, password, &res, password, strlen(cur_ptr)/2);
445 // Remove salt
446 password[strlen((char*)password)-3] = 0;
447 }
448 else
449 {
450 EVP_DecryptInit(s_cipher_ctx, EVP_aes_256_cbc(), params->derived_master_key, params->global_iv);
451 EVP_CipherUpdate(s_cipher_ctx, password, &res, password, strlen(cur_ptr)/2);
452 // Remove salt
453 len = strlen((char*)password);
454 memmove(password, &password[3], len-3);
455 password[len-3] = 0;
456 }
457 display_password((char*)password, DISPLAY_TIME);
458 ret = 0;
459 goto end;
460 }
461 else if (!STRNCMP(token, "pbkdf2_level"))
462 {
463 cur_ptr += sizeof("pbkdf2_level"); // includes "="
464
465 if (atoi(cur_ptr) != params->pbkdf2_level)
466 {
467 params->pbkdf2_level = atoi(cur_ptr);
468 ret = 1;
469 break;
470 }
471 }
472 else if (!STRNCMP(token, "matched_key"))
473 {
474 cur_ptr += sizeof("matched_key"); // includes "="
475
476 matched_key = atoi(cur_ptr);
477 }
478 else
479 {
480 fprintf(stderr, "Error: Unknown server response %s\n", token);
481 break;
482 }
483 token = strtok_r(NULL, "\n", &saveptr);
484 }
485
486 if (ret)
487 printf("Password not found\n");
488
489end:
490 free(request);
491
492 return ret;
493}
494
495static void init_parameters(struct gpass_parameters* params)
496{
497 memset (params, 0, sizeof(*params));
498 params->pbkdf2_level = DEFAULT_PBKDF2_LEVEL;
499 params->server_port = DEFAULT_SERVER_PORT;
500 params->verify_ssl_peer = 1;
501 params->crypto_v1_compatible = 1; // For now, in the next version it must a command line parameter
502}
503
504static void release_parameters(struct gpass_parameters* params)
505{
506 if (params->server) free(params->server);
507 if (params->salt) free(params->salt);
508 if (params->domain) free(params->domain);
509 if (params->username) free(params->username);
510 if (params->orig_master_key) free(params->orig_master_key);
511 if (params->derived_master_key) free(params->derived_master_key);
512 if( params->ca_path) free(params->ca_path);
513 if (params->global_iv) free(params->global_iv);
514}
515
516static int check_parameters(struct gpass_parameters* params)
517{
518 if (!params->server)
519 {
520 fprintf(stderr, "Error: server not set\n");
521 return 1;
522 }
523
524 if (!params->domain)
525 {
526 fprintf(stderr, "Error: gpass domain not set\n");
527 return 1;
528 }
529
530 if (!params->username)
531 {
532 fprintf(stderr, "Error: username not set\n");
533 return 1;
534 }
535
536 return 0;
537}
538
539static int gpass_ini_handler(void* user, const char* section,
540 const char* name, const char* value)
541{
542 struct gpass_parameters* params = (struct gpass_parameters*) user;
543
544 if (!STRNCMP(name, "ca_path"))
545 {
546 if (params->ca_path) free(params->ca_path);
547 params->ca_path = strdup(value);
548 }
549 else if (!STRNCMP(name, "pbkdf2_level"))
550 params->pbkdf2_level = atoi(value);
551 else if (!STRNCMP(name, "verify_ssl_peer"))
552 params->verify_ssl_peer = atoi(value);
553 else if (!STRNCMP(name, "server_port"))
554 {
555 params->server_port = atoi(value);
556 params->port_set = 1;
557 }
558 else if (!STRNCMP(name, "server"))
559 {
560 if (params->server) free(params->server);
561 params->server = strdup(value);
562 }
563 else
564 fprintf(stderr, "Error: Unknown key '%s' in config file\n", name);
565
566 return 1;
567}
568
569static void usage(char* program_name)
570{
571 fprintf(stderr, "Usage: %s [-f config_file] [-p server_port] [-c CA_certificate_path] [-l PBKDF2_level] [-s gpass_server] [-v] -d domain -u username\n",
572 program_name);
573 exit(EXIT_FAILURE);
574}
575
576int main(int argc, char** argv)
577{
578 struct gpass_parameters params;
579 int opt, ret = 0;
580 char* tmp;
581 char* config_file, *home;
582
583 if (argc == 1)
584 usage(argv[0]);
585
586 init_parameters(&params);
587
588 home = getenv("HOME");
589 if (home)
590 {
591 config_file = malloc(strlen(home)+1+sizeof(DEFAULT_CONFIG_FILE));
592 sprintf(config_file, "%s/" DEFAULT_CONFIG_FILE, home);
593
594 ini_parse(config_file, gpass_ini_handler, &params);
595
596 free(config_file);
597 }
598
599 while ((opt = getopt(argc, argv, "c:d:f:l:np:s:u:vh")) != -1) {
600 switch (opt) {
601 case 'c':
602 if (params.ca_path) free(params.ca_path);
603 params.ca_path = strdup(optarg);
604 break;
605 case 'd':
606 if (params.domain) free(params.domain);
607 params.domain = strdup(optarg);
608 break;
609 case 'f':
610 ini_parse(optarg, gpass_ini_handler, &params);
611 break;
612 case 'l':
613 params.pbkdf2_level = atoi(optarg);
614 break;
615 case 'n':
616 params.verify_ssl_peer = 0;
617 break;
618 case 'p':
619 params.server_port = atoi(optarg);
620 params.port_set = 1;
621 break;
622 case 's':
623 if (params.server) free(params.server);
624 params.server = strdup(optarg);
625 break;
626 case 'u':
627 if (params.username) free(params.username);
628 params.username = strdup(optarg);
629 break;
630 case 'v':
631 params.verbose++;
632 break;
633 case 'h':
634 case '?':
635 default: /* '?' */
636 usage(argv[0]);
637 }
638 }
639
640 ret = check_parameters(&params);
641
642 if (ret)
643 goto end;
644
645 // Manage server, server_port and salt
646 if (!STRNCMP(params.server, "http://"))
647 {
648 if (!params.port_set)
649 params.server_port = 80;
650 params.salt = strdup(&params.server[7]);
651 }
652 else if (!STRNCMP(params.server, "https://"))
653 {
654 if (!params.port_set)
655 params.server_port = 443;
656 params.salt = strdup(&params.server[8]);
657 }
658
659 // Manage domain
660 if (!STRNCMP(params.domain, "http://"))
661 {
662 tmp = strdup(&params.domain[7]);
663 free(params.domain);
664 params.domain = tmp;
665 }
666 else if (!STRNCMP(params.domain, "https://"))
667 {
668 tmp = strdup(&params.domain[8]);
669 free(params.domain);
670 params.domain = tmp;
671 }
672
673 // Remove query part of domain (a.com[/XXXX])
674 for (tmp=params.domain; *tmp; tmp++)
675 {
676 if (*tmp == '/')
677 {
678 *tmp = 0;
679 break;
680 }
681 }
682
683 s_md_ctx = EVP_MD_CTX_new();
684 s_md_256 = EVP_sha256();
685 EVP_DigestInit(s_md_ctx, s_md_256);
686
687 s_cipher_ctx = EVP_CIPHER_CTX_new();
688
689 // Let's go
690 tmp = getpass("Enter master key: ");
691
692 if (!tmp)
693 goto end;
694
695 params.orig_master_key = strdup(tmp);
696 derive_master_key(&params);
697
698 // Ctrl+C
699 signal(SIGINT, signal_handler);
700 // Ctrl+Z
701 signal(SIGTSTP, signal_handler);
702
703 ret = ask_server(&params);
704
705 // try again with new parameters
706 if (ret > 0)
707 {
708 derive_master_key(&params);
709 ask_server(&params);
710 }
711
712end:
713 release_parameters(&params);
714
715 if (s_md_ctx) EVP_MD_CTX_free(s_md_ctx);
716 if (s_cipher_ctx) EVP_CIPHER_CTX_free(s_cipher_ctx);
717
718 return ret;
719}

Archive Download this file