Imported Upstream version 3.3.5
[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                         return -1;
118                 }
119
120                 if (gnutls_srp_base64_encode_alloc(&g, &str_g) < 0) {
121                         fprintf(stderr, "Could not encode\n");
122                         return -1;
123                 }
124
125                 sprintf(line, "%d:%s:%s\n", index, str_n.data, str_g.data);
126
127                 gnutls_free(str_n.data);
128                 gnutls_free(str_g.data);
129
130                 fwrite(line, 1, strlen(line), fd);
131
132         }
133
134         fclose(fd);
135
136         return 0;
137
138 }
139
140 /* The format of a tpasswd file is:
141  * username:verifier:salt:index
142  *
143  * index is the index of the prime-generator pair in tpasswd.conf
144  */
145 static int
146 _verify_passwd_int(const char *username, const char *passwd,
147                    char *verifier, const char *salt,
148                    const gnutls_datum_t * g, const gnutls_datum_t * n)
149 {
150         char _salt[1024];
151         gnutls_datum_t tmp, raw_salt, new_verifier;
152         size_t salt_size;
153         char *pos;
154
155         if (salt == NULL || verifier == NULL)
156                 return -1;
157
158         if (strlen(salt) >= sizeof(_salt)) {
159                 fprintf(stderr, "Too long salt.\n");
160                 return -1;
161         }
162
163         /* copy salt, and null terminate after the ':' */
164         strcpy(_salt, salt);
165         pos = strchr(_salt, ':');
166         if (pos != NULL)
167                 *pos = 0;
168
169         /* convert salt to binary. */
170         tmp.data = (void *) _salt;
171         tmp.size = strlen(_salt);
172
173         if (gnutls_srp_base64_decode_alloc(&tmp, &raw_salt) < 0) {
174                 fprintf(stderr, "Could not decode salt.\n");
175                 return -1;
176         }
177
178         if (gnutls_srp_verifier
179             (username, passwd, &raw_salt, g, n, &new_verifier) < 0) {
180                 fprintf(stderr, "Could not make the verifier\n");
181                 return -1;
182         }
183
184         free(raw_salt.data);
185
186         /* encode the verifier into _salt */
187         salt_size = sizeof(_salt);
188         memset(_salt, 0, salt_size);
189         if (gnutls_srp_base64_encode(&new_verifier, _salt, &salt_size) < 0) {
190                 fprintf(stderr, "Encoding error\n");
191                 return -1;
192         }
193
194         free(new_verifier.data);
195
196         if (strncmp(verifier, _salt, strlen(_salt)) == 0) {
197                 fprintf(stderr, "Password verified\n");
198                 return 0;
199         } else {
200                 fprintf(stderr, "Password does NOT match\n");
201         }
202         return -1;
203 }
204
205 static int filecopy(const char *src, const char *dst)
206 {
207         FILE *fd, *fd2;
208         char line[5 * 1024];
209         char *p;
210
211         fd = fopen(dst, "w");
212         if (fd == NULL) {
213                 fprintf(stderr, "Cannot open '%s' for write\n", dst);
214                 return -1;
215         }
216
217         fd2 = fopen(src, "r");
218         if (fd2 == NULL) {
219                 /* empty file */
220                 fclose(fd);
221                 return 0;
222         }
223
224         line[sizeof(line) - 1] = 0;
225         do {
226                 p = fgets(line, sizeof(line) - 1, fd2);
227                 if (p == NULL)
228                         break;
229
230                 fputs(line, fd);
231         }
232         while (1);
233
234         fclose(fd);
235         fclose(fd2);
236
237         return 0;
238 }
239
240 /* accepts password file */
241 static int find_strchr(const char *username, const char *file)
242 {
243         FILE *fd;
244         char *pos;
245         char line[5 * 1024];
246         unsigned int i;
247
248         fd = fopen(file, "r");
249         if (fd == NULL) {
250                 fprintf(stderr, "Cannot open file '%s'\n", file);
251                 return -1;
252         }
253
254         while (fgets(line, sizeof(line), fd) != NULL) {
255                 /* move to first ':' */
256                 i = 0;
257                 while ((line[i] != ':') && (line[i] != '\0')
258                        && (i < sizeof(line))) {
259                         i++;
260                 }
261                 if (strncmp(username, line, MAX(i, strlen(username))) == 0) {
262                         /* find the index */
263                         pos = strrchr(line, ':');
264                         pos++;
265                         fclose(fd);
266                         return atoi(pos);
267                 }
268         }
269
270         fclose(fd);
271         return -1;
272 }
273
274 /* Parses the tpasswd files, in order to verify the given
275  * username/password pair.
276  */
277 static int
278 verify_passwd(const char *conffile, const char *tpasswd,
279               const char *username, const char *passwd)
280 {
281         FILE *fd;
282         char line[5 * 1024];
283         unsigned int i;
284         gnutls_datum_t g, n;
285         int iindex;
286         char *p, *pos;
287
288         iindex = find_strchr(username, tpasswd);
289         if (iindex == -1) {
290                 fprintf(stderr, "Cannot find '%s' in %s\n", username,
291                         tpasswd);
292                 return -1;
293         }
294
295         fd = fopen(conffile, "r");
296         if (fd == NULL) {
297                 fprintf(stderr, "Cannot find %s\n", conffile);
298                 return -1;
299         }
300
301         do {
302                 p = fgets(line, sizeof(line) - 1, fd);
303         }
304         while (p != NULL && atoi(p) != iindex);
305
306         if (p == NULL) {
307                 fprintf(stderr, "Cannot find entry in %s\n", conffile);
308                 return -1;
309         }
310         line[sizeof(line) - 1] = 0;
311
312         fclose(fd);
313
314         if ((iindex = read_conf_values(&g, &n, line)) < 0) {
315                 fprintf(stderr, "Cannot parse conf file '%s'\n", conffile);
316                 return -1;
317         }
318
319         fd = fopen(tpasswd, "r");
320         if (fd == NULL) {
321                 fprintf(stderr, "Cannot open file '%s'\n", tpasswd);
322                 return -1;
323         }
324
325         while (fgets(line, sizeof(line), fd) != NULL) {
326                 /* move to first ':' 
327                  * This is the actual verifier.
328                  */
329                 i = 0;
330                 while ((line[i] != ':') && (line[i] != '\0')
331                        && (i < sizeof(line))) {
332                         i++;
333                 }
334                 if (strncmp(username, line, MAX(i, strlen(username))) == 0) {
335                         char *verifier_pos, *salt_pos;
336
337                         pos = strchr(line, ':');
338                         fclose(fd);
339                         if (pos == NULL) {
340                                 fprintf(stderr,
341                                         "Cannot parse conf file '%s'\n",
342                                         conffile);
343                                 return -1;
344                         }
345                         pos++;
346                         verifier_pos = pos;
347
348                         /* Move to the salt */
349                         pos = strchr(pos, ':');
350                         if (pos == NULL) {
351                                 fprintf(stderr,
352                                         "Cannot parse conf file '%s'\n",
353                                         conffile);
354                                 return -1;
355                         }
356                         pos++;
357                         salt_pos = pos;
358
359                         return _verify_passwd_int(username, passwd,
360                                                   verifier_pos, salt_pos,
361                                                   &g, &n);
362                 }
363         }
364
365         fclose(fd);
366         return -1;
367
368 }
369
370 #define KPASSWD "/etc/tpasswd"
371 #define KPASSWD_CONF "/etc/tpasswd.conf"
372
373 static void tls_log_func(int level, const char *str)
374 {
375         fprintf(stderr, "|<%d>| %s", level, str);
376 }
377
378 int main(int argc, char **argv)
379 {
380         const char *passwd;
381         int salt_size, ret;
382         int optct;
383         const char *fpasswd, *fpasswd_conf;
384         const char *username;
385 #ifndef _WIN32
386         struct passwd *pwd;
387 #endif
388
389         if ((ret = gnutls_global_init()) < 0) {
390                 fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret));
391                 exit(1);
392         }
393
394         umask(066);
395
396         optct = optionProcess(&srptoolOptions, argc, argv);
397         argc -= optct;
398         argv += optct;
399
400         gnutls_global_set_log_function(tls_log_func);
401         gnutls_global_set_log_level(OPT_VALUE_DEBUG);
402
403         if (HAVE_OPT(CREATE_CONF)) {
404                 return generate_create_conf(OPT_ARG(CREATE_CONF));
405         }
406
407         if (HAVE_OPT(PASSWD))
408                 fpasswd = OPT_ARG(PASSWD);
409         else
410                 fpasswd = (char *) KPASSWD;
411
412         if (HAVE_OPT(PASSWD_CONF))
413                 fpasswd_conf = OPT_ARG(PASSWD_CONF);
414         else
415                 fpasswd_conf = (char *) KPASSWD_CONF;
416
417         if (HAVE_OPT(USERNAME))
418                 username = OPT_ARG(USERNAME);
419         else {
420 #ifndef _WIN32
421                 pwd = getpwuid(getuid());
422
423                 if (pwd == NULL) {
424                         fprintf(stderr, "No such user\n");
425                         return -1;
426                 }
427
428                 username = pwd->pw_name;
429 #else
430                 fprintf(stderr, "Please specify a user\n");
431                 return -1;
432 #endif
433         }
434
435         salt_size = 16;
436
437         passwd = getpass("Enter password: ");
438         if (passwd == NULL) {
439                 fprintf(stderr, "Please specify a password\n");
440                 return -1;
441         }
442
443 /* not ready yet */
444         if (HAVE_OPT(VERIFY)) {
445                 return verify_passwd(fpasswd_conf, fpasswd,
446                                      username, passwd);
447         }
448
449
450         return crypt_int(username, passwd, salt_size,
451                          fpasswd_conf, fpasswd, OPT_VALUE_INDEX);
452
453 }
454
455 static char *_srp_crypt(const char *username, const char *passwd,
456                         int salt_size, const gnutls_datum_t * g,
457                         const gnutls_datum_t * n)
458 {
459         unsigned char salt[128];
460         static char result[1024];
461         gnutls_datum_t dat_salt, txt_salt;
462         gnutls_datum_t verifier, txt_verifier;
463
464         if ((unsigned) salt_size > sizeof(salt))
465                 return NULL;
466
467         /* generate the salt
468          */
469         if (gnutls_rnd(GNUTLS_RND_NONCE, salt, salt_size) < 0) {
470                 fprintf(stderr, "Could not create nonce\n");
471                 return NULL;
472         }
473
474         dat_salt.data = salt;
475         dat_salt.size = salt_size;
476
477         if (gnutls_srp_verifier
478             (username, passwd, &dat_salt, g, n, &verifier) < 0) {
479                 fprintf(stderr, "Error getting verifier\n");
480                 return NULL;
481         }
482
483         /* base64 encode the verifier */
484         if (gnutls_srp_base64_encode_alloc(&verifier, &txt_verifier) < 0) {
485                 fprintf(stderr, "Error encoding\n");
486                 free(verifier.data);
487                 return NULL;
488         }
489
490         free(verifier.data);
491
492         if (gnutls_srp_base64_encode_alloc(&dat_salt, &txt_salt) < 0) {
493                 fprintf(stderr, "Error encoding\n");
494                 return NULL;
495         }
496
497         sprintf(result, "%s:%s", txt_verifier.data, txt_salt.data);
498         free(txt_salt.data);
499         free(txt_verifier.data);
500
501         return result;
502
503 }
504
505
506 int
507 crypt_int(const char *username, const char *passwd, int salt_size,
508           const char *tpasswd_conf, const char *tpasswd, int uindex)
509 {
510         FILE *fd;
511         char *cr;
512         gnutls_datum_t g, n;
513         char line[5 * 1024];
514         char *p, *pp;
515         int iindex;
516         char tmpname[1024];
517
518         fd = fopen(tpasswd_conf, "r");
519         if (fd == NULL) {
520                 fprintf(stderr, "Cannot find %s\n", tpasswd_conf);
521                 return -1;
522         }
523
524         do {                    /* find the specified uindex in file */
525                 p = fgets(line, sizeof(line) - 1, fd);
526         }
527         while (p != NULL && (iindex = atoi(p)) != uindex);
528
529         if (p == NULL) {
530                 fprintf(stderr, "Cannot find entry in %s\n", tpasswd_conf);
531                 return -1;
532         }
533         line[sizeof(line) - 1] = 0;
534
535         fclose(fd);
536         if ((iindex = read_conf_values(&g, &n, line)) < 0) {
537                 fprintf(stderr, "Cannot parse conf file '%s'\n",
538                         tpasswd_conf);
539                 return -1;
540         }
541
542         cr = _srp_crypt(username, passwd, salt_size, &g, &n);
543         if (cr == NULL) {
544                 fprintf(stderr, "Cannot _srp_crypt()...\n");
545                 return -1;
546         } else {
547                 /* delete previous entry */
548                 struct stat st;
549                 FILE *fd2;
550                 int put;
551
552                 if (strlen(tpasswd) + 5 > sizeof(tmpname)) {
553                         fprintf(stderr, "file '%s' is tooooo long\n",
554                                 tpasswd);
555                         return -1;
556                 }
557
558                 snprintf(tmpname, sizeof(tmpname), "%s.tmp", tpasswd);
559
560                 if (stat(tmpname, &st) != -1) {
561                         fprintf(stderr, "file '%s' is locked\n", tpasswd);
562                         return -1;
563                 }
564
565                 if (filecopy(tpasswd, tmpname) != 0) {
566                         fprintf(stderr, "Cannot copy '%s' to '%s'\n",
567                                 tpasswd, tmpname);
568                         return -1;
569                 }
570
571                 fd = fopen(tpasswd, "w");
572                 if (fd == NULL) {
573                         fprintf(stderr, "Cannot open '%s' for write\n",
574                                 tpasswd);
575                         remove(tmpname);
576                         return -1;
577                 }
578
579                 fd2 = fopen(tmpname, "r");
580                 if (fd2 == NULL) {
581                         fprintf(stderr, "Cannot open '%s' for read\n",
582                                 tmpname);
583                         remove(tmpname);
584                         return -1;
585                 }
586
587                 put = 0;
588                 do {
589                         p = fgets(line, sizeof(line) - 1, fd2);
590                         if (p == NULL)
591                                 break;
592
593                         pp = strchr(line, ':');
594                         if (pp == NULL)
595                                 continue;
596
597                         if (strncmp(p, username,
598                                     MAX(strlen(username),
599                                         (unsigned int) (pp - p))) == 0) {
600                                 put = 1;
601                                 fprintf(fd, "%s:%s:%u\n", username, cr,
602                                         iindex);
603                         } else {
604                                 fputs(line, fd);
605                         }
606                 }
607                 while (1);
608
609                 if (put == 0) {
610                         fprintf(fd, "%s:%s:%u\n", username, cr, iindex);
611                 }
612
613                 fclose(fd);
614                 fclose(fd2);
615
616                 remove(tmpname);
617
618         }
619
620
621         return 0;
622 }
623
624
625
626 /* this function parses tpasswd.conf file. Format is:
627  * int(index):base64(n):base64(g)
628  */
629 static int
630 read_conf_values(gnutls_datum_t * g, gnutls_datum_t * n, char *str)
631 {
632         char *p;
633         int len;
634         int index, ret;
635         gnutls_datum_t dat;
636
637         index = atoi(str);
638
639         p = strrchr(str, ':');  /* we have g */
640         if (p == NULL) {
641                 return -1;
642         }
643
644         *p = '\0';
645         p++;
646
647         /* read the generator */
648         len = strlen(p);
649         if (p[len - 1] == '\n')
650                 len--;
651
652         dat.data = (void *) p;
653         dat.size = len;
654         ret = gnutls_srp_base64_decode_alloc(&dat, g);
655
656         if (ret < 0) {
657                 fprintf(stderr, "Decoding error\n");
658                 return -1;
659         }
660
661         /* now go for n - modulo */
662         p = strrchr(str, ':');  /* we have n */
663         if (p == NULL) {
664                 return -1;
665         }
666
667         *p = '\0';
668         p++;
669
670         dat.data = (void *) p;
671         dat.size = strlen(p);
672
673         ret = gnutls_srp_base64_decode_alloc(&dat, n);
674
675         if (ret < 0) {
676                 fprintf(stderr, "Decoding error\n");
677                 free(g->data);
678                 return -1;
679         }
680
681         return index;
682 }