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"
35RCSID("$ID$");
36
37int
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
52void
53_hx509_unmap_file_os(heim_octet_string *os)
54{
55    _hx509_unmap_file(os->data, os->length);
56}
57
58int
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
103void
104_hx509_unmap_file(void *data, size_t len)
105{
106    free(data);
107}
108
109int
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
142static void
143header(FILE *f, const char *type, const char *str)
144{
145    fprintf(f, "-----%s %s-----\n", type, str);
146}
147
148int
149hx509_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
196int
197hx509_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
223void
224hx509_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
240const char *
241hx509_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
256int
257hx509_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