iPXE
pem.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <ipxe/asn1.h>
30 #include <ipxe/base64.h>
31 #include <ipxe/image.h>
32 #include <ipxe/pem.h>
33 
34 /** @file
35  *
36  * PEM-encoded ASN.1 data
37  *
38  */
39 
40 /**
41  * Locate next line
42  *
43  * @v data PEM data
44  * @v len Length of PEM data
45  * @v offset Starting offset
46  * @ret next Offset to next line
47  */
48 static size_t pem_next ( const void *data, size_t len, size_t offset ) {
49  const void *sep;
50 
51  /* Find and skip next newline character, if any */
52  sep = memchr ( ( data + offset ), '\n', ( len - offset ) );
53  if ( ! sep )
54  return len;
55  return ( ( sep - data ) + 1 );
56 }
57 
58 /**
59  * Locate boundary marker line
60  *
61  * @v data PEM data
62  * @v len Length of PEM data
63  * @v offset Starting offset
64  * @v marker Boundary marker
65  * @ret offset Offset to boundary marker line, or negative error
66  */
67 static int pem_marker ( const void *data, size_t len, size_t offset,
68  const char *marker ) {
69  size_t marker_len = strlen ( marker );
70 
71  /* Sanity check */
72  assert ( offset <= len );
73 
74  /* Scan for marker at start of line */
75  while ( offset < len ) {
76 
77  /* Check for marker */
78  if ( ( len - offset ) < marker_len )
79  break;
80  if ( memcmp ( ( data + offset ), marker, marker_len ) == 0 )
81  return offset;
82 
83  /* Move to next line */
84  offset = pem_next ( data, len, offset );
85  assert ( offset <= len );
86  }
87 
88  return -ENOENT;
89 }
90 
91 /**
92  * Extract ASN.1 object from PEM data
93  *
94  * @v data PEM data
95  * @v len Length of PEM data
96  * @v offset Offset within data
97  * @v cursor ASN.1 cursor to fill in
98  * @ret next Offset to next object, or negative error
99  *
100  * The caller is responsible for eventually calling free() on the
101  * allocated ASN.1 cursor.
102  */
103 int pem_asn1 ( const void *data, size_t len, size_t offset,
104  struct asn1_cursor **cursor ) {
105  size_t encoded_len;
106  size_t decoded_max_len;
107  char *encoded;
108  void *decoded;
109  int decoded_len;
110  int begin;
111  int end;
112  int rc;
113 
114  /* Locate and skip BEGIN marker */
115  begin = pem_marker ( data, len, offset, PEM_BEGIN );
116  if ( begin < 0 ) {
117  rc = begin;
118  DBGC ( data, "PEM [%#zx,%#zx) missing BEGIN marker: %s\n",
119  offset, len, strerror ( rc ) );
120  goto err_begin;
121  }
122  begin = pem_next ( data, len, begin );
123 
124  /* Locate and skip END marker */
125  end = pem_marker ( data, len, begin, PEM_END );
126  if ( end < 0 ) {
127  rc = end;
128  DBGC ( data, "PEM [%#zx,%#zx) missing END marker: %s\n",
129  offset, len, strerror ( rc ) );
130  goto err_end;
131  }
132  encoded_len = ( end - begin );
133  end = pem_next ( data, len, end );
134 
135  /* Extract Base64-encoded data */
136  encoded = malloc ( encoded_len + 1 /* NUL */ );
137  if ( ! encoded ) {
138  rc = -ENOMEM;
139  goto err_alloc_encoded;
140  }
141  memcpy ( encoded, ( data + begin ), encoded_len );
142  encoded[encoded_len] = '\0';
143 
144  /* Allocate cursor and data buffer */
145  decoded_max_len = base64_decoded_max_len ( encoded );
146  *cursor = malloc ( sizeof ( **cursor ) + decoded_max_len );
147  if ( ! *cursor ) {
148  rc = -ENOMEM;
149  goto err_alloc_cursor;
150  }
151  decoded = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) );
152 
153  /* Decode Base64-encoded data */
154  decoded_len = base64_decode ( encoded, decoded, decoded_max_len );
155  if ( decoded_len < 0 ) {
156  rc = decoded_len;
157  DBGC ( data, "PEM could not decode: %s\n", strerror ( rc ) );
158  goto err_decode;
159  }
160  (*cursor)->data = decoded;
161  (*cursor)->len = decoded_len;
162  assert ( (*cursor)->len <= decoded_max_len );
163 
164  /* Free Base64-encoded data */
165  free ( encoded );
166 
167  /* Update offset and skip any unencapsulated trailer */
168  offset = end;
169  if ( pem_marker ( data, len, offset, PEM_BEGIN ) < 0 )
170  offset = len;
171 
172  return offset;
173 
174  err_decode:
175  free ( *cursor );
176  *cursor = NULL;
177  err_alloc_cursor:
178  free ( encoded );
179  err_alloc_encoded:
180  err_end:
181  err_begin:
182  return rc;
183 }
184 
185 /**
186  * Probe PEM image
187  *
188  * @v image PEM image
189  * @ret rc Return status code
190  */
191 static int pem_image_probe ( struct image *image ) {
192  int offset;
193  int rc;
194 
195  /* Check that image contains a BEGIN marker */
196  if ( ( offset = pem_marker ( image->data, image->len, 0,
197  PEM_BEGIN ) ) < 0 ) {
198  rc = offset;
199  DBGC ( image, "PEM %s has no BEGIN marker: %s\n",
200  image->name, strerror ( rc ) );
201  return rc;
202  }
203 
204  return 0;
205 }
206 
207 /**
208  * Extract ASN.1 object from image
209  *
210  * @v image PEM image
211  * @v offset Offset within image
212  * @v cursor ASN.1 cursor to fill in
213  * @ret next Offset to next image, or negative error
214  *
215  * The caller is responsible for eventually calling free() on the
216  * allocated ASN.1 cursor.
217  */
218 static int pem_image_asn1 ( struct image *image, size_t offset,
219  struct asn1_cursor **cursor ) {
220  int next;
221  int rc;
222 
223  /* Extract ASN.1 object */
224  if ( ( next = pem_asn1 ( image->data, image->len, offset,
225  cursor ) ) < 0 ) {
226  rc = next;
227  DBGC ( image, "PEM %s could not extract ASN.1: %s\n",
228  image->name, strerror ( rc ) );
229  return rc;
230  }
231 
232  return next;
233 }
234 
235 /** PEM image type */
236 struct image_type pem_image_type __image_type ( PROBE_NORMAL ) = {
237  .name = "PEM",
238  .probe = pem_image_probe,
239  .asn1 = pem_image_asn1,
240 };
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
int pem_asn1(const void *data, size_t len, size_t offset, struct asn1_cursor **cursor)
Extract ASN.1 object from PEM data.
Definition: pem.c:103
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
static int pem_marker(const void *data, size_t len, size_t offset, const char *marker)
Locate boundary marker line.
Definition: pem.c:67
Error codes.
const void * data
Read-only data.
Definition: image.h:50
#define DBGC(...)
Definition: compiler.h:505
An executable image type.
Definition: image.h:94
#define ENOENT
No such file or directory.
Definition: errno.h:514
static size_t pem_next(const void *data, size_t len, size_t offset)
Locate next line.
Definition: pem.c:48
#define PROBE_NORMAL
Normal image probe priority.
Definition: image.h:155
int base64_decode(const char *encoded, void *data, size_t len)
Base64-decode string.
Definition: base64.c:91
An executable image.
Definition: image.h:23
struct image_type pem_image_type __image_type(PROBE_NORMAL)
PEM image type.
char * name
Name of this image type.
Definition: image.h:96
void * memchr(const void *src, int character, size_t len)
Find character within a memory region.
Definition: string.c:135
#define ENOMEM
Not enough space.
Definition: errno.h:534
static size_t base64_decoded_max_len(const char *encoded)
Calculate maximum length of base64-decoded string.
Definition: base64.h:34
void * memcpy(void *dest, const void *src, size_t len) __nonnull
static int pem_image_asn1(struct image *image, size_t offset, struct asn1_cursor **cursor)
Extract ASN.1 object from image.
Definition: pem.c:218
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
Executable images.
ASN.1 encoding.
PEM-encoded ASN.1 data.
ring len
Length.
Definition: dwmac.h:231
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
size_t len
Length of raw file image.
Definition: image.h:55
size_t strlen(const char *src)
Get length of string.
Definition: string.c:243
uint32_t next
Next descriptor address.
Definition: dwmac.h:22
void * malloc(size_t size)
Allocate memory.
Definition: malloc.c:620
Base64 encoding.
#define PEM_END
Post-encapsulation boundary marker.
Definition: pem.h:20
uint32_t end
Ending offset.
Definition: netvsc.h:18
uint8_t data[48]
Additional event data.
Definition: ena.h:22
static int pem_image_probe(struct image *image)
Probe PEM image.
Definition: pem.c:191
#define PEM_BEGIN
Pre-encapsulation boundary marker.
Definition: pem.h:17
uint16_t offset
Offset to command line.
Definition: bzimage.h:8
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:114
char * name
Name.
Definition: image.h:37
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
An ASN.1 object cursor.
Definition: asn1.h:20
struct eth_slow_marker_tlv marker
Marker information.
Definition: eth_slow.h:14