VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/pemfile.cpp@ 51770

Last change on this file since 51770 was 51770, checked in by vboxsync, 10 years ago

Merged in iprt++ dev branch.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: pemfile.cpp 51770 2014-07-01 18:14:02Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - PEM file reader / writer.
4 */
5
6/*
7 * Copyright (C) 2006-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/crypto/pem.h>
33
34#include <iprt/base64.h>
35#include <iprt/ctype.h>
36#include <iprt/err.h>
37#include <iprt/mem.h>
38#include <iprt/file.h>
39#include <iprt/string.h>
40
41
42
43/**
44 * Looks for a PEM-like marker.
45 *
46 * @returns true if found, fasle if not.
47 * @param pbContent Start of the content to search thru.
48 * @param cbContent The size of the content to search.
49 * @param offStart The offset into pbContent to start searching.
50 * @param pszLeadWord The lead word (BEGIN/END).
51 * @param cchLeadWord The length of the lead word.
52 * @param paMarkers Pointer to an array of markers.
53 * @param cMarkers Number of markers in the array.
54 * @param ppMatch Where to return the pointer to the matching
55 * marker. Optional.
56 * @param poffBegin Where to return the start offset of the marker.
57 * Optional.
58 * @param poffEnd Where to return the end offset of the marker
59 * (trailing whitespace and newlines will be
60 * skipped). Optional.
61 */
62static bool rtCrPemFindMarker(uint8_t const *pbContent, size_t cbContent, size_t offStart,
63 const char *pszLeadWord, size_t cchLeadWord, PCRTCRPEMMARKER paMarkers, size_t cMarkers,
64 PCRTCRPEMMARKER *ppMatch, size_t *poffBegin, size_t *poffEnd)
65{
66 /* Remember the start of the content for the purpose of calculating offsets. */
67 uint8_t const * const pbStart = pbContent;
68
69 /* Skip adhead by offStart */
70 if (offStart >= cbContent)
71 return false;
72 pbContent += offStart;
73 cbContent -= offStart;
74
75 /*
76 * Search the content.
77 */
78 while (cbContent > 6)
79 {
80 /*
81 * Look for dashes.
82 */
83 uint8_t const *pbStartSearch = pbContent;
84 pbContent = (uint8_t const *)memchr(pbContent, '-', cbContent);
85 if (!pbContent)
86 break;
87
88 cbContent -= pbContent - pbStartSearch;
89 if (cbContent < 6)
90 break;
91
92 /*
93 * There must be at least three to interest us.
94 */
95 if ( pbContent[1] == '-'
96 && pbContent[2] == '-')
97 {
98 unsigned cDashes = 3;
99 while (cDashes < cbContent && pbContent[cDashes] == '-')
100 cDashes++;
101
102 if (poffBegin)
103 *poffBegin = pbContent - pbStart;
104 cbContent -= cDashes;
105 pbContent += cDashes;
106
107 /*
108 * Match lead word.
109 */
110 if ( cbContent > cchLeadWord
111 && memcmp(pbContent, pszLeadWord, cchLeadWord) == 0
112 && RT_C_IS_BLANK(pbContent[cchLeadWord]) )
113 {
114 pbContent += cchLeadWord;
115 cbContent -= cchLeadWord;
116 while (cbContent > 0 && RT_C_IS_BLANK(*pbContent))
117 {
118 pbContent++;
119 cbContent--;
120 }
121
122 /*
123 * Match one of the specified markers.
124 */
125 uint8_t const *pbSavedContent = pbContent;
126 size_t const cbSavedContent = cbContent;
127 uint32_t iMarker = 0;
128 while (iMarker < cMarkers)
129 {
130 pbContent = pbSavedContent;
131 cbContent = cbSavedContent;
132
133 uint32_t cWords = paMarkers[iMarker].cWords;
134 PCRTCRPEMMARKERWORD pWord = paMarkers[iMarker].paWords;
135 while (cWords > 0)
136 {
137 uint32_t const cchWord = pWord->cchWord;
138 if (cbContent <= cchWord)
139 break;
140 if (memcmp(pbContent, pWord->pszWord, cchWord))
141 break;
142 pbContent += cchWord;
143 cbContent -= cchWord;
144
145 if (!cbContent || !RT_C_IS_BLANK(*pbContent))
146 break;
147 do
148 {
149 pbContent++;
150 cbContent--;
151 } while (cbContent > 0 && RT_C_IS_BLANK(*pbContent));
152
153 cWords--;
154 if (cWords == 0)
155 {
156 /*
157 * If there are three or more dashes following now, we've got a hit.
158 */
159 if ( cbContent > 3
160 && pbContent[0] == '-'
161 && pbContent[1] == '-'
162 && pbContent[2] == '-')
163 {
164 cDashes = 3;
165 while (cDashes < cbContent && pbContent[cDashes] == '-')
166 cDashes++;
167 cbContent -= cDashes;
168 pbContent += cDashes;
169
170 /*
171 * Skip spaces and newline.
172 */
173 while (cbContent > 0 && RT_C_IS_SPACE(*pbContent))
174 pbContent++, cbContent--;
175 if (poffEnd)
176 *poffEnd = pbContent - pbStart;
177 if (*ppMatch)
178 *ppMatch = &paMarkers[iMarker];
179 return true;
180 }
181 break;
182 }
183 } /* for each word in marker. */
184 } /* for each marker. */
185 }
186 }
187 else
188 {
189 pbContent++;
190 cbContent--;
191 }
192 }
193
194 return false;
195}
196
197
198static bool rtCrPemFindMarkerSection(uint8_t const *pbContent, size_t cbContent, size_t offStart,
199 PCRTCRPEMMARKER paMarkers, size_t cMarkers,
200 PCRTCRPEMMARKER *ppMatch, size_t *poffBegin, size_t *poffEnd, size_t *poffResume)
201{
202 /** @todo Detect BEGIN / END mismatch. */
203 PCRTCRPEMMARKER pMatch;
204 if (rtCrPemFindMarker(pbContent, cbContent, offStart, "BEGIN", 5, paMarkers, cMarkers,
205 &pMatch, NULL /*poffStart*/, poffBegin))
206 return rtCrPemFindMarker(pbContent, cbContent, *poffBegin, "END", 3, pMatch, 1,
207 NULL /*ppMatch*/, poffEnd, poffResume);
208 return false;
209}
210
211
212
213/**
214 * Does the decoding of a PEM-like data blob after it has been located.
215 *
216 * @returns IPRT status ocde
217 * @param pbContent The start of the PEM-like content (text).
218 * @param cbContent The max size of the PEM-like content.
219 * @param ppvDecoded Where to return a heap block containing the
220 * decoded content.
221 * @param pcbDecoded Where to return the size of the decoded content.
222 */
223static int rtCrPemDecodeBase64(uint8_t const *pbContent, size_t cbContent, void **ppvDecoded, size_t *pcbDecoded)
224{
225 ssize_t cbDecoded = RTBase64DecodedSizeEx((const char *)pbContent, cbContent, NULL);
226 if (cbDecoded < 0)
227 return VERR_INVALID_BASE64_ENCODING;
228
229 *pcbDecoded = cbDecoded;
230 void *pvDecoded = RTMemAlloc(cbDecoded);
231 if (!pvDecoded)
232 return VERR_NO_MEMORY;
233
234 size_t cbActual;
235 int rc = RTBase64DecodeEx((const char *)pbContent, cbContent, pvDecoded, cbDecoded, &cbActual, NULL);
236 if (RT_SUCCESS(rc))
237 {
238 if (cbActual == (size_t)cbDecoded)
239 {
240 *ppvDecoded = pvDecoded;
241 return VINF_SUCCESS;
242 }
243 rc = VERR_INTERNAL_ERROR_3;
244 }
245 RTMemFree(pvDecoded);
246 return rc;
247}
248
249
250/**
251 * Checks if the content of a file looks to be binary or not.
252 *
253 * @returns true if likely to be binary, false if not binary.
254 * @param pbFile The file bytes to scan.
255 * @param cbFile The number of bytes.
256 */
257static bool rtCrPemIsBinaryFile(uint8_t *pbFile, size_t cbFile)
258{
259 /*
260 * Assume a well formed PEM file contains only 7-bit ASCII and restricts
261 * itself to the following control characters:
262 * tab, newline, return, form feed
263 */
264 while (cbFile-- > 0)
265 {
266 uint8_t const b = *pbFile++;
267 if ( b >= 0x7f
268 || (b < 32 && b != '\t' && b != '\n' && b != '\r' && b != '\f') )
269 {
270 /* Ignore EOT (4), SUB (26) and NUL (0) at the end of the file. */
271 if ( (b == 4 || b == 26)
272 && ( cbFile == 0
273 || ( cbFile == 1
274 && *pbFile == '\0')))
275 return true;
276 if (b == 0 && cbFile == 0)
277 return true;
278 return false;
279 }
280 }
281 return true;
282}
283
284
285RTDECL(int) RTCrPemFreeSections(PCRTCRPEMSECTION pSectionHead)
286{
287 while (pSectionHead != NULL)
288 {
289 PRTCRPEMSECTION pFree = (PRTCRPEMSECTION)pSectionHead;
290 pSectionHead = pSectionHead->pNext;
291
292 if (pFree->pMarker)
293 {
294 if (pFree->pbData)
295 {
296 RTMemFree(pFree->pbData);
297 pFree->pbData = NULL;
298 pFree->cbData = 0;
299 }
300
301 if (pFree->pszPreamble)
302 {
303 RTMemFree(pFree->pszPreamble);
304 pFree->pszPreamble = NULL;
305 pFree->cchPreamble = 0;
306 }
307 }
308 else
309 {
310 RTFileReadAllFree(pFree->pbData, pFree->cbData);
311 Assert(!pFree->pszPreamble);
312 }
313 pFree->pbData = NULL;
314 pFree->cbData = 0;
315 }
316 return VINF_SUCCESS;
317}
318
319
320RTDECL(int) RTCrPemReadFile(const char *pszFilename, uint32_t fFlags, PCRTCRPEMMARKER paMarkers, size_t cMarkers,
321 PCRTCRPEMSECTION *ppSectionHead, PRTERRINFO pErrInfo)
322{
323 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
324
325 size_t cbContent;
326 uint8_t *pbContent;
327 int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, (void **)&pbContent, &cbContent);
328 if (RT_SUCCESS(rc))
329 {
330 PRTCRPEMSECTION pSection = (PRTCRPEMSECTION)RTMemAllocZ(sizeof(*pSection));
331 if (pSection)
332 {
333 /*
334 * Try locate the first section.
335 */
336 size_t offBegin, offEnd, offResume;
337 PCRTCRPEMMARKER pMatch;
338 if ( !rtCrPemIsBinaryFile(pbContent, cbContent)
339 && rtCrPemFindMarkerSection(pbContent, cbContent, 0 /*offStart*/, paMarkers, cMarkers,
340 &pMatch, &offBegin, &offEnd, &offResume) )
341 {
342 PCRTCRPEMSECTION *ppNext = ppSectionHead;
343 for (;;)
344 {
345 //pSection->pNext = NULL;
346 pSection->pMarker = pMatch;
347 //pSection->pbData = NULL;
348 //pSection->cbData = 0;
349 //pSection->pszPreamble = NULL;
350 //pSection->cchPreamble = 0;
351
352 *ppNext = pSection;
353 ppNext = &pSection->pNext;
354
355 /* Decode the section. */
356 /** @todo copy the preamble as well. */
357 rc = rtCrPemDecodeBase64(pbContent + offBegin, offEnd - offBegin,
358 (void **)&pSection->pbData, &pSection->cbData);
359 if (RT_FAILURE(rc))
360 {
361 pSection->pbData = NULL;
362 pSection->cbData = 0;
363 break;
364 }
365
366 /* More sections? */
367 if ( offResume + 12 >= cbContent
368 || offResume >= cbContent
369 || !rtCrPemFindMarkerSection(pbContent, cbContent, offResume, paMarkers, cMarkers,
370 &pMatch, &offBegin, &offEnd, &offResume) )
371 break; /* No. */
372
373 /* Ok, allocate a new record for it. */
374 pSection = (PRTCRPEMSECTION)RTMemAllocZ(sizeof(*pSection));
375 if (RT_UNLIKELY(!pSection))
376 {
377 rc = VERR_NO_MEMORY;
378 break;
379 }
380 }
381 if (RT_SUCCESS(rc))
382 {
383 RTFileReadAllFree(pbContent, cbContent);
384 return rc;
385 }
386
387 RTCrPemFreeSections(*ppSectionHead);
388 }
389 else
390 {
391 /*
392 * No PEM section found. Return the whole file as one binary section.
393 */
394 //pSection->pNext = NULL;
395 //pSection->pMarker = NULL;
396 pSection->pbData = pbContent;
397 pSection->cbData = cbContent;
398 //pSection->pszPreamble = NULL;
399 //pSection->cchPreamble = 0;
400 *ppSectionHead = pSection;
401 return VINF_SUCCESS;
402 }
403 }
404 else
405 rc = VERR_NO_MEMORY;
406 RTFileReadAllFree(pbContent, cbContent);
407 }
408 *ppSectionHead = NULL;
409 return rc;
410}
411
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette