iPXE
pem.c
Go to the documentation of this file.
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 };