1178825Sdfr/*
2178825Sdfr * Copyright (c) 2005 - 2006 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "hx_locl.h"
35178825SdfrRCSID("$ID$");
36178825Sdfr
37178825Sdfrint
38178825Sdfr_hx509_map_file_os(const char *fn, heim_octet_string *os, struct stat *rsb)
39178825Sdfr{
40178825Sdfr    size_t length;
41178825Sdfr    void *data;
42178825Sdfr    int ret;
43178825Sdfr
44178825Sdfr    ret = _hx509_map_file(fn, &data, &length, rsb);
45178825Sdfr
46178825Sdfr    os->data = data;
47178825Sdfr    os->length = length;
48178825Sdfr
49178825Sdfr    return ret;
50178825Sdfr}
51178825Sdfr
52178825Sdfrvoid
53178825Sdfr_hx509_unmap_file_os(heim_octet_string *os)
54178825Sdfr{
55178825Sdfr    _hx509_unmap_file(os->data, os->length);
56178825Sdfr}
57178825Sdfr
58178825Sdfrint
59178825Sdfr_hx509_map_file(const char *fn, void **data, size_t *length, struct stat *rsb)
60178825Sdfr{
61178825Sdfr    struct stat sb;
62178825Sdfr    size_t len;
63178825Sdfr    ssize_t l;
64178825Sdfr    int ret;
65178825Sdfr    void *d;
66178825Sdfr    int fd;
67178825Sdfr
68178825Sdfr    *data = NULL;
69178825Sdfr    *length = 0;
70178825Sdfr
71178825Sdfr    fd = open(fn, O_RDONLY);
72178825Sdfr    if (fd < 0)
73178825Sdfr	return errno;
74178825Sdfr
75178825Sdfr    if (fstat(fd, &sb) < 0) {
76178825Sdfr	ret = errno;
77178825Sdfr	close(fd);
78178825Sdfr	return ret;
79178825Sdfr    }
80178825Sdfr
81178825Sdfr    len = sb.st_size;
82178825Sdfr
83178825Sdfr    d = malloc(len);
84178825Sdfr    if (d == NULL) {
85178825Sdfr	close(fd);
86178825Sdfr	return ENOMEM;
87178825Sdfr    }
88178825Sdfr
89178825Sdfr    l = read(fd, d, len);
90178825Sdfr    close(fd);
91178825Sdfr    if (l < 0 || l != len) {
92178825Sdfr	free(d);
93178825Sdfr	return EINVAL;
94178825Sdfr    }
95178825Sdfr
96178825Sdfr    if (rsb)
97178825Sdfr	*rsb = sb;
98178825Sdfr    *data = d;
99178825Sdfr    *length = len;
100178825Sdfr    return 0;
101178825Sdfr}
102178825Sdfr
103178825Sdfrvoid
104178825Sdfr_hx509_unmap_file(void *data, size_t len)
105178825Sdfr{
106178825Sdfr    free(data);
107178825Sdfr}
108178825Sdfr
109178825Sdfrint
110178825Sdfr_hx509_write_file(const char *fn, const void *data, size_t length)
111178825Sdfr{
112178825Sdfr    ssize_t sz;
113178825Sdfr    const unsigned char *p = data;
114178825Sdfr    int fd;
115178825Sdfr
116178825Sdfr    fd = open(fn, O_WRONLY|O_TRUNC|O_CREAT, 0644);
117178825Sdfr    if (fd < 0)
118178825Sdfr	return errno;
119178825Sdfr
120178825Sdfr    do {
121178825Sdfr	sz = write(fd, p, length);
122178825Sdfr	if (sz < 0) {
123178825Sdfr	    int saved_errno = errno;
124178825Sdfr	    close(fd);
125178825Sdfr	    return saved_errno;
126178825Sdfr	}
127178825Sdfr	if (sz == 0)
128178825Sdfr	    break;
129178825Sdfr	length -= sz;
130178825Sdfr    } while (length > 0);
131178825Sdfr
132178825Sdfr    if (close(fd) == -1)
133178825Sdfr	return errno;
134178825Sdfr
135178825Sdfr    return 0;
136178825Sdfr}
137178825Sdfr
138178825Sdfr/*
139178825Sdfr *
140178825Sdfr */
141178825Sdfr
142178825Sdfrstatic void
143178825Sdfrheader(FILE *f, const char *type, const char *str)
144178825Sdfr{
145178825Sdfr    fprintf(f, "-----%s %s-----\n", type, str);
146178825Sdfr}
147178825Sdfr
148178825Sdfrint
149178825Sdfrhx509_pem_write(hx509_context context, const char *type,
150178825Sdfr		hx509_pem_header *headers, FILE *f,
151178825Sdfr		const void *data, size_t size)
152178825Sdfr{
153178825Sdfr    const char *p = data;
154178825Sdfr    size_t length;
155178825Sdfr    char *line;
156178825Sdfr
157178825Sdfr#define ENCODE_LINE_LENGTH	54
158178825Sdfr
159178825Sdfr    header(f, "BEGIN", type);
160178825Sdfr
161178825Sdfr    while (headers) {
162178825Sdfr	fprintf(f, "%s: %s\n%s",
163178825Sdfr		headers->header, headers->value,
164178825Sdfr		headers->next ? "" : "\n");
165178825Sdfr	headers = headers->next;
166178825Sdfr    }
167178825Sdfr
168178825Sdfr    while (size > 0) {
169178825Sdfr	ssize_t l;
170178825Sdfr
171178825Sdfr	length = size;
172178825Sdfr	if (length > ENCODE_LINE_LENGTH)
173178825Sdfr	    length = ENCODE_LINE_LENGTH;
174178825Sdfr
175178825Sdfr	l = base64_encode(p, length, &line);
176178825Sdfr	if (l < 0) {
177178825Sdfr	    hx509_set_error_string(context, 0, ENOMEM,
178178825Sdfr				   "malloc - out of memory");
179178825Sdfr	    return ENOMEM;
180178825Sdfr	}
181178825Sdfr	size -= length;
182178825Sdfr	fprintf(f, "%s\n", line);
183178825Sdfr	p += length;
184178825Sdfr	free(line);
185178825Sdfr    }
186178825Sdfr
187178825Sdfr    header(f, "END", type);
188178825Sdfr
189178825Sdfr    return 0;
190178825Sdfr}
191178825Sdfr
192178825Sdfr/*
193178825Sdfr *
194178825Sdfr */
195178825Sdfr
196178825Sdfrint
197178825Sdfrhx509_pem_add_header(hx509_pem_header **headers,
198178825Sdfr		     const char *header, const char *value)
199178825Sdfr{
200178825Sdfr    hx509_pem_header *h;
201178825Sdfr
202178825Sdfr    h = calloc(1, sizeof(*h));
203178825Sdfr    if (h == NULL)
204178825Sdfr	return ENOMEM;
205178825Sdfr    h->header = strdup(header);
206178825Sdfr    if (h->header == NULL) {
207178825Sdfr	free(h);
208178825Sdfr	return ENOMEM;
209178825Sdfr    }
210178825Sdfr    h->value = strdup(value);
211178825Sdfr    if (h->value == NULL) {
212178825Sdfr	free(h->header);
213178825Sdfr	free(h);
214178825Sdfr	return ENOMEM;
215178825Sdfr    }
216178825Sdfr
217178825Sdfr    h->next = *headers;
218178825Sdfr    *headers = h;
219178825Sdfr
220178825Sdfr    return 0;
221178825Sdfr}
222178825Sdfr
223178825Sdfrvoid
224178825Sdfrhx509_pem_free_header(hx509_pem_header *headers)
225178825Sdfr{
226178825Sdfr    hx509_pem_header *h;
227178825Sdfr    while (headers) {
228178825Sdfr	h = headers;
229178825Sdfr	headers = headers->next;
230178825Sdfr	free(h->header);
231178825Sdfr	free(h->value);
232178825Sdfr	free(h);
233178825Sdfr    }
234178825Sdfr}
235178825Sdfr
236178825Sdfr/*
237178825Sdfr *
238178825Sdfr */
239178825Sdfr
240178825Sdfrconst char *
241178825Sdfrhx509_pem_find_header(const hx509_pem_header *h, const char *header)
242178825Sdfr{
243178825Sdfr    while(h) {
244178825Sdfr	if (strcmp(header, h->header) == 0)
245178825Sdfr	    return h->value;
246178825Sdfr	h = h->next;
247178825Sdfr    }
248178825Sdfr    return NULL;
249178825Sdfr}
250178825Sdfr
251178825Sdfr
252178825Sdfr/*
253178825Sdfr *
254178825Sdfr */
255178825Sdfr
256178825Sdfrint
257178825Sdfrhx509_pem_read(hx509_context context,
258178825Sdfr	       FILE *f,
259178825Sdfr	       hx509_pem_read_func func,
260178825Sdfr	       void *ctx)
261178825Sdfr{
262178825Sdfr    hx509_pem_header *headers = NULL;
263178825Sdfr    char *type = NULL;
264178825Sdfr    void *data = NULL;
265178825Sdfr    size_t len = 0;
266178825Sdfr    char buf[1024];
267178825Sdfr    int ret = HX509_PARSING_KEY_FAILED;
268178825Sdfr
269178825Sdfr    enum { BEFORE, SEARCHHEADER, INHEADER, INDATA, DONE } where;
270178825Sdfr
271178825Sdfr    where = BEFORE;
272178825Sdfr
273178825Sdfr    while (fgets(buf, sizeof(buf), f) != NULL) {
274178825Sdfr	char *p;
275178825Sdfr	int i;
276178825Sdfr
277178825Sdfr	i = strcspn(buf, "\n");
278178825Sdfr	if (buf[i] == '\n') {
279178825Sdfr	    buf[i] = '\0';
280178825Sdfr	    if (i > 0)
281178825Sdfr		i--;
282178825Sdfr	}
283178825Sdfr	if (buf[i] == '\r') {
284178825Sdfr	    buf[i] = '\0';
285178825Sdfr	    if (i > 0)
286178825Sdfr		i--;
287178825Sdfr	}
288178825Sdfr
289178825Sdfr	switch (where) {
290178825Sdfr	case BEFORE:
291178825Sdfr	    if (strncmp("-----BEGIN ", buf, 11) == 0) {
292178825Sdfr		type = strdup(buf + 11);
293178825Sdfr		if (type == NULL)
294178825Sdfr		    break;
295178825Sdfr		p = strchr(type, '-');
296178825Sdfr		if (p)
297178825Sdfr		    *p = '\0';
298178825Sdfr		where = SEARCHHEADER;
299178825Sdfr	    }
300178825Sdfr	    break;
301178825Sdfr	case SEARCHHEADER:
302178825Sdfr	    p = strchr(buf, ':');
303178825Sdfr	    if (p == NULL) {
304178825Sdfr		where = INDATA;
305178825Sdfr		goto indata;
306178825Sdfr	    }
307178825Sdfr	    /* FALLTHOUGH */
308178825Sdfr	case INHEADER:
309178825Sdfr	    if (buf[0] == '\0') {
310178825Sdfr		where = INDATA;
311178825Sdfr		break;
312178825Sdfr	    }
313178825Sdfr	    p = strchr(buf, ':');
314178825Sdfr	    if (p) {
315178825Sdfr		*p++ = '\0';
316178825Sdfr		while (isspace((int)*p))
317178825Sdfr		    p++;
318178825Sdfr		ret = hx509_pem_add_header(&headers, buf, p);
319178825Sdfr		if (ret)
320178825Sdfr		    abort();
321178825Sdfr	    }
322178825Sdfr	    break;
323178825Sdfr	case INDATA:
324178825Sdfr	indata:
325178825Sdfr
326178825Sdfr	    if (strncmp("-----END ", buf, 9) == 0) {
327178825Sdfr		where = DONE;
328178825Sdfr		break;
329178825Sdfr	    }
330178825Sdfr
331178825Sdfr	    p = emalloc(i);
332178825Sdfr	    i = base64_decode(buf, p);
333178825Sdfr	    if (i < 0) {
334178825Sdfr		free(p);
335178825Sdfr		goto out;
336178825Sdfr	    }
337178825Sdfr
338178825Sdfr	    data = erealloc(data, len + i);
339178825Sdfr	    memcpy(((char *)data) + len, p, i);
340178825Sdfr	    free(p);
341178825Sdfr	    len += i;
342178825Sdfr	    break;
343178825Sdfr	case DONE:
344178825Sdfr	    abort();
345178825Sdfr	}
346178825Sdfr
347178825Sdfr	if (where == DONE) {
348178825Sdfr	    ret = (*func)(context, type, headers, data, len, ctx);
349178825Sdfr	out:
350178825Sdfr	    free(data);
351178825Sdfr	    data = NULL;
352178825Sdfr	    len = 0;
353178825Sdfr	    free(type);
354178825Sdfr	    type = NULL;
355178825Sdfr	    where = BEFORE;
356178825Sdfr	    hx509_pem_free_header(headers);
357178825Sdfr	    headers = NULL;
358178825Sdfr	    if (ret)
359178825Sdfr		break;
360178825Sdfr	}
361178825Sdfr    }
362178825Sdfr
363178825Sdfr    if (where != BEFORE) {
364178825Sdfr	hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
365178825Sdfr			       "File ends before end of PEM end tag");
366178825Sdfr	ret = HX509_PARSING_KEY_FAILED;
367178825Sdfr    }
368178825Sdfr    if (data)
369178825Sdfr	free(data);
370178825Sdfr    if (type)
371178825Sdfr	free(type);
372178825Sdfr    if (headers)
373178825Sdfr	hx509_pem_free_header(headers);
374178825Sdfr
375178825Sdfr    return ret;
376178825Sdfr}
377