1/*
2 * Copyright 2004-2021 The OpenSSL Project Authors. All Rights Reserved.
3 * Copyright (c) 2004, EdelKey Project. All Rights Reserved.
4 *
5 * Licensed under the Apache License 2.0 (the "License").  You may not use
6 * this file except in compliance with the License.  You can obtain a copy
7 * in the file LICENSE in the source distribution or at
8 * https://www.openssl.org/source/license.html
9 *
10 * Originally written by Christophe Renou and Peter Sylvester,
11 * for the EdelKey project.
12 */
13
14/* SRP is deprecated, so we're going to have to use some deprecated APIs */
15#define OPENSSL_SUPPRESS_DEPRECATED
16
17#include <openssl/opensslconf.h>
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <openssl/conf.h>
23#include <openssl/bio.h>
24#include <openssl/err.h>
25#include <openssl/txt_db.h>
26#include <openssl/buffer.h>
27#include <openssl/srp.h>
28#include "apps.h"
29#include "progs.h"
30
31#define BASE_SECTION    "srp"
32#define CONFIG_FILE "openssl.cnf"
33
34
35#define ENV_DATABASE            "srpvfile"
36#define ENV_DEFAULT_SRP         "default_srp"
37
38static int get_index(CA_DB *db, char *id, char type)
39{
40    char **pp;
41    int i;
42    if (id == NULL)
43        return -1;
44    if (type == DB_SRP_INDEX) {
45        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
46            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
47            if (pp[DB_srptype][0] == DB_SRP_INDEX
48                && strcmp(id, pp[DB_srpid]) == 0)
49                return i;
50        }
51    } else {
52        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
53            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
54
55            if (pp[DB_srptype][0] != DB_SRP_INDEX
56                && strcmp(id, pp[DB_srpid]) == 0)
57                return i;
58        }
59    }
60
61    return -1;
62}
63
64static void print_entry(CA_DB *db, int indx, int verbose, char *s)
65{
66    if (indx >= 0 && verbose) {
67        int j;
68        char **pp = sk_OPENSSL_PSTRING_value(db->db->data, indx);
69        BIO_printf(bio_err, "%s \"%s\"\n", s, pp[DB_srpid]);
70        for (j = 0; j < DB_NUMBER; j++) {
71            BIO_printf(bio_err, "  %d = \"%s\"\n", j, pp[j]);
72        }
73    }
74}
75
76static void print_index(CA_DB *db, int indexindex, int verbose)
77{
78    print_entry(db, indexindex, verbose, "g N entry");
79}
80
81static void print_user(CA_DB *db, int userindex, int verbose)
82{
83    if (verbose > 0) {
84        char **pp = sk_OPENSSL_PSTRING_value(db->db->data, userindex);
85
86        if (pp[DB_srptype][0] != 'I') {
87            print_entry(db, userindex, verbose, "User entry");
88            print_entry(db, get_index(db, pp[DB_srpgN], 'I'), verbose,
89                        "g N entry");
90        }
91
92    }
93}
94
95static int update_index(CA_DB *db, char **row)
96{
97    char **irow;
98    int i;
99
100    irow = app_malloc(sizeof(*irow) * (DB_NUMBER + 1), "row pointers");
101    for (i = 0; i < DB_NUMBER; i++)
102        irow[i] = row[i];
103    irow[DB_NUMBER] = NULL;
104
105    if (!TXT_DB_insert(db->db, irow)) {
106        BIO_printf(bio_err, "failed to update srpvfile\n");
107        BIO_printf(bio_err, "TXT_DB error number %ld\n", db->db->error);
108        OPENSSL_free(irow);
109        return 0;
110    }
111    return 1;
112}
113
114static char *lookup_conf(const CONF *conf, const char *section, const char *tag)
115{
116    char *entry = NCONF_get_string(conf, section, tag);
117    if (entry == NULL)
118        BIO_printf(bio_err, "variable lookup failed for %s::%s\n", section, tag);
119    return entry;
120}
121
122static char *srp_verify_user(const char *user, const char *srp_verifier,
123                             char *srp_usersalt, const char *g, const char *N,
124                             const char *passin, int verbose)
125{
126    char password[1025];
127    PW_CB_DATA cb_tmp;
128    char *verifier = NULL;
129    char *gNid = NULL;
130    int len;
131
132    cb_tmp.prompt_info = user;
133    cb_tmp.password = passin;
134
135    len = password_callback(password, sizeof(password)-1, 0, &cb_tmp);
136    if (len > 0) {
137        password[len] = 0;
138        if (verbose)
139            BIO_printf(bio_err,
140                       "Validating\n   user=\"%s\"\n srp_verifier=\"%s\"\n srp_usersalt=\"%s\"\n g=\"%s\"\n N=\"%s\"\n",
141                       user, srp_verifier, srp_usersalt, g, N);
142        if (verbose > 1)
143            BIO_printf(bio_err, "Pass %s\n", password);
144
145        OPENSSL_assert(srp_usersalt != NULL);
146        if ((gNid = SRP_create_verifier(user, password, &srp_usersalt,
147                                        &verifier, N, g)) == NULL) {
148            BIO_printf(bio_err, "Internal error validating SRP verifier\n");
149        } else {
150            if (strcmp(verifier, srp_verifier))
151                gNid = NULL;
152            OPENSSL_free(verifier);
153        }
154        OPENSSL_cleanse(password, len);
155    }
156    return gNid;
157}
158
159static char *srp_create_user(char *user, char **srp_verifier,
160                             char **srp_usersalt, char *g, char *N,
161                             char *passout, int verbose)
162{
163    char password[1025];
164    PW_CB_DATA cb_tmp;
165    char *gNid = NULL;
166    char *salt = NULL;
167    int len;
168    cb_tmp.prompt_info = user;
169    cb_tmp.password = passout;
170
171    len = password_callback(password, sizeof(password)-1, 1, &cb_tmp);
172    if (len > 0) {
173        password[len] = 0;
174        if (verbose)
175            BIO_printf(bio_err, "Creating\n user=\"%s\"\n g=\"%s\"\n N=\"%s\"\n",
176                       user, g, N);
177        if ((gNid = SRP_create_verifier(user, password, &salt,
178                                        srp_verifier, N, g)) == NULL) {
179            BIO_printf(bio_err, "Internal error creating SRP verifier\n");
180        } else {
181            *srp_usersalt = salt;
182        }
183        OPENSSL_cleanse(password, len);
184        if (verbose > 1)
185            BIO_printf(bio_err, "gNid=%s salt =\"%s\"\n verifier =\"%s\"\n",
186                       gNid, salt, *srp_verifier);
187
188    }
189    return gNid;
190}
191
192typedef enum OPTION_choice {
193    OPT_COMMON,
194    OPT_VERBOSE, OPT_CONFIG, OPT_NAME, OPT_SRPVFILE, OPT_ADD,
195    OPT_DELETE, OPT_MODIFY, OPT_LIST, OPT_GN, OPT_USERINFO,
196    OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE, OPT_R_ENUM, OPT_PROV_ENUM
197} OPTION_CHOICE;
198
199const OPTIONS srp_options[] = {
200    {OPT_HELP_STR, 1, '-', "Usage: %s [options] [user...]\n"},
201
202    OPT_SECTION("General"),
203    {"help", OPT_HELP, '-', "Display this summary"},
204    {"verbose", OPT_VERBOSE, '-', "Talk a lot while doing things"},
205    {"config", OPT_CONFIG, '<', "A config file"},
206    {"name", OPT_NAME, 's', "The particular srp definition to use"},
207#ifndef OPENSSL_NO_ENGINE
208    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
209#endif
210
211    OPT_SECTION("Action"),
212    {"add", OPT_ADD, '-', "Add a user and SRP verifier"},
213    {"modify", OPT_MODIFY, '-', "Modify the SRP verifier of an existing user"},
214    {"delete", OPT_DELETE, '-', "Delete user from verifier file"},
215    {"list", OPT_LIST, '-', "List users"},
216
217    OPT_SECTION("Configuration"),
218    {"srpvfile", OPT_SRPVFILE, '<', "The srp verifier file name"},
219    {"gn", OPT_GN, 's', "Set g and N values to be used for new verifier"},
220    {"userinfo", OPT_USERINFO, 's', "Additional info to be set for user"},
221    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
222    {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
223
224    OPT_R_OPTIONS,
225    OPT_PROV_OPTIONS,
226
227    OPT_PARAMETERS(),
228    {"user", 0, 0, "Username(s) to process (optional)"},
229    {NULL}
230};
231
232int srp_main(int argc, char **argv)
233{
234    ENGINE *e = NULL;
235    CA_DB *db = NULL;
236    CONF *conf = NULL;
237    int gNindex = -1, maxgN = -1, ret = 1, errors = 0, verbose = 0, i;
238    int doupdatedb = 0, mode = OPT_ERR;
239    char *user = NULL, *passinarg = NULL, *passoutarg = NULL;
240    char *passin = NULL, *passout = NULL, *gN = NULL, *userinfo = NULL;
241    char *section = NULL;
242    char **gNrow = NULL, *configfile = NULL;
243    char *srpvfile = NULL, **pp, *prog;
244    OPTION_CHOICE o;
245
246    prog = opt_init(argc, argv, srp_options);
247    while ((o = opt_next()) != OPT_EOF) {
248        switch (o) {
249        case OPT_EOF:
250        case OPT_ERR:
251 opthelp:
252            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
253            goto end;
254        case OPT_HELP:
255            opt_help(srp_options);
256            ret = 0;
257            goto end;
258        case OPT_VERBOSE:
259            verbose++;
260            break;
261        case OPT_CONFIG:
262            configfile = opt_arg();
263            break;
264        case OPT_NAME:
265            section = opt_arg();
266            break;
267        case OPT_SRPVFILE:
268            srpvfile = opt_arg();
269            break;
270        case OPT_ADD:
271        case OPT_DELETE:
272        case OPT_MODIFY:
273        case OPT_LIST:
274            if (mode != OPT_ERR) {
275                BIO_printf(bio_err,
276                           "%s: Only one of -add/-delete/-modify/-list\n",
277                           prog);
278                goto opthelp;
279            }
280            mode = o;
281            break;
282        case OPT_GN:
283            gN = opt_arg();
284            break;
285        case OPT_USERINFO:
286            userinfo = opt_arg();
287            break;
288        case OPT_PASSIN:
289            passinarg = opt_arg();
290            break;
291        case OPT_PASSOUT:
292            passoutarg = opt_arg();
293            break;
294        case OPT_ENGINE:
295            e = setup_engine(opt_arg(), 0);
296            break;
297        case OPT_R_CASES:
298            if (!opt_rand(o))
299                goto end;
300            break;
301        case OPT_PROV_CASES:
302            if (!opt_provider(o))
303                goto end;
304            break;
305        }
306    }
307
308    /* Optional parameters are usernames. */
309    argc = opt_num_rest();
310    argv = opt_rest();
311
312    if (!app_RAND_load())
313        goto end;
314
315    if (srpvfile != NULL && configfile != NULL) {
316        BIO_printf(bio_err,
317                   "-srpvfile and -configfile cannot be specified together.\n");
318        goto end;
319    }
320    if (mode == OPT_ERR) {
321        BIO_printf(bio_err,
322                   "Exactly one of the options -add, -delete, -modify -list must be specified.\n");
323        goto opthelp;
324    }
325    if (mode == OPT_DELETE || mode == OPT_MODIFY || mode == OPT_ADD) {
326        if (argc == 0) {
327            BIO_printf(bio_err, "Need at least one user.\n");
328            goto opthelp;
329        }
330        user = *argv++;
331    }
332    if ((passinarg != NULL || passoutarg != NULL) && argc != 1) {
333        BIO_printf(bio_err,
334                   "-passin, -passout arguments only valid with one user.\n");
335        goto opthelp;
336    }
337
338    if (!app_passwd(passinarg, passoutarg, &passin, &passout)) {
339        BIO_printf(bio_err, "Error getting passwords\n");
340        goto end;
341    }
342
343    if (srpvfile == NULL) {
344        if (configfile == NULL)
345            configfile = default_config_file;
346
347        conf = app_load_config_verbose(configfile, verbose);
348        if (conf == NULL)
349            goto end;
350        if (configfile != default_config_file && !app_load_modules(conf))
351            goto end;
352
353        /* Lets get the config section we are using */
354        if (section == NULL) {
355            if (verbose)
356                BIO_printf(bio_err,
357                           "trying to read " ENV_DEFAULT_SRP
358                           " in " BASE_SECTION "\n");
359
360            section = lookup_conf(conf, BASE_SECTION, ENV_DEFAULT_SRP);
361            if (section == NULL)
362                goto end;
363        }
364
365        app_RAND_load_conf(conf, BASE_SECTION);
366
367        if (verbose)
368            BIO_printf(bio_err,
369                       "trying to read " ENV_DATABASE " in section \"%s\"\n",
370                       section);
371
372        srpvfile = lookup_conf(conf, section, ENV_DATABASE);
373        if (srpvfile == NULL)
374            goto end;
375    }
376
377    if (verbose)
378        BIO_printf(bio_err, "Trying to read SRP verifier file \"%s\"\n",
379                   srpvfile);
380
381    db = load_index(srpvfile, NULL);
382    if (db == NULL) {
383        BIO_printf(bio_err, "Problem with index file: %s (could not load/parse file)\n", srpvfile);
384        goto end;
385    }
386
387    /* Lets check some fields */
388    for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
389        pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
390
391        if (pp[DB_srptype][0] == DB_SRP_INDEX) {
392            maxgN = i;
393            if ((gNindex < 0) && (gN != NULL) && strcmp(gN, pp[DB_srpid]) == 0)
394                gNindex = i;
395
396            print_index(db, i, verbose > 1);
397        }
398    }
399
400    if (verbose)
401        BIO_printf(bio_err, "Database initialised\n");
402
403    if (gNindex >= 0) {
404        gNrow = sk_OPENSSL_PSTRING_value(db->db->data, gNindex);
405        print_entry(db, gNindex, verbose > 1, "Default g and N");
406    } else if (maxgN > 0 && !SRP_get_default_gN(gN)) {
407        BIO_printf(bio_err, "No g and N value for index \"%s\"\n", gN);
408        goto end;
409    } else {
410        if (verbose)
411            BIO_printf(bio_err, "Database has no g N information.\n");
412        gNrow = NULL;
413    }
414
415    if (verbose > 1)
416        BIO_printf(bio_err, "Starting user processing\n");
417
418    while (mode == OPT_LIST || user != NULL) {
419        int userindex = -1;
420
421        if (user != NULL && verbose > 1)
422            BIO_printf(bio_err, "Processing user \"%s\"\n", user);
423        if ((userindex = get_index(db, user, 'U')) >= 0)
424            print_user(db, userindex, (verbose > 0) || mode == OPT_LIST);
425
426        if (mode == OPT_LIST) {
427            if (user == NULL) {
428                BIO_printf(bio_err, "List all users\n");
429
430                for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++)
431                    print_user(db, i, 1);
432            } else if (userindex < 0) {
433                BIO_printf(bio_err,
434                           "user \"%s\" does not exist, ignored. t\n", user);
435                errors++;
436            }
437        } else if (mode == OPT_ADD) {
438            if (userindex >= 0) {
439                /* reactivation of a new user */
440                char **row =
441                    sk_OPENSSL_PSTRING_value(db->db->data, userindex);
442                BIO_printf(bio_err, "user \"%s\" reactivated.\n", user);
443                row[DB_srptype][0] = 'V';
444
445                doupdatedb = 1;
446            } else {
447                char *row[DB_NUMBER];
448                char *gNid;
449                row[DB_srpverifier] = NULL;
450                row[DB_srpsalt] = NULL;
451                row[DB_srpinfo] = NULL;
452                if (!
453                    (gNid =
454                     srp_create_user(user, &(row[DB_srpverifier]),
455                                     &(row[DB_srpsalt]),
456                                     gNrow ? gNrow[DB_srpsalt] : gN,
457                                     gNrow ? gNrow[DB_srpverifier] : NULL,
458                                     passout, verbose))) {
459                    BIO_printf(bio_err,
460                               "Cannot create srp verifier for user \"%s\", operation abandoned .\n",
461                               user);
462                    errors++;
463                    goto end;
464                }
465                row[DB_srpid] = OPENSSL_strdup(user);
466                row[DB_srptype] = OPENSSL_strdup("v");
467                row[DB_srpgN] = OPENSSL_strdup(gNid);
468
469                if ((row[DB_srpid] == NULL)
470                    || (row[DB_srpgN] == NULL)
471                    || (row[DB_srptype] == NULL)
472                    || (row[DB_srpverifier] == NULL)
473                    || (row[DB_srpsalt] == NULL)
474                    || (userinfo
475                        && ((row[DB_srpinfo] = OPENSSL_strdup(userinfo)) == NULL))
476                    || !update_index(db, row)) {
477                    OPENSSL_free(row[DB_srpid]);
478                    OPENSSL_free(row[DB_srpgN]);
479                    OPENSSL_free(row[DB_srpinfo]);
480                    OPENSSL_free(row[DB_srptype]);
481                    OPENSSL_free(row[DB_srpverifier]);
482                    OPENSSL_free(row[DB_srpsalt]);
483                    goto end;
484                }
485                doupdatedb = 1;
486            }
487        } else if (mode == OPT_MODIFY) {
488            if (userindex < 0) {
489                BIO_printf(bio_err,
490                           "user \"%s\" does not exist, operation ignored.\n",
491                           user);
492                errors++;
493            } else {
494
495                char **row =
496                    sk_OPENSSL_PSTRING_value(db->db->data, userindex);
497                char type = row[DB_srptype][0];
498                if (type == 'v') {
499                    BIO_printf(bio_err,
500                               "user \"%s\" already updated, operation ignored.\n",
501                               user);
502                    errors++;
503                } else {
504                    char *gNid;
505
506                    if (row[DB_srptype][0] == 'V') {
507                        int user_gN;
508                        char **irow = NULL;
509                        if (verbose)
510                            BIO_printf(bio_err,
511                                       "Verifying password for user \"%s\"\n",
512                                       user);
513                        if ((user_gN =
514                             get_index(db, row[DB_srpgN], DB_SRP_INDEX)) >= 0)
515                            irow =
516                                sk_OPENSSL_PSTRING_value(db->db->data,
517                                                         userindex);
518
519                        if (!srp_verify_user
520                            (user, row[DB_srpverifier], row[DB_srpsalt],
521                             irow ? irow[DB_srpsalt] : row[DB_srpgN],
522                             irow ? irow[DB_srpverifier] : NULL, passin,
523                             verbose)) {
524                            BIO_printf(bio_err,
525                                       "Invalid password for user \"%s\", operation abandoned.\n",
526                                       user);
527                            errors++;
528                            goto end;
529                        }
530                    }
531                    if (verbose)
532                        BIO_printf(bio_err, "Password for user \"%s\" ok.\n",
533                                   user);
534
535                    if (!
536                        (gNid =
537                         srp_create_user(user, &(row[DB_srpverifier]),
538                                         &(row[DB_srpsalt]),
539                                         gNrow ? gNrow[DB_srpsalt] : NULL,
540                                         gNrow ? gNrow[DB_srpverifier] : NULL,
541                                         passout, verbose))) {
542                        BIO_printf(bio_err,
543                                   "Cannot create srp verifier for user \"%s\", operation abandoned.\n",
544                                   user);
545                        errors++;
546                        goto end;
547                    }
548
549                    row[DB_srptype][0] = 'v';
550                    row[DB_srpgN] = OPENSSL_strdup(gNid);
551
552                    if (row[DB_srpid] == NULL
553                        || row[DB_srpgN] == NULL
554                        || row[DB_srptype] == NULL
555                        || row[DB_srpverifier] == NULL
556                        || row[DB_srpsalt] == NULL
557                        || (userinfo
558                            && ((row[DB_srpinfo] = OPENSSL_strdup(userinfo))
559                                == NULL)))
560                        goto end;
561
562                    doupdatedb = 1;
563                }
564            }
565        } else if (mode == OPT_DELETE) {
566            if (userindex < 0) {
567                BIO_printf(bio_err,
568                           "user \"%s\" does not exist, operation ignored. t\n",
569                           user);
570                errors++;
571            } else {
572                char **xpp = sk_OPENSSL_PSTRING_value(db->db->data, userindex);
573
574                BIO_printf(bio_err, "user \"%s\" revoked. t\n", user);
575                xpp[DB_srptype][0] = 'R';
576                doupdatedb = 1;
577            }
578        }
579        user = *argv++;
580        if (user == NULL) {
581            /* no more processing in any mode if no users left */
582            break;
583        }
584    }
585
586    if (verbose)
587        BIO_printf(bio_err, "User procession done.\n");
588
589    if (doupdatedb) {
590        /* Lets check some fields */
591        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
592            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
593
594            if (pp[DB_srptype][0] == 'v') {
595                pp[DB_srptype][0] = 'V';
596                print_user(db, i, verbose);
597            }
598        }
599
600        if (verbose)
601            BIO_printf(bio_err, "Trying to update srpvfile.\n");
602        if (!save_index(srpvfile, "new", db))
603            goto end;
604
605        if (verbose)
606            BIO_printf(bio_err, "Temporary srpvfile created.\n");
607        if (!rotate_index(srpvfile, "new", "old"))
608            goto end;
609
610        if (verbose)
611            BIO_printf(bio_err, "srpvfile updated.\n");
612    }
613
614    ret = (errors != 0);
615 end:
616    if (errors != 0)
617        if (verbose)
618            BIO_printf(bio_err, "User errors %d.\n", errors);
619
620    if (verbose)
621        BIO_printf(bio_err, "SRP terminating with code %d.\n", ret);
622
623    OPENSSL_free(passin);
624    OPENSSL_free(passout);
625    if (ret)
626        ERR_print_errors(bio_err);
627    NCONF_free(conf);
628    free_index(db);
629    release_engine(e);
630    return ret;
631}
632