1 /* Copyright (C) 2007 The Written Word, Inc.
2  * Copyright (C) 2008, Simon Josefsson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms,
6  * with or without modification, are permitted provided
7  * that the following conditions are met:
8  *
9  *   Redistributions of source code must retain the above
10  *   copyright notice, this list of conditions and the
11  *   following disclaimer.
12  *
13  *   Redistributions in binary form must reproduce the above
14  *   copyright notice, this list of conditions and the following
15  *   disclaimer in the documentation and/or other materials
16  *   provided with the distribution.
17  *
18  *   Neither the name of the copyright holder nor the names
19  *   of any other contributors may be used to endorse or
20  *   promote products derived from this software without
21  *   specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
35  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  */
38 
39 #include "libssh2_priv.h"
40 
41 static int
readline(char * line,int line_size,FILE * fp)42 readline(char *line, int line_size, FILE * fp)
43 {
44     size_t len;
45 
46     if (!line) {
47         return -1;
48     }
49     if (!fgets(line, line_size, fp)) {
50         return -1;
51     }
52 
53     if (*line) {
54         len = strlen(line);
55         if (len > 0 && line[len - 1] == '\n') {
56             line[len - 1] = '\0';
57         }
58     }
59 
60     if (*line) {
61         len = strlen(line);
62         if (len > 0 && line[len - 1] == '\r') {
63             line[len - 1] = '\0';
64         }
65     }
66 
67     return 0;
68 }
69 
70 static int
readline_memory(char * line,size_t line_size,const char * filedata,size_t filedata_len,size_t * filedata_offset)71 readline_memory(char *line, size_t line_size,
72                 const char *filedata, size_t filedata_len,
73                 size_t *filedata_offset)
74 {
75     size_t off, len;
76 
77     off = *filedata_offset;
78 
79     for (len = 0; off + len < filedata_len && len < line_size; len++) {
80         if (filedata[off + len] == '\n' ||
81             filedata[off + len] == '\r') {
82                 break;
83         }
84     }
85 
86     if (len) {
87         memcpy(line, filedata + off, len);
88         *filedata_offset += len;
89     }
90 
91     line[len] = '\0';
92     *filedata_offset += 1;
93 
94     return 0;
95 }
96 
97 #define LINE_SIZE 128
98 
99 int
_libssh2_pem_parse(LIBSSH2_SESSION * session,const char * headerbegin,const char * headerend,FILE * fp,unsigned char ** data,unsigned int * datalen)100 _libssh2_pem_parse(LIBSSH2_SESSION * session,
101                    const char *headerbegin,
102                    const char *headerend,
103                    FILE * fp, unsigned char **data, unsigned int *datalen)
104 {
105     char line[LINE_SIZE];
106     char *b64data = NULL;
107     unsigned int b64datalen = 0;
108     int ret;
109 
110     do {
111         *line = '\0';
112 
113         if (readline(line, LINE_SIZE, fp)) {
114             return -1;
115         }
116     }
117     while (strcmp(line, headerbegin) != 0);
118 
119     *line = '\0';
120 
121     do {
122         if (*line) {
123             char *tmp;
124             size_t linelen;
125 
126             linelen = strlen(line);
127             tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
128             if (!tmp) {
129                 ret = -1;
130                 goto out;
131             }
132             memcpy(tmp + b64datalen, line, linelen);
133             b64data = tmp;
134             b64datalen += linelen;
135         }
136 
137         *line = '\0';
138 
139         if (readline(line, LINE_SIZE, fp)) {
140             ret = -1;
141             goto out;
142         }
143     } while (strcmp(line, headerend) != 0);
144 
145     if (!b64data) {
146         return -1;
147     }
148 
149     if (libssh2_base64_decode(session, (char**) data, datalen,
150                               b64data, b64datalen)) {
151         ret = -1;
152         goto out;
153     }
154 
155     ret = 0;
156   out:
157     if (b64data) {
158         LIBSSH2_FREE(session, b64data);
159     }
160     return ret;
161 }
162 
163 int
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,const char * headerbegin,const char * headerend,const char * filedata,size_t filedata_len,unsigned char ** data,unsigned int * datalen)164 _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
165                           const char *headerbegin,
166                           const char *headerend,
167                           const char *filedata, size_t filedata_len,
168                           unsigned char **data, unsigned int *datalen)
169 {
170     char line[LINE_SIZE];
171     char *b64data = NULL;
172     unsigned int b64datalen = 0;
173     size_t off = 0;
174     int ret;
175 
176     do {
177         *line = '\0';
178 
179         if (readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
180             return -1;
181         }
182     }
183     while (strcmp(line, headerbegin) != 0);
184 
185     *line = '\0';
186 
187     do {
188         if (*line) {
189             char *tmp;
190             size_t linelen;
191 
192             linelen = strlen(line);
193             tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
194             if (!tmp) {
195                 ret = -1;
196                 goto out;
197             }
198             memcpy(tmp + b64datalen, line, linelen);
199             b64data = tmp;
200             b64datalen += linelen;
201         }
202 
203         *line = '\0';
204 
205         if (readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
206             ret = -1;
207             goto out;
208         }
209     } while (strcmp(line, headerend) != 0);
210 
211     if (!b64data) {
212         return -1;
213     }
214 
215     if (libssh2_base64_decode(session, (char**) data, datalen,
216                               b64data, b64datalen)) {
217         ret = -1;
218         goto out;
219     }
220 
221     ret = 0;
222   out:
223     if (b64data) {
224         LIBSSH2_FREE(session, b64data);
225     }
226     return ret;
227 }
228 
229 static int
read_asn1_length(const unsigned char * data,unsigned int datalen,unsigned int * len)230 read_asn1_length(const unsigned char *data,
231                  unsigned int datalen, unsigned int *len)
232 {
233     unsigned int lenlen;
234     int nextpos;
235 
236     if (datalen < 1) {
237         return -1;
238     }
239     *len = data[0];
240 
241     if (*len >= 0x80) {
242         lenlen = *len & 0x7F;
243         *len = data[1];
244         if (1 + lenlen > datalen) {
245             return -1;
246         }
247         if (lenlen > 1) {
248             *len <<= 8;
249             *len |= data[2];
250         }
251     } else {
252         lenlen = 0;
253     }
254 
255     nextpos = 1 + lenlen;
256     if (lenlen > 2 || 1 + lenlen + *len > datalen) {
257         return -1;
258     }
259 
260     return nextpos;
261 }
262 
263 int
_libssh2_pem_decode_sequence(unsigned char ** data,unsigned int * datalen)264 _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen)
265 {
266     unsigned int len;
267     int lenlen;
268 
269     if (*datalen < 1) {
270         return -1;
271     }
272 
273     if ((*data)[0] != '\x30') {
274         return -1;
275     }
276 
277     (*data)++;
278     (*datalen)--;
279 
280     lenlen = read_asn1_length(*data, *datalen, &len);
281     if (lenlen < 0 || lenlen + len != *datalen) {
282         return -1;
283     }
284 
285     *data += lenlen;
286     *datalen -= lenlen;
287 
288     return 0;
289 }
290 
291 int
_libssh2_pem_decode_integer(unsigned char ** data,unsigned int * datalen,unsigned char ** i,unsigned int * ilen)292 _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
293                             unsigned char **i, unsigned int *ilen)
294 {
295     unsigned int len;
296     int lenlen;
297 
298     if (*datalen < 1) {
299         return -1;
300     }
301 
302     if ((*data)[0] != '\x02') {
303         return -1;
304     }
305 
306     (*data)++;
307     (*datalen)--;
308 
309     lenlen = read_asn1_length(*data, *datalen, &len);
310     if (lenlen < 0 || lenlen + len > *datalen) {
311         return -1;
312     }
313 
314     *data += lenlen;
315     *datalen -= lenlen;
316 
317     *i = *data;
318     *ilen = len;
319 
320     *data += len;
321     *datalen -= len;
322 
323     return 0;
324 }
325