Merge pull request #115 from hfmanson/master
[tlspool] / tool / set_localid.c
1 /* tool/set_localid.c -- Add local identity credential
2  *
3  * Provide a config, a NAI and a type of credential.  The command erases all
4  * old entries that match and replaces them with what is provided, if anything.
5  * The provided information should be a PKCS #11 URI and a binary file holding
6  * public credentials (not a base64 / Armoured / PEM notation).
7  *
8  * From: Rick van Rein <rick@openfortress.nl>
9  */
10
11
12 #include <stdlib.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <string.h>
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <errno.h>
23
24 #include <arpa/inet.h>
25
26 #include <db.h>
27
28 #include <tlspool/internal.h>
29
30
31 const char usage[] =
32 "Usage: %s tlspool.conf [user@]fqdn type [p11priv pubdata...]\n"
33 " - tlspool.conf      is the configuration file for the TLS Pool\n"
34 " - user@fqdn or fqdn is a network access identifier\n"
35 " - type              X.509,OpenPGP,Kerberos,valexp,client,server,nop11,chained\n"
36 " - p11priv           is a PKCS #11 URI string for the private key\n"
37 " - pubdata           is a file name    string for the public key package\n"
38 "The pairs of p11priv and pubdata replace the old content.  An empty list of\n"
39 "pairs is nothing special; it replaces the old content with zero entries.\n";
40
41
42 struct typemap_t {
43         char *name;
44         uint32_t bits;
45 };
46
47 struct typemap_t typemap [] = {
48         { "X.509",      1 },
49         { "x509",       1 },
50         { "OpenPGP",    2 },
51         { "Kerberos",   4 },
52         { "krb5",       4 },
53         { "valexp",     5 },
54         { "cli",        256 },
55         { "srv",        512 },
56         { "client",     256 },
57         { "server",     512 },
58         { "noP11",      4096 },
59         { "chained",    8192 },
60         { NULL,         0 }
61 };
62
63
64 /* Setup and tear down management */
65 int setup_management (char *cfgfile, DB_ENV **dbenv, DB_TXN **txn, DB **dbh) {
66         char *dbenv_dir = tlspool_configvar (cfgfile, "dbenv_dir");
67         char *dblid_fnm = tlspool_configvar (cfgfile, "db_localid");
68         if (dbenv_dir == NULL) {
69                 fprintf (stderr, "Please configure database environment directory\n");
70                 return 0;
71         }
72         if (dblid_fnm == NULL) {
73                 fprintf (stderr, "Please configure localid database name\n");
74                 return 0;
75         }
76         if (db_env_create (dbenv, 0) != 0) {
77                 fprintf (stderr, "Failed to create database environment");
78                 return 0;
79         }
80         if ((*dbenv)->open (*dbenv, dbenv_dir, DB_CREATE | DB_RECOVER | DB_INIT_TXN | DB_INIT_LOG | DB_INIT_LOCK | DB_THREAD | DB_INIT_MPOOL, S_IRUSR | S_IWUSR) != 0) {
81                 fprintf (stderr, "Failed to open database environment");
82                 return 0;
83         }
84         if ((*dbenv)->txn_begin (*dbenv, NULL, txn, 0) != 0) {
85                 fprintf (stderr, "Failed to start transaction\n");
86                 exit (1);
87         }
88         if (db_create (dbh, *dbenv, 0) != 0) {
89                 fprintf (stderr, "Failed to create localid database\n");
90                 return 0;
91         }
92         if ((*dbh)->set_flags (*dbh, DB_DUP) != 0) {
93                 fprintf (stderr, "Failed to setup localid database for duplicate entries\n");
94                 return 0;
95         }
96         if ((*dbh)->open (*dbh, *txn, dblid_fnm, NULL, DB_HASH, DB_CREATE | DB_THREAD, 0) != 0) {
97                 fprintf (stderr, "Failed to open localid database\n");
98                 return 0;
99         }
100         return 1;
101 }
102
103 /* Cleanup maangement structures */
104 void cleanup_management (DB_ENV *dbenv, DB *db) {
105         db->close (db, 0);
106         dbenv->close (dbenv, 0);
107 }
108
109 int main (int argc, char *argv []) {
110         char *localid = NULL;
111         char *partstr = NULL;
112         char *saveptr = NULL;
113         char *p11uri = NULL;
114         uint8_t e_buf [5000];
115         int argi = argc;
116         int filesz = 0;
117         int p11len = 0;
118         struct stat statbuf;
119         uint32_t flags = 0;
120         DB_ENV *dbenv;
121         DB_TXN *txn;
122         DB *dbh;
123         DBC *crs;
124         DBT k_localid;
125         DBT e_value;
126         int nomore;
127         int fd;
128         char *cfgfile;
129         //
130         // Sanity check
131         if ((argc < 4) || ((argc % 2) != 0)) {
132                 fprintf (stderr, usage, argv [0]);
133                 exit (1);
134         }
135         //
136         // Initialise the modules taken from the src directory
137         ;
138         //
139         // Prepare variables from arguments
140         cfgfile = argv [1];
141         localid = argv [2];
142         partstr = strtok_r (argv [3], ",", &saveptr);
143         if (partstr == NULL) {
144                 fprintf (stderr, "Flags must not be empty\n");
145                 exit (1);
146         }
147         while (partstr != NULL) {
148                 struct typemap_t *walker = typemap;
149                 while (walker->name != NULL) {
150                         if (strcasecmp (walker->name, partstr) == 0) {
151                                 flags |= walker->bits;
152                                 break;
153                         }
154                         walker++;
155                 }
156                 if (walker->name == NULL) {
157                         fprintf (stderr, "Flag name %s not recognised\n", partstr);
158                         exit (1);
159                 }
160                 partstr = strtok_r (NULL, ",", &saveptr);
161         }
162         if ((flags & 0x000000ff) != 5) {
163                 argi = 4;
164                 while (argi < argc) {
165                         if (strncmp (argv [argi], "pkcs11:", 7) != 0) {
166                                 fprintf (stderr, "PKCS #11 URIs must start with \"pkcs11:\"\n");
167                                 exit (1);
168                         }
169                         argi += 2;
170                 }
171         }
172         //
173         // Now modify the matching entries
174         if (!setup_management (cfgfile, &dbenv, &txn, &dbh)) {
175                 exit (1);
176         }
177         if (dbh->cursor (dbh, txn, &crs, 0) != 0) {
178                 fprintf (stderr, "Failed to open cursor on localid.db\n");
179                 goto failure;
180         }
181         memset (&k_localid, 0, sizeof (k_localid));
182         k_localid.data = localid;
183         k_localid.size = strlen (localid);
184         nomore = crs->get (crs, &k_localid, &e_value, DB_SET);
185         while (nomore == 0) {
186                 uint32_t e_flags = 0;
187                 char *e_p11uri = NULL;
188                 uint8_t *e_bindata;
189                 int e_binlen;
190                 if (e_value.size < 4) {
191                         fprintf (stderr, "Found too-short entry?!?\n");
192                         crs->close (crs);
193                         goto failure;
194                 }
195                 e_flags = ntohl (* (uint32_t *) e_value.data);
196                 e_p11uri = (char *) & ((uint32_t *) e_value.data) [1];
197                 e_bindata = (uint8_t *)(e_p11uri + strnlen (e_p11uri, e_value.size - 4) + 1);
198                 e_binlen = e_value.size - 4 - strnlen (e_p11uri, e_value.size - 4) - 1;
199                 if (e_binlen <= 0) {
200                         fprintf (stderr, "Error retrieving binary data;\n");
201                 }
202                 printf ("Object flags are 0x%x\n", e_flags);
203                 if ((e_flags & 0xff) == (flags & 0xff)) {
204                         printf ("Deleting old entry 0x%x, %s, #%d\n",
205                                 e_flags, e_p11uri, e_binlen);
206                         if (crs->del (crs, 0) != 0) {
207                                 fprintf (stderr, "Failed to delete record\n");
208                                 crs->close (crs);
209                                 goto failure;
210                         } else {
211                                 printf ("Deleted this old record\n");
212                         }
213                 } else {
214                         printf ("Won't remove, type is 0x%x and not 0x%x\n",
215                                 e_flags & 255, flags & 255);
216                 }
217                 nomore = crs->get (crs, &k_localid, &e_value, DB_NEXT_DUP);
218         }
219         crs->close (crs);
220         if (nomore != DB_NOTFOUND) {
221                 fprintf (stderr, "Database error encountered while iterating\n");
222                 goto failure;
223         }
224         //
225         // Now append any new values
226         argi = 4;
227         while (argi < argc) {
228                 p11len = strlen (argv [argi]);
229                 if (stat (argv [argi+1], &statbuf) != 0) {
230                         fprintf (stderr, "Failed to stat %s: %s\n",
231                                 argv [argi+1], strerror (errno));
232                         goto failure;
233                 }
234                 filesz = statbuf.st_size;
235                 if (4 + p11len + 1 + filesz > sizeof (e_buf)) {
236                         fprintf (stderr, "Out of buffer memory trying to fill %s\n",
237                                 argv [argi]);
238                         goto failure;
239                 }
240                 * (uint32_t *) e_buf = htonl (flags);
241                 strcpy ((char *) & ((uint32_t *) e_buf) [1], argv [argi]);
242                 fd = open (argv [argi+1], O_RDONLY);
243                 if (fd == -1) {
244                         fprintf (stderr, "Failed to open %s: %s\n",
245                                 argv [argi+1], strerror (errno));
246                         goto failure;
247                 }
248                 if (read (fd, &e_buf [4 + p11len + 1], filesz) != filesz) {
249                         fprintf (stderr, "Failed to read from %s: %s\n",
250                                 argv [argi+1], strerror (errno));
251                         close (fd);
252                         goto failure;
253                 }
254                 close (fd);
255                 e_value.data = e_buf;
256                 e_value.size = 4 + p11len + 1 + filesz;
257                 if (dbh->put (dbh, txn, &k_localid, &e_value, 0) != 0) {
258                         fprintf (stderr, "Failed to write record to database\n");
259                         goto failure;
260                 }
261                 printf ("Written %s and %s\n", argv [argi], argv [argi+1]);
262                 argi += 2;
263         }
264         if (txn->commit (txn, 0) != 0) {
265                 fprintf (stderr, "Failed to commit transaction\n");
266                 exit (1);
267         } else {
268                 fprintf (stderr, "Committed transaction\n");
269         }
270         //
271         // Finish up and report success
272 success:
273         cleanup_management (dbenv, dbh);
274         return 0;
275         //
276         // Handle failure during database interactions
277 failure:
278         fprintf (stderr, "Rolling back transaction\n");
279         txn->abort (txn);
280         cleanup_management (dbenv, dbh);
281         exit (1);
282 }