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 |
} |