Fix CVE-2017-6891 in minitasn1 code
[platform/upstream/gnutls.git] / src / srptool.c
1 /*
2  * Copyright (C) 2001-2012 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuTLS.
5  *
6  * GnuTLS is free software: you can redistribute it and/or modify it
7  * 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  * GnuTLS is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <gnutls/gnutls.h>
27 #include <gnutls/crypto.h>      /* for random */
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 #ifndef _WIN32
33 #include <pwd.h>
34 #include <unistd.h>
35 #else
36 #include <windows.h>
37 #endif
38
39 /* Gnulib portability files. */
40 #include <getpass.h>
41 #include <minmax.h>
42
43 #include <srptool-args.h>
44
45 /* This may need some rewrite. A lot of stuff which should be here
46  * are in the library, which is not good.
47  */
48
49 int crypt_int(const char *username, const char *passwd, int salt,
50               const char *tpasswd_conf, const char *tpasswd, int uindex);
51 static int read_conf_values(gnutls_datum_t * g, gnutls_datum_t * n,
52                             char *str);
53 static int _verify_passwd_int(const char *username, const char *passwd,
54                               char *verifier, const char *salt,
55                               const gnutls_datum_t * g,
56                               const gnutls_datum_t * n);
57
58 static void print_num(const char *msg, const gnutls_datum_t * num)
59 {
60         unsigned int i;
61
62         printf("%s:\t", msg);
63
64         for (i = 0; i < num->size; i++) {
65                 if (i != 0 && i % 12 == 0)
66                         printf("\n\t");
67                 else if (i != 0 && i != num->size)
68                         printf(":");
69                 printf("%.2x", num->data[i]);
70         }
71         printf("\n\n");
72
73 }
74
75 static int generate_create_conf(const char *tpasswd_conf)
76 {
77         FILE *fd;
78         char line[5 * 1024];
79         int index = 1;
80         gnutls_datum_t g, n;
81         gnutls_datum_t str_g, str_n;
82
83         fd = fopen(tpasswd_conf, "w");
84         if (fd == NULL) {
85                 fprintf(stderr, "Cannot open file '%s'\n", tpasswd_conf);
86                 return -1;
87         }
88
89         for (index = 1; index <= 5; index++) {
90
91                 if (index == 1) {
92                         n = gnutls_srp_1024_group_prime;
93                         g = gnutls_srp_1024_group_generator;
94                 } else if (index == 2) {
95                         n = gnutls_srp_1536_group_prime;
96                         g = gnutls_srp_1536_group_generator;
97                 } else if (index == 3) {
98                         n = gnutls_srp_2048_group_prime;
99                         g = gnutls_srp_2048_group_generator;
100                 } else if (index == 4) {
101                         n = gnutls_srp_3072_group_prime;
102                         g = gnutls_srp_3072_group_generator;
103                 } else if (index == 5) {
104                         n = gnutls_srp_4096_group_prime;
105                         g = gnutls_srp_4096_group_generator;
106                 } else {
107                         fprintf(stderr, "Unknown index: %d\n", index);
108                         return -1;
109                 }
110
111                 printf("\nGroup %d, of %d bits:\n", index, n.size * 8);
112                 print_num("Generator", &g);
113                 print_num("Prime", &n);
114
115                 if (gnutls_srp_base64_encode_alloc(&n, &str_n) < 0) {
116                         fprintf(stderr, "Could not encode\n");
117                         fclose(fd);
118                         return -1;
119                 }
120
121                 if (gnutls_srp_base64_encode_alloc(&g, &str_g) < 0) {
122                         fprintf(stderr, "Could not encode\n");
123                         fclose(fd);
124                         return -1;
125                 }
126
127                 sprintf(line, "%d:%s:%s\n", index, str_n.data, str_g.data);
128
129                 gnutls_free(str_n.data);
130                 gnutls_free(str_g.data);
131
132                 fwrite(line, 1, strlen(line), fd);
133
134         }
135
136         fclose(fd);
137
138         return 0;
139
140 }
141
142 /* The format of a tpasswd file is:
143  * username:verifier:salt:index
144  *
145  * index is the index of the prime-generator pair in tpasswd.conf
146  */
147 static int
148 _verify_passwd_int(const char *username, const char *passwd,
149                    char *verifier, const char *salt,
150                    const gnutls_datum_t * g, const gnutls_datum_t * n)
151 {
152         char _salt[1024];
153         gnutls_datum_t tmp, raw_salt, new_verifier;
154         size_t salt_size;
155         char *pos;
156
157         if (salt == NULL || verifier == NULL)
158                 return -1;
159
160         if (strlen(salt) >= sizeof(_salt)) {
161                 fprintf(stderr, "Too long salt.\n");
162                 return -1;
163         }
164
165         /* copy salt, and null terminate after the ':' */
166         strcpy(_salt, salt);
167         pos = strchr(_salt, ':');
168         if (pos != NULL)
169                 *pos = 0;
170
171         /* convert salt to binary. */
172         tmp.data = (void *) _salt;
173         tmp.size = strlen(_salt);
174
175         if (gnutls_srp_base64_decode_alloc(&tmp, &raw_salt) < 0) {
176                 fprintf(stderr, "Could not decode salt.\n");
177                 return -1;
178         }
179
180         if (gnutls_srp_verifier
181             (username, passwd, &raw_salt, g, n, &new_verifier) < 0) {
182                 fprintf(stderr, "Could not make the verifier\n");
183                 return -1;
184         }
185
186         free(raw_salt.data);
187
188         /* encode the verifier into _salt */
189         salt_size = sizeof(_salt);
190         memset(_salt, 0, salt_size);
191         if (gnutls_srp_base64_encode(&new_verifier, _salt, &salt_size) < 0) {
192                 fprintf(stderr, "Encoding error\n");
193                 return -1;
194         }
195
196         free(new_verifier.data);
197
198         if (strncmp(verifier, _salt, strlen(_salt)) == 0) {
199                 fprintf(stderr, "Password verified\n");
200                 return 0;
201         } else {
202                 fprintf(stderr, "Password does NOT match\n");
203         }
204         return -1;
205 }
206
207 static int filecopy(const char *src, const char *dst)
208 {
209         FILE *fd, *fd2;
210         char line[5 * 1024];
211         char *p;
212
213         fd = fopen(dst, "w");
214         if (fd == NULL) {
215                 fprintf(stderr, "Cannot open '%s' for write\n", dst);
216                 return -1;
217         }
218
219         fd2 = fopen(src, "r");
220         if (fd2 == NULL) {
221                 /* empty file */
222                 fclose(fd);
223                 return 0;
224         }
225
226         line[sizeof(line) - 1] = 0;
227         do {
228                 p = fgets(line, sizeof(line) - 1, fd2);
229                 if (p == NULL)
230                         break;
231
232                 fputs(line, fd);
233         }
234         while (1);
235
236         fclose(fd);
237         fclose(fd2);
238
239         return 0;
240 }
241
242 /* accepts password file */
243 static int find_strchr(const char *username, const char *file)
244 {
245         FILE *fd;
246         char *pos;
247         char line[5 * 1024];
248         unsigned int i;
249
250         fd = fopen(file, "r");
251         if (fd == NULL) {
252                 fprintf(stderr, "Cannot open file '%s'\n", file);
253                 return -1;
254         }
255
256         while (fgets(line, sizeof(line), fd) != NULL) {
257                 /* move to first ':' */
258                 i = 0;
259                 while ((line[i] != ':') && (line[i] != '\0')
260                        && (i < sizeof(line))) {
261                         i++;
262                 }
263                 if (strncmp(username, line, MAX(i, strlen(username))) == 0) {
264                         /* find the index */
265                         pos = strrchr(line, ':');
266                         pos++;
267                         fclose(fd);
268                         return atoi(pos);
269                 }
270         }
271
272         fclose(fd);
273         return -1;
274 }
275
276 /* Parses the tpasswd files, in order to verify the given
277  * username/password pair.
278  */
279 static int
280 verify_passwd(const char *conffile, const char *tpasswd,
281               const char *username, const char *passwd)
282 {
283         FILE *fd;
284         char line[5 * 1024];
285         unsigned int i;
286         gnutls_datum_t g, n;
287         int iindex;
288         char *p, *pos;
289
290         iindex = find_strchr(username, tpasswd);
291         if (iindex == -1) {
292                 fprintf(stderr, "Cannot find '%s' in %s\n", username,
293                         tpasswd);
294                 return -1;
295         }
296
297         fd = fopen(conffile, "r");
298         if (fd == NULL) {
299                 fprintf(stderr, "Cannot find %s\n", conffile);
300                 return -1;
301         }
302
303         do {
304                 p = fgets(line, sizeof(line) - 1, fd);
305         }
306         while (p != NULL && atoi(p) != iindex);
307
308         if (p == NULL) {
309                 fprintf(stderr, "Cannot find entry in %s\n", conffile);
310                 return -1;
311         }
312         line[sizeof(line) - 1] = 0;
313
314         fclose(fd);
315
316         if ((iindex = read_conf_values(&g, &n, line)) < 0) {
317                 fprintf(stderr, "Cannot parse conf file '%s'\n", conffile);
318                 return -1;
319         }
320
321         fd = fopen(tpasswd, "r");
322         if (fd == NULL) {
323                 fprintf(stderr, "Cannot open file '%s'\n", tpasswd);
324                 return -1;
325         }
326
327         while (fgets(line, sizeof(line), fd) != NULL) {
328                 /* move to first ':' 
329                  * This is the actual verifier.
330                  */
331                 i = 0;
332                 while ((line[i] != ':') && (line[i] != '\0')
333                        && (i < sizeof(line))) {
334                         i++;
335                 }
336                 if (strncmp(username, line, MAX(i, strlen(username))) == 0) {
337                         char *verifier_pos, *salt_pos;
338
339                         pos = strchr(line, ':');
340                         fclose(fd);
341                         if (pos == NULL) {
342                                 fprintf(stderr,
343                                         "Cannot parse conf file '%s'\n",
344                                         conffile);
345                                 return -1;
346                         }
347                         pos++;
348                         verifier_pos = pos;
349
350                         /* Move to the salt */
351                         pos = strchr(pos, ':');
352                         if (pos == NULL) {
353                                 fprintf(stderr,
354                                         "Cannot parse conf file '%s'\n",
355                                         conffile);
356                                 return -1;
357                         }
358                         pos++;
359                         salt_pos = pos;
360
361                         return _verify_passwd_int(username, passwd,
362                                                   verifier_pos, salt_pos,
363                                                   &g, &n);
364                 }
365         }
366
367         fclose(fd);
368         return -1;
369
370 }
371
372 #define KPASSWD "/etc/tpasswd"
373 #define KPASSWD_CONF "/etc/tpasswd.conf"
374
375 static void tls_log_func(int level, const char *str)
376 {
377         fprintf(stderr, "|<%d>| %s", level, str);
378 }
379
380 int main(int argc, char **argv)
381 {
382         const char *passwd;
383         int salt_size, ret;
384         int optct;
385         const char *fpasswd, *fpasswd_conf;
386         const char *username;
387 #ifndef _WIN32
388         struct passwd *pwd;
389 #endif
390
391         if ((ret = gnutls_global_init()) < 0) {
392                 fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret));
393                 exit(1);
394         }
395
396         umask(066);
397
398         optct = optionProcess(&srptoolOptions, argc, argv);
399         argc -= optct;
400         argv += optct;
401
402         gnutls_global_set_log_function(tls_log_func);
403         gnutls_global_set_log_level(OPT_VALUE_DEBUG);
404
405         if (HAVE_OPT(CREATE_CONF)) {
406                 return generate_create_conf(OPT_ARG(CREATE_CONF));
407         }
408
409         if (HAVE_OPT(PASSWD))
410                 fpasswd = OPT_ARG(PASSWD);
411         else
412                 fpasswd = (char *) KPASSWD;
413
414         if (HAVE_OPT(PASSWD_CONF))
415                 fpasswd_conf = OPT_ARG(PASSWD_CONF);
416         else
417                 fpasswd_conf = (char *) KPASSWD_CONF;
418
419         if (HAVE_OPT(USERNAME))
420                 username = OPT_ARG(USERNAME);
421         else {
422 #ifndef _WIN32
423                 pwd = getpwuid(getuid());
424
425                 if (pwd == NULL) {
426                         fprintf(stderr, "No such user\n");
427                         return -1;
428                 }
429
430                 username = pwd->pw_name;
431 #else
432                 fprintf(stderr, "Please specify a user\n");
433                 return -1;
434 #endif
435         }
436
437         salt_size = 16;
438
439         passwd = getpass("Enter password: ");
440         if (passwd == NULL) {
441                 fprintf(stderr, "Please specify a password\n");
442                 return -1;
443         }
444
445 /* not ready yet */
446         if (HAVE_OPT(VERIFY)) {
447                 return verify_passwd(fpasswd_conf, fpasswd,
448                                      username, passwd);
449         }
450
451
452         return crypt_int(username, passwd, salt_size,
453                          fpasswd_conf, fpasswd, OPT_VALUE_INDEX);
454
455 }
456
457 static char *_srp_crypt(const char *username, const char *passwd,
458                         int salt_size, const gnutls_datum_t * g,
459                         const gnutls_datum_t * n)
460 {
461         unsigned char salt[128];
462         static char result[1024];
463         gnutls_datum_t dat_salt, txt_salt;
464         gnutls_datum_t verifier, txt_verifier;
465
466         if ((unsigned) salt_size > sizeof(salt))
467                 return NULL;
468
469         /* generate the salt
470          */
471         if (gnutls_rnd(GNUTLS_RND_NONCE, salt, salt_size) < 0) {
472                 fprintf(stderr, "Could not create nonce\n");
473                 return NULL;
474         }
475
476         dat_salt.data = salt;
477         dat_salt.size = salt_size;
478
479         if (gnutls_srp_verifier
480             (username, passwd, &dat_salt, g, n, &verifier) < 0) {
481                 fprintf(stderr, "Error getting verifier\n");
482                 return NULL;
483         }
484
485         /* base64 encode the verifier */
486         if (gnutls_srp_base64_encode_alloc(&verifier, &txt_verifier) < 0) {
487                 fprintf(stderr, "Error encoding\n");
488                 free(verifier.data);
489                 return NULL;
490         }
491
492         free(verifier.data);
493
494         if (gnutls_srp_base64_encode_alloc(&dat_salt, &txt_salt) < 0) {
495                 fprintf(stderr, "Error encoding\n");
496                 return NULL;
497         }
498
499         sprintf(result, "%s:%s", txt_verifier.data, txt_salt.data);
500         free(txt_salt.data);
501         free(txt_verifier.data);
502
503         return result;
504
505 }
506
507
508 int
509 crypt_int(const char *username, const char *passwd, int salt_size,
510           const char *tpasswd_conf, const char *tpasswd, int uindex)
511 {
512         FILE *fd;
513         char *cr;
514         gnutls_datum_t g, n;
515         char line[5 * 1024];
516         char *p, *pp;
517         int iindex;
518         char tmpname[1024];
519
520         fd = fopen(tpasswd_conf, "r");
521         if (fd == NULL) {
522                 fprintf(stderr, "Cannot find %s\n", tpasswd_conf);
523                 return -1;
524         }
525
526         do {                    /* find the specified uindex in file */
527                 p = fgets(line, sizeof(line) - 1, fd);
528         }
529         while (p != NULL && (iindex = atoi(p)) != uindex);
530
531         if (p == NULL) {
532                 fprintf(stderr, "Cannot find entry in %s\n", tpasswd_conf);
533                 return -1;
534         }
535         line[sizeof(line) - 1] = 0;
536
537         fclose(fd);
538         if ((iindex = read_conf_values(&g, &n, line)) < 0) {
539                 fprintf(stderr, "Cannot parse conf file '%s'\n",
540                         tpasswd_conf);
541                 return -1;
542         }
543
544         cr = _srp_crypt(username, passwd, salt_size, &g, &n);
545         if (cr == NULL) {
546                 fprintf(stderr, "Cannot _srp_crypt()...\n");
547                 return -1;
548         } else {
549                 /* delete previous entry */
550                 struct stat st;
551                 FILE *fd2;
552                 int put;
553
554                 if (strlen(tpasswd) + 5 > sizeof(tmpname)) {
555                         fprintf(stderr, "file '%s' is tooooo long\n",
556                                 tpasswd);
557                         return -1;
558                 }
559
560                 snprintf(tmpname, sizeof(tmpname), "%s.tmp", tpasswd);
561
562                 if (stat(tmpname, &st) != -1) {
563                         fprintf(stderr, "file '%s' is locked\n", tpasswd);
564                         return -1;
565                 }
566
567                 if (filecopy(tpasswd, tmpname) != 0) {
568                         fprintf(stderr, "Cannot copy '%s' to '%s'\n",
569                                 tpasswd, tmpname);
570                         return -1;
571                 }
572
573                 fd = fopen(tpasswd, "w");
574                 if (fd == NULL) {
575                         fprintf(stderr, "Cannot open '%s' for write\n",
576                                 tpasswd);
577                         remove(tmpname);
578                         return -1;
579                 }
580
581                 fd2 = fopen(tmpname, "r");
582                 if (fd2 == NULL) {
583                         fprintf(stderr, "Cannot open '%s' for read\n",
584                                 tmpname);
585                         remove(tmpname);
586                         return -1;
587                 }
588
589                 put = 0;
590                 do {
591                         p = fgets(line, sizeof(line) - 1, fd2);
592                         if (p == NULL)
593                                 break;
594
595                         pp = strchr(line, ':');
596                         if (pp == NULL)
597                                 continue;
598
599                         if (strncmp(p, username,
600                                     MAX(strlen(username),
601                                         (unsigned int) (pp - p))) == 0) {
602                                 put = 1;
603                                 fprintf(fd, "%s:%s:%u\n", username, cr,
604                                         iindex);
605                         } else {
606                                 fputs(line, fd);
607                         }
608                 }
609                 while (1);
610
611                 if (put == 0) {
612                         fprintf(fd, "%s:%s:%u\n", username, cr, iindex);
613                 }
614
615                 fclose(fd);
616                 fclose(fd2);
617
618                 remove(tmpname);
619
620         }
621
622
623         return 0;
624 }
625
626
627
628 /* this function parses tpasswd.conf file. Format is:
629  * int(index):base64(n):base64(g)
630  */
631 static int
632 read_conf_values(gnutls_datum_t * g, gnutls_datum_t * n, char *str)
633 {
634         char *p;
635         int len;
636         int index, ret;
637         gnutls_datum_t dat;
638
639         index = atoi(str);
640
641         p = strrchr(str, ':');  /* we have g */
642         if (p == NULL) {
643                 return -1;
644         }
645
646         *p = '\0';
647         p++;
648
649         /* read the generator */
650         len = strlen(p);
651         if (p[len - 1] == '\n')
652                 len--;
653
654         dat.data = (void *) p;
655         dat.size = len;
656         ret = gnutls_srp_base64_decode_alloc(&dat, g);
657
658         if (ret < 0) {
659                 fprintf(stderr, "Decoding error\n");
660                 return -1;
661         }
662
663         /* now go for n - modulo */
664         p = strrchr(str, ':');  /* we have n */
665         if (p == NULL) {
666                 return -1;
667         }
668
669         *p = '\0';
670         p++;
671
672         dat.data = (void *) p;
673         dat.size = strlen(p);
674
675         ret = gnutls_srp_base64_decode_alloc(&dat, n);
676
677         if (ret < 0) {
678                 fprintf(stderr, "Decoding error\n");
679                 free(g->data);
680                 return -1;
681         }
682
683         return index;
684 }