Line data Source code
1 : /* gpgkey2ssh.c - Converter (Debug helper)
2 : * Copyright (C) 2005 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG 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 : * GnuPG is distributed in the hope that it will be useful, but WITHOUT
12 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 : * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 : * 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 <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /*
21 : FIXME: This tool needs some cleanup:
22 :
23 : - Do not use assert() for error output.
24 : - Add proper option parsing and standard options.
25 : - retrieve_key_material needs to take the ordinal at field 1 in account.
26 : 0 Write a man page.
27 : */
28 :
29 : #include <config.h>
30 :
31 : #include <gcrypt.h>
32 : #include <unistd.h>
33 : #include <stdlib.h>
34 : #include <assert.h>
35 : #include <stdio.h>
36 : #include <errno.h>
37 :
38 : #include "util.h"
39 : #include "sysutils.h"
40 :
41 :
42 :
43 : typedef struct pkdbuf
44 : {
45 : unsigned char *buffer;
46 : size_t buffer_n;
47 : } pkdbuf_t;
48 :
49 :
50 :
51 : /* Retrieve the public key material for the RSA key, whose fingerprint
52 : is FPR, from gpg output, which can be read through the stream FP.
53 : The RSA modulus will be stored at the address of M and MLEN, the
54 : public exponent at E and ELEN. Returns zero on success, an error
55 : code on failure. Caller must release the allocated buffers at M
56 : and E if the function returns success. */
57 : static gpg_error_t
58 0 : retrieve_key_material (FILE *fp, const char *hexkeyid, int *algorithm_id,
59 : pkdbuf_t **pkdbuf, size_t *pkdbuf_n)
60 : {
61 : pkdbuf_t *pkdbuf_new;
62 : pkdbuf_t *pkdbuf_tmp;
63 : size_t pkdbuf_new_n;
64 0 : gcry_error_t err = 0;
65 0 : char *line = NULL; /* read_line() buffer. */
66 0 : size_t line_size = 0; /* Helper for for read_line. */
67 0 : int found_key = 0; /* Helper to find a matching key. */
68 : int id;
69 : unsigned char *buffer;
70 : size_t buffer_n;
71 : int i;
72 :
73 0 : pkdbuf_new = NULL;
74 0 : pkdbuf_new_n = 0;
75 0 : id = 0;
76 :
77 : /* Loop over all records until we have found the subkey
78 : corresponsing to the fingerprint. Inm general the first record
79 : should be the pub record, but we don't rely on that. Given that
80 : we only need to look at one key, it is sufficient to compare the
81 : keyid so that we don't need to look at "fpr" records. */
82 : for (;;)
83 : {
84 : char *p;
85 : char *fields[6];
86 : int nfields;
87 : size_t max_length;
88 : gcry_mpi_t mpi;
89 :
90 0 : max_length = 4096;
91 0 : i = read_line (fp, &line, &line_size, &max_length);
92 0 : if (!i)
93 0 : break; /* EOF. */
94 0 : if (i < 0)
95 : {
96 0 : err = gpg_error_from_syserror ();
97 0 : goto leave; /* Error. */
98 : }
99 0 : if (!max_length)
100 : {
101 0 : err = gpg_error (GPG_ERR_TRUNCATED);
102 0 : goto leave; /* Line truncated - we better stop processing. */
103 : }
104 :
105 : /* Parse the line into fields. */
106 0 : for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
107 : {
108 0 : fields[nfields] = p;
109 0 : p = strchr (p, ':');
110 0 : if (p)
111 0 : *(p++) = 0;
112 : }
113 0 : if (!nfields)
114 0 : continue; /* No fields at all - skip line. */
115 :
116 0 : if (!found_key)
117 : {
118 0 : if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
119 0 : && nfields > 4 &&
120 0 : (((strlen (hexkeyid) == 8)
121 0 : && (strlen (fields[4]) == 16)
122 0 : && (! strcmp (fields[4] + 8, hexkeyid)))
123 0 : || ((strlen (hexkeyid) == 16)
124 0 : && (! strcmp (fields[4], hexkeyid)))))
125 : {
126 0 : found_key = 1;
127 : /* Save algorithm ID. */
128 0 : id = atoi (fields[3]);
129 : }
130 0 : continue;
131 : }
132 :
133 0 : if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
134 : break; /* Next key - stop. */
135 :
136 0 : if ( strcmp (fields[0], "pkd") )
137 0 : continue; /* Not a key data record. */
138 :
139 : /* FIXME, necessary? */
140 :
141 0 : i = atoi (fields[1]);
142 0 : if ((nfields < 4) || (i < 0))
143 : {
144 0 : err = gpg_error (GPG_ERR_GENERAL);
145 0 : goto leave;
146 : }
147 :
148 0 : err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
149 0 : if (err)
150 0 : mpi = NULL;
151 :
152 0 : err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &buffer, &buffer_n, mpi);
153 0 : gcry_mpi_release (mpi);
154 0 : if (err)
155 0 : goto leave;
156 :
157 0 : pkdbuf_tmp = xrealloc (pkdbuf_new, sizeof (*pkdbuf_new) * (pkdbuf_new_n + 1));
158 0 : if (pkdbuf_new != pkdbuf_tmp)
159 0 : pkdbuf_new = pkdbuf_tmp;
160 0 : pkdbuf_new[pkdbuf_new_n].buffer = buffer;
161 0 : pkdbuf_new[pkdbuf_new_n].buffer_n = buffer_n;
162 0 : pkdbuf_new_n++;
163 0 : }
164 :
165 0 : *algorithm_id = id;
166 0 : *pkdbuf = pkdbuf_new;
167 0 : *pkdbuf_n = pkdbuf_new_n;
168 :
169 : leave:
170 :
171 0 : if (err)
172 0 : if (pkdbuf_new)
173 : {
174 0 : for (i = 0; i < pkdbuf_new_n; i++)
175 0 : xfree (pkdbuf_new[i].buffer);
176 0 : xfree (pkdbuf_new);
177 : }
178 0 : xfree (line);
179 :
180 0 : return err;
181 : }
182 :
183 :
184 :
185 : int
186 0 : key_to_blob (unsigned char **blob, size_t *blob_n, const char *identifier, ...)
187 : {
188 : unsigned char *blob_new;
189 : size_t blob_new_n;
190 : unsigned char uint32_buffer[4];
191 : u32 identifier_n;
192 : FILE *stream;
193 : va_list ap;
194 : int ret;
195 : pkdbuf_t *pkd;
196 :
197 0 : stream = gnupg_tmpfile ();
198 0 : assert (stream);
199 :
200 0 : identifier_n = strlen (identifier);
201 0 : uint32_buffer[0] = identifier_n >> 24;
202 0 : uint32_buffer[1] = identifier_n >> 16;
203 0 : uint32_buffer[2] = identifier_n >> 8;
204 0 : uint32_buffer[3] = identifier_n >> 0;
205 0 : ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
206 0 : assert (ret == 1);
207 0 : ret = fwrite (identifier, identifier_n, 1, stream);
208 0 : assert (ret == 1);
209 :
210 0 : va_start (ap, identifier);
211 : while (1)
212 : {
213 0 : pkd = va_arg (ap, pkdbuf_t *);
214 0 : if (! pkd)
215 0 : break;
216 :
217 0 : uint32_buffer[0] = pkd->buffer_n >> 24;
218 0 : uint32_buffer[1] = pkd->buffer_n >> 16;
219 0 : uint32_buffer[2] = pkd->buffer_n >> 8;
220 0 : uint32_buffer[3] = pkd->buffer_n >> 0;
221 0 : ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
222 0 : assert (ret == 1);
223 0 : ret = fwrite (pkd->buffer, pkd->buffer_n, 1, stream);
224 0 : assert (ret == 1);
225 0 : }
226 :
227 0 : va_end (ap);
228 :
229 0 : blob_new_n = ftell (stream);
230 0 : rewind (stream);
231 :
232 0 : blob_new = xmalloc (blob_new_n);
233 0 : ret = fread (blob_new, blob_new_n, 1, stream);
234 0 : assert (ret == 1);
235 :
236 0 : *blob = blob_new;
237 0 : *blob_n = blob_new_n;
238 :
239 0 : fclose (stream);
240 :
241 0 : return 0;
242 : }
243 :
244 : int
245 0 : main (int argc, char **argv)
246 : {
247 : const char *keyid;
248 : int algorithm_id;
249 : pkdbuf_t *pkdbuf;
250 : size_t pkdbuf_n;
251 0 : char *command = NULL;
252 : FILE *fp;
253 : int ret;
254 : gcry_error_t err;
255 : unsigned char *blob;
256 : size_t blob_n;
257 : struct b64state b64_state;
258 : const char *identifier;
259 :
260 0 : pkdbuf = NULL;
261 0 : pkdbuf_n = 0;
262 :
263 0 : algorithm_id = 0; /* (avoid cc warning) */
264 0 : identifier = NULL; /* (avoid cc warning) */
265 :
266 0 : if (argc != 2)
267 : {
268 0 : fprintf (stderr, "Usage: %s KEYID\n", argv[0]);
269 0 : exit (1);
270 : }
271 0 : if (strcmp (argv[1], "--help") == 0)
272 : {
273 0 : fprintf (stderr, "Usage: %s KEYID\n", argv[0]);
274 0 : fprintf (stderr, "\n");
275 0 : fprintf (stderr,
276 : "Convert a gpg key to a format appropriate for inclusion in an\n"
277 : "ssh authorized_keys file.\n");
278 0 : exit (0);
279 : }
280 :
281 0 : keyid = argv[1];
282 :
283 0 : asprintf (&command,
284 : "gpg2 --list-keys --with-colons --with-key-data '%s'",
285 : keyid);
286 0 : if (! command)
287 : {
288 0 : fprintf (stderr, "Out of memory.\n");
289 0 : exit (1);
290 : }
291 :
292 0 : fp = popen (command, "r");
293 0 : if (! fp)
294 : {
295 0 : fprintf (stderr, "Failed to running: '%s'\n", command);
296 0 : exit (1);
297 : }
298 :
299 0 : err = retrieve_key_material (fp, keyid, &algorithm_id, &pkdbuf, &pkdbuf_n);
300 0 : if (err)
301 : {
302 0 : fprintf (stderr, "Error looking up key: %s\n", gpg_strerror (err));
303 0 : exit (1);
304 : }
305 0 : if (! ((algorithm_id == 1) || (algorithm_id == 17)))
306 : {
307 0 : fprintf (stderr, "Unsupported algorithm: %d\n", algorithm_id);
308 0 : exit (1);
309 : }
310 :
311 0 : if (algorithm_id == 1)
312 : {
313 0 : identifier = "ssh-rsa";
314 0 : ret = key_to_blob (&blob, &blob_n, identifier,
315 : &pkdbuf[1], &pkdbuf[0], NULL);
316 : }
317 0 : else if (algorithm_id == 17)
318 : {
319 0 : identifier = "ssh-dss";
320 0 : ret = key_to_blob (&blob, &blob_n, identifier,
321 : &pkdbuf[0], &pkdbuf[1], &pkdbuf[2], &pkdbuf[3], NULL);
322 : }
323 0 : assert (! ret);
324 :
325 0 : printf ("%s ", identifier);
326 :
327 0 : err = b64enc_start (&b64_state, stdout, "");
328 0 : assert (! err);
329 0 : err = b64enc_write (&b64_state, blob, blob_n);
330 0 : assert (! err);
331 0 : err = b64enc_finish (&b64_state);
332 0 : assert (! err);
333 :
334 0 : printf (" COMMENT\n");
335 :
336 0 : return 0;
337 : }
|