xref: /freebsd/crypto/heimdal/lib/hx509/file.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
1 /*
2  * Copyright (c) 2005 - 2006 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "hx_locl.h"
35 RCSID("$ID$");
36 
37 int
38 _hx509_map_file_os(const char *fn, heim_octet_string *os, struct stat *rsb)
39 {
40     size_t length;
41     void *data;
42     int ret;
43 
44     ret = _hx509_map_file(fn, &data, &length, rsb);
45 
46     os->data = data;
47     os->length = length;
48 
49     return ret;
50 }
51 
52 void
53 _hx509_unmap_file_os(heim_octet_string *os)
54 {
55     _hx509_unmap_file(os->data, os->length);
56 }
57 
58 int
59 _hx509_map_file(const char *fn, void **data, size_t *length, struct stat *rsb)
60 {
61     struct stat sb;
62     size_t len;
63     ssize_t l;
64     int ret;
65     void *d;
66     int fd;
67 
68     *data = NULL;
69     *length = 0;
70 
71     fd = open(fn, O_RDONLY);
72     if (fd < 0)
73 	return errno;
74 
75     if (fstat(fd, &sb) < 0) {
76 	ret = errno;
77 	close(fd);
78 	return ret;
79     }
80 
81     len = sb.st_size;
82 
83     d = malloc(len);
84     if (d == NULL) {
85 	close(fd);
86 	return ENOMEM;
87     }
88 
89     l = read(fd, d, len);
90     close(fd);
91     if (l < 0 || l != len) {
92 	free(d);
93 	return EINVAL;
94     }
95 
96     if (rsb)
97 	*rsb = sb;
98     *data = d;
99     *length = len;
100     return 0;
101 }
102 
103 void
104 _hx509_unmap_file(void *data, size_t len)
105 {
106     free(data);
107 }
108 
109 int
110 _hx509_write_file(const char *fn, const void *data, size_t length)
111 {
112     ssize_t sz;
113     const unsigned char *p = data;
114     int fd;
115 
116     fd = open(fn, O_WRONLY|O_TRUNC|O_CREAT, 0644);
117     if (fd < 0)
118 	return errno;
119 
120     do {
121 	sz = write(fd, p, length);
122 	if (sz < 0) {
123 	    int saved_errno = errno;
124 	    close(fd);
125 	    return saved_errno;
126 	}
127 	if (sz == 0)
128 	    break;
129 	length -= sz;
130     } while (length > 0);
131 
132     if (close(fd) == -1)
133 	return errno;
134 
135     return 0;
136 }
137 
138 /*
139  *
140  */
141 
142 static void
143 header(FILE *f, const char *type, const char *str)
144 {
145     fprintf(f, "-----%s %s-----\n", type, str);
146 }
147 
148 int
149 hx509_pem_write(hx509_context context, const char *type,
150 		hx509_pem_header *headers, FILE *f,
151 		const void *data, size_t size)
152 {
153     const char *p = data;
154     size_t length;
155     char *line;
156 
157 #define ENCODE_LINE_LENGTH	54
158 
159     header(f, "BEGIN", type);
160 
161     while (headers) {
162 	fprintf(f, "%s: %s\n%s",
163 		headers->header, headers->value,
164 		headers->next ? "" : "\n");
165 	headers = headers->next;
166     }
167 
168     while (size > 0) {
169 	ssize_t l;
170 
171 	length = size;
172 	if (length > ENCODE_LINE_LENGTH)
173 	    length = ENCODE_LINE_LENGTH;
174 
175 	l = base64_encode(p, length, &line);
176 	if (l < 0) {
177 	    hx509_set_error_string(context, 0, ENOMEM,
178 				   "malloc - out of memory");
179 	    return ENOMEM;
180 	}
181 	size -= length;
182 	fprintf(f, "%s\n", line);
183 	p += length;
184 	free(line);
185     }
186 
187     header(f, "END", type);
188 
189     return 0;
190 }
191 
192 /*
193  *
194  */
195 
196 int
197 hx509_pem_add_header(hx509_pem_header **headers,
198 		     const char *header, const char *value)
199 {
200     hx509_pem_header *h;
201 
202     h = calloc(1, sizeof(*h));
203     if (h == NULL)
204 	return ENOMEM;
205     h->header = strdup(header);
206     if (h->header == NULL) {
207 	free(h);
208 	return ENOMEM;
209     }
210     h->value = strdup(value);
211     if (h->value == NULL) {
212 	free(h->header);
213 	free(h);
214 	return ENOMEM;
215     }
216 
217     h->next = *headers;
218     *headers = h;
219 
220     return 0;
221 }
222 
223 void
224 hx509_pem_free_header(hx509_pem_header *headers)
225 {
226     hx509_pem_header *h;
227     while (headers) {
228 	h = headers;
229 	headers = headers->next;
230 	free(h->header);
231 	free(h->value);
232 	free(h);
233     }
234 }
235 
236 /*
237  *
238  */
239 
240 const char *
241 hx509_pem_find_header(const hx509_pem_header *h, const char *header)
242 {
243     while(h) {
244 	if (strcmp(header, h->header) == 0)
245 	    return h->value;
246 	h = h->next;
247     }
248     return NULL;
249 }
250 
251 
252 /*
253  *
254  */
255 
256 int
257 hx509_pem_read(hx509_context context,
258 	       FILE *f,
259 	       hx509_pem_read_func func,
260 	       void *ctx)
261 {
262     hx509_pem_header *headers = NULL;
263     char *type = NULL;
264     void *data = NULL;
265     size_t len = 0;
266     char buf[1024];
267     int ret = HX509_PARSING_KEY_FAILED;
268 
269     enum { BEFORE, SEARCHHEADER, INHEADER, INDATA, DONE } where;
270 
271     where = BEFORE;
272 
273     while (fgets(buf, sizeof(buf), f) != NULL) {
274 	char *p;
275 	int i;
276 
277 	i = strcspn(buf, "\n");
278 	if (buf[i] == '\n') {
279 	    buf[i] = '\0';
280 	    if (i > 0)
281 		i--;
282 	}
283 	if (buf[i] == '\r') {
284 	    buf[i] = '\0';
285 	    if (i > 0)
286 		i--;
287 	}
288 
289 	switch (where) {
290 	case BEFORE:
291 	    if (strncmp("-----BEGIN ", buf, 11) == 0) {
292 		type = strdup(buf + 11);
293 		if (type == NULL)
294 		    break;
295 		p = strchr(type, '-');
296 		if (p)
297 		    *p = '\0';
298 		where = SEARCHHEADER;
299 	    }
300 	    break;
301 	case SEARCHHEADER:
302 	    p = strchr(buf, ':');
303 	    if (p == NULL) {
304 		where = INDATA;
305 		goto indata;
306 	    }
307 	    /* FALLTHOUGH */
308 	case INHEADER:
309 	    if (buf[0] == '\0') {
310 		where = INDATA;
311 		break;
312 	    }
313 	    p = strchr(buf, ':');
314 	    if (p) {
315 		*p++ = '\0';
316 		while (isspace((int)*p))
317 		    p++;
318 		ret = hx509_pem_add_header(&headers, buf, p);
319 		if (ret)
320 		    abort();
321 	    }
322 	    break;
323 	case INDATA:
324 	indata:
325 
326 	    if (strncmp("-----END ", buf, 9) == 0) {
327 		where = DONE;
328 		break;
329 	    }
330 
331 	    p = emalloc(i);
332 	    i = base64_decode(buf, p);
333 	    if (i < 0) {
334 		free(p);
335 		goto out;
336 	    }
337 
338 	    data = erealloc(data, len + i);
339 	    memcpy(((char *)data) + len, p, i);
340 	    free(p);
341 	    len += i;
342 	    break;
343 	case DONE:
344 	    abort();
345 	}
346 
347 	if (where == DONE) {
348 	    ret = (*func)(context, type, headers, data, len, ctx);
349 	out:
350 	    free(data);
351 	    data = NULL;
352 	    len = 0;
353 	    free(type);
354 	    type = NULL;
355 	    where = BEFORE;
356 	    hx509_pem_free_header(headers);
357 	    headers = NULL;
358 	    if (ret)
359 		break;
360 	}
361     }
362 
363     if (where != BEFORE) {
364 	hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
365 			       "File ends before end of PEM end tag");
366 	ret = HX509_PARSING_KEY_FAILED;
367     }
368     if (data)
369 	free(data);
370     if (type)
371 	free(type);
372     if (headers)
373 	hx509_pem_free_header(headers);
374 
375     return ret;
376 }
377