Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

gdp / apps / gdp-create.c @ master

History | View | Annotate | Download (11.4 KB)

1
/* vim: set ai sw=4 sts=4 ts=4 : */
2

    
3
/*
4
**  LOG-CREATE --- create a new log
5
**
6
**                This is a temporary interface.  Ultimately we need a log
7
**                creation service that does reasonable log placement rather
8
**                than having to name a specific log server.
9
**
10
**        ----- BEGIN LICENSE BLOCK -----
11
**        Applications for the Global Data Plane
12
**        From the Ubiquitous Swarm Lab, 490 Cory Hall, U.C. Berkeley.
13
**
14
**        Copyright (c) 2015-2019, Regents of the University of California.
15
**        All rights reserved.
16
**
17
**        Permission is hereby granted, without written agreement and without
18
**        license or royalty fees, to use, copy, modify, and distribute this
19
**        software and its documentation for any purpose, provided that the above
20
**        copyright notice and the following two paragraphs appear in all copies
21
**        of this software.
22
**
23
**        IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
24
**        SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
25
**        PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
26
**        EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
**
28
**        REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
29
**        LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30
**        FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION,
31
**        IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO
32
**        OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
33
**        OR MODIFICATIONS.
34
**        ----- END LICENSE BLOCK -----
35
*/
36

    
37
#include <ep/ep.h>
38
#include <ep/ep_app.h>
39
//#include <ep/ep_crypto.h>
40
#include <ep/ep_dbg.h>
41
//#include <ep/ep_string.h>
42
#include <gdp/gdp.h>
43

    
44
#include <errno.h>
45
#include <string.h>
46
#include <sysexits.h>
47
#include <sys/stat.h>
48

    
49

    
50
//static EP_DBG        Dbg = EP_DBG_INIT("gdp-create", "Create new log");
51

    
52

    
53
// command line options
54
#define OPTIONS                "b:c:C:D:e:G:h:k:K:qs:SwW:"
55

    
56
void
57
usage(void)
58
{
59
        fprintf(stderr, "Usage: %s [-C creator] [-D dbgspec] [-e key_enc_alg]\n"
60
                        "\t[-G gdpd_addr] [-q] [-s logd_name] [-S]\n"
61
                        "\t[-h] [-k keytype] [-K owner_keyfile] [-w] [-W writer_keyfile]\n"
62
                        "\t[-b keybits] [-c curve] [<mdid>=<metadata>...] [log_name]\n"
63
                        "    -C  set name of log creator (owner; for metadata)\n"
64
                        "    -D  set debugging flags\n"
65
                        "    -e  set secret key encryption algorithm\n"
66
                        "    -G  IP host to contact for GDP router\n"
67
                        "    -q  don't give error if log already exists\n"
68
                        "    -s  set creation service name\n"
69
                        "    -S  skip the test to see if log already exists (for debugging)\n"
70
                        "    -h  set the hash (message digest) algorithm (defaults to sha256)\n"
71
                        "    -k  type of key; valid key types are \"rsa\", \"dsa\", and \"ec\"\n"
72
                        "\t(defaults to ec); \"none\" turns off key generation\n"
73
                        "    -K  use indicated key/place to write secret owner key\n"
74
                        "    -w  create separate writer key\n"
75
                        "    -W  use indicated key/place to write secret writer key\n"
76
                        "\tIf -K or -W specify a directory, a .pem file is written there\n"
77
                        "\twith the name of the GCL (defaults to \"KEYS\" or \".\")\n"
78
                        "    -b  set size of key in bits (RSA and DSA only)\n"
79
                        "    -c  set curve name (EC only)\n"
80
                        "    creator is the id of the creator, formatted as an email address\n"
81
                        "    logd_name is the name of the log server to host this log\n"
82
                        "    metadata ids are (by convention) four letters or digits\n"
83
                        "    log_name is the name of the log to create\n",
84
                        ep_app_getprogname());
85
        exit(EX_USAGE);
86
}
87

    
88

    
89
int
90
main(int argc, char **argv)
91
{
92
        const char *gobxname = NULL;        // external name of GCL
93
        const char *logdxname = NULL;        // external name of log daemon
94
        const char *cname = NULL;                // creator/owner name (for metadata)
95
        gdp_gin_t *gin = NULL;
96
        gdp_create_info_t *gci = NULL;
97
        int opt;
98
        EP_STAT estat;
99
        char *gdpd_addr = NULL;
100
        bool show_usage = false;
101
        bool quiet = false;
102
        bool skip_existence_test = false;
103
        const char *dig_alg_name = "def";
104
        const char *key_alg_name = "def";
105
        int key_bits = 0;
106
        const char *key_curve = NULL;
107
        const char *owner_keyfile = NULL;
108
        const char *writer_keyfile = NULL;
109
        const char *key_enc_alg_name = "def";
110
        bool separate_writer_key = false;
111
        const char *phase = NULL;
112

    
113
        // quick pass so debugging is on for initialization
114
        while ((opt = getopt(argc, argv, OPTIONS)) > 0)
115
        {
116
                if (opt == 'D')
117
                        ep_dbg_set(optarg);
118
        }
119
        optind = 1;
120
#if EP_OSCF_NEED_OPTRESET
121
        optreset = 1;
122
#endif
123

    
124
        // preinit library (must be early due to crypto code in arg processing)
125
        ep_crypto_init(0);
126

    
127
        // collect command-line arguments
128
        while ((opt = getopt(argc, argv, OPTIONS)) > 0)
129
        {
130
                switch (opt)
131
                {
132
                 case 'b':
133
                        key_bits = atoi(optarg);
134
                        break;
135

    
136
                 case 'c':
137
                        key_curve = optarg;
138
                        break;
139

    
140
                 case 'C':
141
                        cname = optarg;
142
                        break;
143

    
144
                 case 'D':
145
                        // already done
146
                        break;
147

    
148
                 case 'e':
149
                        if (ep_crypto_keyenc_byname(optarg) < 0)
150
                        {
151
                                ep_app_error("unknown key encryption algorithm %s", optarg);
152
                                show_usage = true;
153
                        }
154
                        key_enc_alg_name = optarg;
155
                        break;
156

    
157
                 case 'G':
158
                        gdpd_addr = optarg;
159
                        break;
160

    
161
                 case 'h':
162
                        if (ep_crypto_md_alg_byname(optarg) < 0)
163
                        {
164
                                ep_app_error("unknown digest hash algorithm %s", optarg);
165
                                show_usage = true;
166
                        }
167
                        dig_alg_name = optarg;
168
                        break;
169

    
170
                 case 'k':
171
                        if (ep_crypto_keytype_byname(optarg) == EP_CRYPTO_KEYTYPE_UNKNOWN)
172
                        {
173
                                ep_app_error("unknown key type %s", optarg);
174
                                show_usage = true;
175
                        }
176
                        key_alg_name = optarg;
177
                        break;
178

    
179
                 case 'K':
180
                        owner_keyfile = optarg;
181
                        break;
182

    
183
                 case 'q':
184
                        quiet = true;
185
                        break;
186

    
187
                 case 's':
188
                        logdxname = optarg;
189
                        break;
190

    
191
                 case 'S':
192
                        skip_existence_test = true;
193
                        break;
194

    
195
                 case 'w':
196
                        separate_writer_key = true;
197
                        break;
198

    
199
                 case 'W':
200
                        writer_keyfile = optarg;
201
                        separate_writer_key = true;
202
                        break;
203

    
204
                 default:
205
                        show_usage = true;
206
                        break;
207
                }
208
        }
209
        argc -= optind;
210
        argv += optind;
211

    
212
        if (show_usage || argc < 0)
213
                usage();
214

    
215
        // initialize the GDP library
216
        phase = "initializing GDP";
217
        estat = gdp_init(gdpd_addr);
218
        if (!EP_STAT_ISOK(estat))
219
                goto fail0;
220

    
221
        // allow thread to settle to avoid interspersed debug output
222
        ep_time_nanosleep(INT64_C(100000000));
223

    
224
        gci = gdp_create_info_new();
225

    
226
        // collect any metadata and external name
227
        while (argc > 0)
228
        {
229
                char *p;
230
                if ((p = strchr(argv[0], '=')) != NULL)
231
                {
232
                        gdp_md_id_t mdid = 0;
233
                        int i;
234

    
235
                        p++;
236
                        for (i = 0; i < 4; i++)
237
                        {
238
                                if (argv[0][i] == '=')
239
                                        break;
240
                                mdid = (mdid << 8) | (unsigned) argv[0][i];
241
                        }
242
                        if (argv[0][i] != '=')
243
                                ep_app_warn("metadata id %.*s truncated to 4 characters",
244
                                                (int) (p - argv[0] - 1), argv[0]);
245

    
246
                        gdp_create_info_add_metadata(gci, mdid, strlen(p), p);
247
                }
248
                else if (gobxname == NULL)
249
                {
250
                        gobxname = *argv;
251
                }
252
                else
253
                {
254
                        usage();
255
                }
256

    
257
                argc--;
258
                argv++;
259
        }
260

    
261
        // name is optional ; if omitted one will be created
262
        if (argc-- > 0)
263
                gobxname = *argv++;
264
        if (gobxname != NULL && gobxname[0] == '0')
265
                gobxname = NULL;
266

    
267
        if (show_usage || argc > 0)
268
                usage();
269

    
270
        if (gobxname != NULL)
271
        {
272
                // check validity of the name
273
                gdp_name_t gobiname;
274
                if (EP_STAT_ISOK(gdp_internal_name(gobxname, gobiname)))
275
                {
276
                        // this is a valid base64-encooded log name: not appropriate
277
                        estat = GDP_STAT_GDP_NAME_INVALID;
278
                        if (!quiet)
279
                        {
280
                                ep_app_error("Cannot choose internal GDPname (%s)",
281
                                                gobxname);
282
                                exit(EX_CANTCREAT);
283
                        }
284
                }
285
                if (!skip_existence_test)
286
                {
287
                        /*
288
                        **  If the external name is already known in the
289
                        **  Human-Oriented Name to GDPname directory, OR if the
290
                        **  hashed (old-style) internal name can be found,
291
                        **  then there is a conflict.
292
                        */
293

    
294
                        gin = NULL;
295
                        estat = gdp_name_parse(gobxname, gobiname, NULL);
296
                        if (EP_STAT_ISWARN(estat))
297
                        {
298
                                // iname used is the SHA256 of the xname
299
                                estat = gdp_gin_open(gobiname, GDP_MODE_RO, NULL, &gin);
300
                        }
301
                        if (EP_STAT_ISOK(estat))
302
                        {
303
                                // oops, we shouldn't be able to parse or open it
304
                                if (gin != NULL)
305
                                        (void) gdp_gin_close(gin);
306
                                if (!quiet)
307
                                        ep_app_error("Cannot create %s: already exists", gobxname);
308
                                exit(EX_CANTCREAT);
309
                        }
310
                }
311
        }
312

    
313
        /**************************************************************
314
        **  Set up other automatic metadata
315
        */
316

    
317
        if (cname != NULL)
318
        {
319
                phase = "setting creator";
320
                estat = gdp_create_info_set_creator(gci, cname, NULL);
321
                EP_STAT_CHECK(estat, goto fail1);
322
        }
323

    
324
        if (logdxname != NULL)
325
        {
326
                phase = "setting creation service";
327
                estat = gdp_create_info_set_creation_service(gci, logdxname);
328
                // OK if iname is hash of xname, which presents as a warning
329
                if (EP_STAT_ISFAIL(estat))
330
                        goto fail1;
331
        }
332

    
333
        if (owner_keyfile != NULL)
334
        {
335
                // could be an existing file or a directory
336
                phase = "setting owner key";
337
                struct stat st;
338
                int istat = stat(owner_keyfile, &st);
339

    
340
                if (istat < 0)
341
                {
342
                        // non-existent file: error
343
                        ep_app_error("%s must exist", owner_keyfile);
344
                        estat = ep_stat_from_errno(ENOENT);
345
                }
346
                else if ((st.st_mode & S_IFMT) == S_IFDIR)
347
                {
348
                        // arrange for key to be written to this directory
349
                        phase = "creating owner key";
350
                        ep_adm_setparam("swarm.gdp.crypto.key.dir", owner_keyfile);
351
                        estat = gdp_create_info_new_owner_key(gci, dig_alg_name,
352
                                                        key_alg_name, key_bits, key_curve,
353
                                                        key_enc_alg_name);
354
                }
355
                else
356
                {
357
                        // existing key file --- use it instead of creating new key
358
                        EP_CRYPTO_KEY *key = NULL;
359
                        key = ep_crypto_key_read_file(owner_keyfile,
360
                                                                EP_CRYPTO_KEYFORM_UNKNOWN, EP_CRYPTO_F_SECRET);
361
                        estat = gdp_create_info_set_owner_key(gci, key, dig_alg_name);
362
                }
363
        }
364
        else
365
        {
366
                phase = "creating owner key";
367
                estat = gdp_create_info_new_owner_key(gci, dig_alg_name, key_alg_name,
368
                                                key_bits, key_curve, key_enc_alg_name);
369
        }
370
        EP_STAT_CHECK(estat, goto fail1);
371

    
372
        if (writer_keyfile != NULL)
373
        {
374
                phase = "setting writer key";
375
                EP_CRYPTO_KEY *key;
376
                key = ep_crypto_key_read_file(writer_keyfile, 0, EP_CRYPTO_F_SECRET);
377
                estat = gdp_create_info_set_writer_key(gci, key, dig_alg_name);
378
        }
379
        else if (separate_writer_key)
380
        {
381
                phase = "creating writer key";
382
                estat = gdp_create_info_new_writer_key(gci, dig_alg_name, key_alg_name,
383
                                                key_bits, key_curve, key_enc_alg_name);
384
        }
385
        EP_STAT_CHECK(estat, goto fail1);
386

    
387
        // if the user specified an unqualified name and we have a root name,
388
        // use the extended name.
389
        const char *root = gdp_name_root_get();
390
        char xnamebuf[GDP_HUMAN_NAME_MAX + 1];
391
        if (gobxname != NULL && strchr(gobxname, '.') == NULL && root != NULL)
392
        {
393
                snprintf(xnamebuf, sizeof xnamebuf, "%s.%s", root, gobxname);
394
                gobxname = xnamebuf;
395
        }
396

    
397
        /*
398
        **  Hello sailor, this is where the actual creation happens
399
        */
400

    
401
        phase = "creating GDP object";
402
        estat = gdp_gin_create(gci, gobxname, &gin);
403
        EP_STAT_CHECK(estat, goto fail1);
404

    
405
        // just for a lark, let the user know the (internal) name
406
        if (!quiet)
407
        {
408
                gdp_pname_t pname;
409

    
410
                // this output gets parsed by gdp-rest --- don't change it!
411
                printf("Created new GCL %s\n",
412
                                gdp_printable_name(*gdp_gin_getname(gin), pname));
413
        }
414

    
415
        gdp_gin_close(gin);
416

    
417
fail1:
418
        // free creation information
419
        gdp_create_info_free(&gci);
420

    
421
fail0:
422
        if (!EP_STAT_ISOK(estat))
423
        {
424
                char ebuf[100];
425
                ep_app_error("Creation failed while %s: %s",
426
                                phase, ep_stat_tostr(estat, ebuf, sizeof ebuf));
427
        }
428

    
429
        int exitstat;
430
        if (EP_STAT_ISOK(estat))
431
                exitstat = EX_OK;
432
        else if (EP_STAT_IS_SAME(estat, GDP_STAT_NAK_NOROUTE))
433
                exitstat = EX_NOHOST;
434
        else if (EP_STAT_IS_SAME(estat, GDP_STAT_NAK_CONFLICT))
435
                exitstat = EX_CANTCREAT;
436
        else if (EP_STAT_ISABORT(estat))
437
                exitstat = EX_SOFTWARE;
438
        else
439
                exitstat = EX_UNAVAILABLE;
440

    
441
        // OK status can have values; hide that from the user
442
        if (EP_STAT_ISOK(estat))
443
                estat = EP_STAT_OK;
444

    
445
        if (!EP_STAT_ISOK(estat))
446
                ep_app_message(estat, "exiting with status");
447
        else if (!quiet)
448
                fprintf(stderr, "Exiting with status OK\n");
449

    
450
        return exitstat;
451
}