Add more passphrase policy rules.
[gnupg.git] / tools / gpgkey2ssh.c
1 /* gpgkey2ssh.c - Converter ...
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 #include <config.h>
21
22 #include <gcrypt.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <stdio.h>
27 #include <errno.h>
28
29 #include "util.h"
30
31 \f
32
33 typedef struct pkdbuf
34 {
35   unsigned char *buffer;
36   size_t buffer_n;
37 } pkdbuf_t;
38
39 \f
40
41 /* Retrieve the public key material for the RSA key, whose fingerprint
42    is FPR, from gpg output, which can be read through the stream FP.
43    The RSA modulus will be stored at the address of M and MLEN, the
44    public exponent at E and ELEN.  Returns zero on success, an error
45    code on failure.  Caller must release the allocated buffers at M
46    and E if the function returns success.  */
47 static gpg_error_t
48 retrieve_key_material (FILE *fp, const char *hexkeyid, int *algorithm_id,
49                        pkdbuf_t **pkdbuf, size_t *pkdbuf_n)
50 {
51   pkdbuf_t *pkdbuf_new;
52   pkdbuf_t *pkdbuf_tmp;
53   size_t pkdbuf_new_n;
54   gcry_error_t err = 0;
55   char *line = NULL;    /* read_line() buffer. */
56   size_t line_size = 0; /* Helper for for read_line. */
57   int found_key = 0;    /* Helper to find a matching key. */
58   int id;
59   unsigned char *buffer;
60   size_t buffer_n;
61   int i;
62
63   pkdbuf_new = NULL;
64   pkdbuf_new_n = 0;
65   id = 0;
66
67   /* Loop over all records until we have found the subkey
68      corresponsing to the fingerprint. Inm general the first record
69      should be the pub record, but we don't rely on that.  Given that
70      we only need to look at one key, it is sufficient to compare the
71      keyid so that we don't need to look at "fpr" records. */
72   for (;;)
73     {
74       char *p;
75       char *fields[6];
76       int nfields;
77       size_t max_length;
78       gcry_mpi_t mpi;
79
80       max_length = 4096;
81       i = read_line (fp, &line, &line_size, &max_length);
82       if (!i)
83         break; /* EOF. */
84       if (i < 0)
85         {
86           err = gpg_error_from_syserror ();
87           goto leave; /* Error. */
88         }
89       if (!max_length)
90         {
91           err = gpg_error (GPG_ERR_TRUNCATED);
92           goto leave;  /* Line truncated - we better stop processing.  */
93         }
94
95       /* Parse the line into fields. */
96       for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
97         {
98           fields[nfields] = p;
99           p = strchr (p, ':');
100           if (p)
101             *(p++) = 0;
102         }
103       if (!nfields)
104         continue; /* No fields at all - skip line.  */
105
106       if (!found_key)
107         {
108           if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
109                && nfields > 4 &&
110                (((strlen (hexkeyid) == 8)
111                  && (strlen (fields[4]) == 16)
112                  && (! strcmp (fields[4] + 8, hexkeyid)))
113                 || ((strlen (hexkeyid) == 16)
114                     && (! strcmp (fields[4], hexkeyid)))))
115             {
116               found_key = 1;
117               /* Save algorithm ID.  */
118               id = atoi (fields[3]);
119             }
120           continue;
121         }
122       
123       if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
124         break; /* Next key - stop.  */
125
126       if ( strcmp (fields[0], "pkd") )
127         continue; /* Not a key data record.  */
128
129       /* FIXME, necessary?  */
130
131       i = atoi (fields[1]);
132       if ((nfields < 4) || (i < 0))
133         {
134           err = gpg_error (GPG_ERR_GENERAL);
135           goto leave;
136         }
137
138       err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
139       if (err)
140         mpi = NULL;
141
142       err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &buffer, &buffer_n, mpi);
143       gcry_mpi_release (mpi);
144       if (err)
145         goto leave;
146
147       pkdbuf_tmp = xrealloc (pkdbuf_new, sizeof (*pkdbuf_new) * (pkdbuf_new_n + 1));
148       if (pkdbuf_new != pkdbuf_tmp)
149         pkdbuf_new = pkdbuf_tmp;
150       pkdbuf_new[pkdbuf_new_n].buffer = buffer;
151       pkdbuf_new[pkdbuf_new_n].buffer_n = buffer_n;
152       pkdbuf_new_n++;
153     }
154
155   *algorithm_id = id;
156   *pkdbuf = pkdbuf_new;
157   *pkdbuf_n = pkdbuf_new_n;
158
159  leave:
160
161   if (err)
162     if (pkdbuf_new)
163       {
164         for (i = 0; i < pkdbuf_new_n; i++)
165           xfree (pkdbuf_new[i].buffer);
166         xfree (pkdbuf_new);
167       }
168   xfree (line);
169
170   return err;
171 }
172
173 \f
174
175 int
176 key_to_blob (unsigned char **blob, size_t *blob_n, const char *identifier, ...)
177 {
178   unsigned char *blob_new;
179   size_t blob_new_n;
180   unsigned char uint32_buffer[4];
181   u32 identifier_n;
182   FILE *stream;
183   va_list ap;
184   int ret;
185   pkdbuf_t *pkd;
186
187   stream = gnupg_tmpfile ();
188   assert (stream);
189
190   identifier_n = strlen (identifier);
191   uint32_buffer[0] = identifier_n >> 24;
192   uint32_buffer[1] = identifier_n >> 16;
193   uint32_buffer[2] = identifier_n >>  8;
194   uint32_buffer[3] = identifier_n >>  0;
195   ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
196   assert (ret == 1);
197   ret = fwrite (identifier, identifier_n, 1, stream);
198   assert (ret == 1);
199
200   va_start (ap, identifier);
201   while (1)
202     {
203       pkd = va_arg (ap, pkdbuf_t *);
204       if (! pkd)
205         break;
206
207       uint32_buffer[0] = pkd->buffer_n >> 24;
208       uint32_buffer[1] = pkd->buffer_n >> 16;
209       uint32_buffer[2] = pkd->buffer_n >>  8;
210       uint32_buffer[3] = pkd->buffer_n >>  0;
211       ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
212       assert (ret == 1);
213       ret = fwrite (pkd->buffer, pkd->buffer_n, 1, stream);
214       assert (ret == 1);
215     }
216
217   blob_new_n = ftell (stream);
218   rewind (stream);
219
220   blob_new = xmalloc (blob_new_n);
221   ret = fread (blob_new, blob_new_n, 1, stream);
222   assert (ret == 1);
223
224   *blob = blob_new;
225   *blob_n = blob_new_n;
226
227   fclose (stream);
228
229   return 0;
230 }
231
232 int
233 main (int argc, char **argv)
234 {
235   const char *keyid;
236   int algorithm_id;
237   pkdbuf_t *pkdbuf;
238   size_t pkdbuf_n;
239   char *command;
240   FILE *fp;
241   int ret;
242   gcry_error_t err;
243   unsigned char *blob;
244   size_t blob_n;
245   struct b64state b64_state;
246   const char *identifier;
247
248   pkdbuf = NULL;
249   pkdbuf_n = 0;
250
251   algorithm_id = 0;  /* (avoid cc warning) */
252   identifier = NULL; /* (avoid cc warning) */
253
254   assert (argc == 2);
255
256   keyid = argv[1];
257
258   ret = asprintf (&command,
259                   "gpg --list-keys --with-colons --with-key-data '%s'",
260                   keyid);
261   assert (ret > 0);
262
263   fp = popen (command, "r");
264   assert (fp);
265
266   err = retrieve_key_material (fp, keyid, &algorithm_id, &pkdbuf, &pkdbuf_n);
267   assert (! err);
268   assert ((algorithm_id == 1) || (algorithm_id == 17));
269
270   if (algorithm_id == 1)
271     {
272       identifier = "ssh-rsa";
273       ret = key_to_blob (&blob, &blob_n, identifier,
274                          &pkdbuf[0], &pkdbuf[1], NULL);
275     }
276   else if (algorithm_id == 17)
277     {
278       identifier = "ssh-dsa";
279       ret = key_to_blob (&blob, &blob_n, identifier,
280                          &pkdbuf[0], &pkdbuf[1], &pkdbuf[2], &pkdbuf[3], NULL);
281     }
282   assert (! ret);
283
284   printf ("%s ", identifier);
285
286   err = b64enc_start (&b64_state, stdout, "");
287   assert (! err);
288   err = b64enc_write (&b64_state, blob, blob_n);
289   assert (! err);
290   err = b64enc_finish (&b64_state);
291   assert (! err);
292
293   printf (" COMMENT\n");
294
295   return 0;
296 }