iPXE
|
00001 /* 00002 * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>. 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License as 00006 * published by the Free Software Foundation; either version 2 of the 00007 * License, or any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, but 00010 * WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 * General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software 00016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 * 02110-1301, USA. 00018 * 00019 * You can also choose to distribute this program under the terms of 00020 * the Unmodified Binary Distribution Licence (as given in the file 00021 * COPYING.UBDL), provided that you have satisfied its requirements. 00022 */ 00023 00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 00025 00026 #include <stdlib.h> 00027 #include <errno.h> 00028 #include <assert.h> 00029 #include <ipxe/asn1.h> 00030 #include <ipxe/base64.h> 00031 #include <ipxe/uaccess.h> 00032 #include <ipxe/image.h> 00033 #include <ipxe/pem.h> 00034 00035 /** @file 00036 * 00037 * PEM-encoded ASN.1 data 00038 * 00039 */ 00040 00041 /** 00042 * Locate next line 00043 * 00044 * @v data PEM data 00045 * @v len Length of PEM data 00046 * @v offset Starting offset 00047 * @ret next Offset to next line 00048 */ 00049 static size_t pem_next ( userptr_t data, size_t len, size_t offset ) { 00050 off_t eol; 00051 00052 /* Find and skip next newline character, if any */ 00053 eol = memchr_user ( data, offset, '\n', ( len - offset ) ); 00054 if ( eol < 0 ) 00055 return len; 00056 return ( eol + 1 ); 00057 } 00058 00059 /** 00060 * Locate boundary marker line 00061 * 00062 * @v data PEM data 00063 * @v len Length of PEM data 00064 * @v offset Starting offset 00065 * @v marker Boundary marker 00066 * @ret offset Offset to boundary marker line, or negative error 00067 */ 00068 static int pem_marker ( userptr_t data, size_t len, size_t offset, 00069 const char *marker ) { 00070 char buf[ strlen ( marker ) ]; 00071 00072 /* Sanity check */ 00073 assert ( offset <= len ); 00074 00075 /* Scan for marker at start of line */ 00076 while ( offset < len ) { 00077 00078 /* Check for marker */ 00079 if ( ( len - offset ) < sizeof ( buf ) ) 00080 break; 00081 copy_from_user ( buf, data, offset, sizeof ( buf ) ); 00082 if ( memcmp ( buf, marker, sizeof ( buf ) ) == 0 ) 00083 return offset; 00084 00085 /* Move to next line */ 00086 offset = pem_next ( data, len, offset ); 00087 assert ( offset <= len ); 00088 } 00089 00090 return -ENOENT; 00091 } 00092 00093 /** 00094 * Extract ASN.1 object from PEM data 00095 * 00096 * @v data PEM data 00097 * @v len Length of PEM data 00098 * @v offset Offset within data 00099 * @v cursor ASN.1 cursor to fill in 00100 * @ret next Offset to next object, or negative error 00101 * 00102 * The caller is responsible for eventually calling free() on the 00103 * allocated ASN.1 cursor. 00104 */ 00105 int pem_asn1 ( userptr_t data, size_t len, size_t offset, 00106 struct asn1_cursor **cursor ) { 00107 size_t encoded_len; 00108 size_t decoded_max_len; 00109 char *encoded; 00110 void *decoded; 00111 int decoded_len; 00112 int begin; 00113 int end; 00114 int rc; 00115 00116 /* Locate and skip BEGIN marker */ 00117 begin = pem_marker ( data, len, offset, PEM_BEGIN ); 00118 if ( begin < 0 ) { 00119 rc = begin; 00120 DBGC ( data, "PEM [%#zx,%#zx) missing BEGIN marker: %s\n", 00121 offset, len, strerror ( rc ) ); 00122 goto err_begin; 00123 } 00124 begin = pem_next ( data, len, begin ); 00125 00126 /* Locate and skip END marker */ 00127 end = pem_marker ( data, len, begin, PEM_END ); 00128 if ( end < 0 ) { 00129 rc = end; 00130 DBGC ( data, "PEM [%#zx,%#zx) missing END marker: %s\n", 00131 offset, len, strerror ( rc ) ); 00132 goto err_end; 00133 } 00134 encoded_len = ( end - begin ); 00135 end = pem_next ( data, len, end ); 00136 00137 /* Extract Base64-encoded data */ 00138 encoded = malloc ( encoded_len + 1 /* NUL */ ); 00139 if ( ! encoded ) { 00140 rc = -ENOMEM; 00141 goto err_alloc_encoded; 00142 } 00143 copy_from_user ( encoded, data, begin, encoded_len ); 00144 encoded[encoded_len] = '\0'; 00145 00146 /* Allocate cursor and data buffer */ 00147 decoded_max_len = base64_decoded_max_len ( encoded ); 00148 *cursor = malloc ( sizeof ( **cursor ) + decoded_max_len ); 00149 if ( ! *cursor ) { 00150 rc = -ENOMEM; 00151 goto err_alloc_cursor; 00152 } 00153 decoded = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) ); 00154 00155 /* Decode Base64-encoded data */ 00156 decoded_len = base64_decode ( encoded, decoded, decoded_max_len ); 00157 if ( decoded_len < 0 ) { 00158 rc = decoded_len; 00159 DBGC ( data, "PEM could not decode: %s\n", strerror ( rc ) ); 00160 goto err_decode; 00161 } 00162 (*cursor)->data = decoded; 00163 (*cursor)->len = decoded_len; 00164 assert ( (*cursor)->len <= decoded_max_len ); 00165 00166 /* Free Base64-encoded data */ 00167 free ( encoded ); 00168 00169 /* Update offset and skip any unencapsulated trailer */ 00170 offset = end; 00171 if ( pem_marker ( data, len, offset, PEM_BEGIN ) < 0 ) 00172 offset = len; 00173 00174 return offset; 00175 00176 err_decode: 00177 free ( *cursor ); 00178 *cursor = NULL; 00179 err_alloc_cursor: 00180 free ( encoded ); 00181 err_alloc_encoded: 00182 err_end: 00183 err_begin: 00184 return rc; 00185 } 00186 00187 /** 00188 * Probe PEM image 00189 * 00190 * @v image PEM image 00191 * @ret rc Return status code 00192 */ 00193 static int pem_image_probe ( struct image *image ) { 00194 int offset; 00195 int rc; 00196 00197 /* Check that image contains a BEGIN marker */ 00198 if ( ( offset = pem_marker ( image->data, image->len, 0, 00199 PEM_BEGIN ) ) < 0 ) { 00200 rc = offset; 00201 DBGC ( image, "PEM %s has no BEGIN marker: %s\n", 00202 image->name, strerror ( rc ) ); 00203 return rc; 00204 } 00205 00206 return 0; 00207 } 00208 00209 /** 00210 * Extract ASN.1 object from image 00211 * 00212 * @v image PEM image 00213 * @v offset Offset within image 00214 * @v cursor ASN.1 cursor to fill in 00215 * @ret next Offset to next image, or negative error 00216 * 00217 * The caller is responsible for eventually calling free() on the 00218 * allocated ASN.1 cursor. 00219 */ 00220 static int pem_image_asn1 ( struct image *image, size_t offset, 00221 struct asn1_cursor **cursor ) { 00222 int next; 00223 int rc; 00224 00225 /* Extract ASN.1 object */ 00226 if ( ( next = pem_asn1 ( image->data, image->len, offset, 00227 cursor ) ) < 0 ) { 00228 rc = next; 00229 DBGC ( image, "PEM %s could not extract ASN.1: %s\n", 00230 image->name, strerror ( rc ) ); 00231 return rc; 00232 } 00233 00234 return next; 00235 } 00236 00237 /** PEM image type */ 00238 struct image_type pem_image_type __image_type ( PROBE_NORMAL ) = { 00239 .name = "PEM", 00240 .probe = pem_image_probe, 00241 .asn1 = pem_image_asn1, 00242 };