1*12720SWyllys.Ingersoll@Sun.COM /* 2*12720SWyllys.Ingersoll@Sun.COM * CDDL HEADER START 3*12720SWyllys.Ingersoll@Sun.COM * 4*12720SWyllys.Ingersoll@Sun.COM * The contents of this file are subject to the terms of the 5*12720SWyllys.Ingersoll@Sun.COM * Common Development and Distribution License (the "License"). 6*12720SWyllys.Ingersoll@Sun.COM * You may not use this file except in compliance with the License. 7*12720SWyllys.Ingersoll@Sun.COM * 8*12720SWyllys.Ingersoll@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*12720SWyllys.Ingersoll@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*12720SWyllys.Ingersoll@Sun.COM * See the License for the specific language governing permissions 11*12720SWyllys.Ingersoll@Sun.COM * and limitations under the License. 12*12720SWyllys.Ingersoll@Sun.COM * 13*12720SWyllys.Ingersoll@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*12720SWyllys.Ingersoll@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*12720SWyllys.Ingersoll@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*12720SWyllys.Ingersoll@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*12720SWyllys.Ingersoll@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*12720SWyllys.Ingersoll@Sun.COM * 19*12720SWyllys.Ingersoll@Sun.COM * CDDL HEADER END 20*12720SWyllys.Ingersoll@Sun.COM */ 21*12720SWyllys.Ingersoll@Sun.COM 22*12720SWyllys.Ingersoll@Sun.COM /* 23*12720SWyllys.Ingersoll@Sun.COM * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24*12720SWyllys.Ingersoll@Sun.COM */ 25*12720SWyllys.Ingersoll@Sun.COM 26*12720SWyllys.Ingersoll@Sun.COM /** 27*12720SWyllys.Ingersoll@Sun.COM * \file KMSAgentPKICertOpenSSL.cpp 28*12720SWyllys.Ingersoll@Sun.COM */ 29*12720SWyllys.Ingersoll@Sun.COM 30*12720SWyllys.Ingersoll@Sun.COM #include <stdio.h> 31*12720SWyllys.Ingersoll@Sun.COM #include <openssl/bio.h> 32*12720SWyllys.Ingersoll@Sun.COM #include <openssl/pem.h> 33*12720SWyllys.Ingersoll@Sun.COM 34*12720SWyllys.Ingersoll@Sun.COM #include "SYSCommon.h" 35*12720SWyllys.Ingersoll@Sun.COM #include "KMSAgentPKIimpl.h" 36*12720SWyllys.Ingersoll@Sun.COM 37*12720SWyllys.Ingersoll@Sun.COM typedef struct X509control 38*12720SWyllys.Ingersoll@Sun.COM { 39*12720SWyllys.Ingersoll@Sun.COM X509* pX509; 40*12720SWyllys.Ingersoll@Sun.COM } X509control; 41*12720SWyllys.Ingersoll@Sun.COM 42*12720SWyllys.Ingersoll@Sun.COM void * InitializeCertImpl() 43*12720SWyllys.Ingersoll@Sun.COM { 44*12720SWyllys.Ingersoll@Sun.COM X509control *pX509Control = (X509control *) malloc(sizeof(X509control)); 45*12720SWyllys.Ingersoll@Sun.COM 46*12720SWyllys.Ingersoll@Sun.COM if ( pX509Control != NULL ) 47*12720SWyllys.Ingersoll@Sun.COM { 48*12720SWyllys.Ingersoll@Sun.COM pX509Control->pX509 = NULL; 49*12720SWyllys.Ingersoll@Sun.COM } 50*12720SWyllys.Ingersoll@Sun.COM 51*12720SWyllys.Ingersoll@Sun.COM return pX509Control; 52*12720SWyllys.Ingersoll@Sun.COM } 53*12720SWyllys.Ingersoll@Sun.COM 54*12720SWyllys.Ingersoll@Sun.COM /** 55*12720SWyllys.Ingersoll@Sun.COM * export the Cert to a memory BIO, if error, return NULL 56*12720SWyllys.Ingersoll@Sun.COM */ 57*12720SWyllys.Ingersoll@Sun.COM BIO* SaveCertToMemoryBIO( X509control* i_pX509control ) 58*12720SWyllys.Ingersoll@Sun.COM { 59*12720SWyllys.Ingersoll@Sun.COM BIO *pMemBio = NULL; 60*12720SWyllys.Ingersoll@Sun.COM int iReturn; 61*12720SWyllys.Ingersoll@Sun.COM 62*12720SWyllys.Ingersoll@Sun.COM // create memory BIO 63*12720SWyllys.Ingersoll@Sun.COM pMemBio = BIO_new(BIO_s_mem()); 64*12720SWyllys.Ingersoll@Sun.COM 65*12720SWyllys.Ingersoll@Sun.COM if(pMemBio == NULL) 66*12720SWyllys.Ingersoll@Sun.COM { 67*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- no memory 68*12720SWyllys.Ingersoll@Sun.COM return NULL; 69*12720SWyllys.Ingersoll@Sun.COM } 70*12720SWyllys.Ingersoll@Sun.COM 71*12720SWyllys.Ingersoll@Sun.COM //iReturn = PEM_write_bio_X509(pMemBio, m_pNative); 72*12720SWyllys.Ingersoll@Sun.COM iReturn = PEM_write_bio_X509(pMemBio, i_pX509control->pX509); 73*12720SWyllys.Ingersoll@Sun.COM 74*12720SWyllys.Ingersoll@Sun.COM if(!iReturn) // return 0: means error occurs 75*12720SWyllys.Ingersoll@Sun.COM { 76*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- could not export private key 77*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); 78*12720SWyllys.Ingersoll@Sun.COM return NULL; 79*12720SWyllys.Ingersoll@Sun.COM } 80*12720SWyllys.Ingersoll@Sun.COM 81*12720SWyllys.Ingersoll@Sun.COM return pMemBio; 82*12720SWyllys.Ingersoll@Sun.COM } 83*12720SWyllys.Ingersoll@Sun.COM 84*12720SWyllys.Ingersoll@Sun.COM bool SaveX509CertTofile( 85*12720SWyllys.Ingersoll@Sun.COM void* const i_pImplResource, 86*12720SWyllys.Ingersoll@Sun.COM const char * const i_pcFileName ) 87*12720SWyllys.Ingersoll@Sun.COM { 88*12720SWyllys.Ingersoll@Sun.COM FATAL_ASSERT( i_pImplResource != NULL && i_pcFileName ); 89*12720SWyllys.Ingersoll@Sun.COM 90*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*)i_pImplResource; 91*12720SWyllys.Ingersoll@Sun.COM // the BIO for output, need cleanup when exiting 92*12720SWyllys.Ingersoll@Sun.COM BIO *pMemBio = NULL; 93*12720SWyllys.Ingersoll@Sun.COM int iLength; 94*12720SWyllys.Ingersoll@Sun.COM unsigned char *pData; 95*12720SWyllys.Ingersoll@Sun.COM FILE *fp; 96*12720SWyllys.Ingersoll@Sun.COM 97*12720SWyllys.Ingersoll@Sun.COM // create memory BIO 98*12720SWyllys.Ingersoll@Sun.COM pMemBio = SaveCertToMemoryBIO( pX509control ); 99*12720SWyllys.Ingersoll@Sun.COM 100*12720SWyllys.Ingersoll@Sun.COM if(pMemBio == NULL) 101*12720SWyllys.Ingersoll@Sun.COM { 102*12720SWyllys.Ingersoll@Sun.COM return false; 103*12720SWyllys.Ingersoll@Sun.COM } 104*12720SWyllys.Ingersoll@Sun.COM 105*12720SWyllys.Ingersoll@Sun.COM // now pMemBIO != NULL, remember to free it before exiting 106*12720SWyllys.Ingersoll@Sun.COM iLength = BIO_get_mem_data(pMemBio, &pData); 107*12720SWyllys.Ingersoll@Sun.COM 108*12720SWyllys.Ingersoll@Sun.COM // open the file 109*12720SWyllys.Ingersoll@Sun.COM fp = fopen(i_pcFileName, "wb"); 110*12720SWyllys.Ingersoll@Sun.COM if(fp == NULL) 111*12720SWyllys.Ingersoll@Sun.COM { 112*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- could not open file for exporting Cert 113*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); 114*12720SWyllys.Ingersoll@Sun.COM return false; 115*12720SWyllys.Ingersoll@Sun.COM } 116*12720SWyllys.Ingersoll@Sun.COM 117*12720SWyllys.Ingersoll@Sun.COM fwrite(pData, 1, iLength, fp); 118*12720SWyllys.Ingersoll@Sun.COM fclose(fp); 119*12720SWyllys.Ingersoll@Sun.COM 120*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); // BIO_free close the file and clean the BIO 121*12720SWyllys.Ingersoll@Sun.COM return true; 122*12720SWyllys.Ingersoll@Sun.COM } 123*12720SWyllys.Ingersoll@Sun.COM 124*12720SWyllys.Ingersoll@Sun.COM bool SaveX509CertToBuffer( 125*12720SWyllys.Ingersoll@Sun.COM void* const i_pImplResource, 126*12720SWyllys.Ingersoll@Sun.COM unsigned char * const i_pcBuffer, 127*12720SWyllys.Ingersoll@Sun.COM int i_iBufferLength, 128*12720SWyllys.Ingersoll@Sun.COM int * const o_pActualLength ) 129*12720SWyllys.Ingersoll@Sun.COM { 130*12720SWyllys.Ingersoll@Sun.COM FATAL_ASSERT( i_pImplResource != NULL && 131*12720SWyllys.Ingersoll@Sun.COM i_pcBuffer && 132*12720SWyllys.Ingersoll@Sun.COM o_pActualLength && 133*12720SWyllys.Ingersoll@Sun.COM i_iBufferLength > 0 ); 134*12720SWyllys.Ingersoll@Sun.COM 135*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*)i_pImplResource; 136*12720SWyllys.Ingersoll@Sun.COM 137*12720SWyllys.Ingersoll@Sun.COM BIO *pMemBio = NULL; 138*12720SWyllys.Ingersoll@Sun.COM char *pData = NULL; 139*12720SWyllys.Ingersoll@Sun.COM int iLength; 140*12720SWyllys.Ingersoll@Sun.COM 141*12720SWyllys.Ingersoll@Sun.COM // create memory BIO 142*12720SWyllys.Ingersoll@Sun.COM pMemBio = SaveCertToMemoryBIO( pX509control ); 143*12720SWyllys.Ingersoll@Sun.COM 144*12720SWyllys.Ingersoll@Sun.COM if( pMemBio == NULL ) 145*12720SWyllys.Ingersoll@Sun.COM { 146*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- no memory 147*12720SWyllys.Ingersoll@Sun.COM return false; 148*12720SWyllys.Ingersoll@Sun.COM } 149*12720SWyllys.Ingersoll@Sun.COM 150*12720SWyllys.Ingersoll@Sun.COM iLength = BIO_get_mem_data( pMemBio, &pData ); 151*12720SWyllys.Ingersoll@Sun.COM 152*12720SWyllys.Ingersoll@Sun.COM // If the output buffer is a string, it needs to be NULL terminated 153*12720SWyllys.Ingersoll@Sun.COM // So always append a NULL to the output 154*12720SWyllys.Ingersoll@Sun.COM if(iLength + 1 > i_iBufferLength) 155*12720SWyllys.Ingersoll@Sun.COM { 156*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- buffer too small 157*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); 158*12720SWyllys.Ingersoll@Sun.COM return false; 159*12720SWyllys.Ingersoll@Sun.COM } 160*12720SWyllys.Ingersoll@Sun.COM // copy the data to given buffer 161*12720SWyllys.Ingersoll@Sun.COM memcpy(i_pcBuffer, pData, iLength); 162*12720SWyllys.Ingersoll@Sun.COM // NULL terminate the string 163*12720SWyllys.Ingersoll@Sun.COM i_pcBuffer[iLength] = '\0'; 164*12720SWyllys.Ingersoll@Sun.COM *o_pActualLength = iLength; 165*12720SWyllys.Ingersoll@Sun.COM 166*12720SWyllys.Ingersoll@Sun.COM // free memory 167*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); 168*12720SWyllys.Ingersoll@Sun.COM 169*12720SWyllys.Ingersoll@Sun.COM return true; 170*12720SWyllys.Ingersoll@Sun.COM } 171*12720SWyllys.Ingersoll@Sun.COM 172*12720SWyllys.Ingersoll@Sun.COM /** 173*12720SWyllys.Ingersoll@Sun.COM * import the Cert from a BIO, if error, return NULL 174*12720SWyllys.Ingersoll@Sun.COM */ 175*12720SWyllys.Ingersoll@Sun.COM bool LoadCertFromBIO(X509control* i_pX509control, BIO *i_pBio) 176*12720SWyllys.Ingersoll@Sun.COM { 177*12720SWyllys.Ingersoll@Sun.COM X509 *pRequest = NULL; 178*12720SWyllys.Ingersoll@Sun.COM 179*12720SWyllys.Ingersoll@Sun.COM if (i_pX509control == NULL) return false; 180*12720SWyllys.Ingersoll@Sun.COM 181*12720SWyllys.Ingersoll@Sun.COM if(i_pBio == NULL) return false; 182*12720SWyllys.Ingersoll@Sun.COM 183*12720SWyllys.Ingersoll@Sun.COM //if(m_pNative != NULL) return false; // do not allow overwrite 184*12720SWyllys.Ingersoll@Sun.COM if (i_pX509control->pX509 != NULL ) return false; 185*12720SWyllys.Ingersoll@Sun.COM 186*12720SWyllys.Ingersoll@Sun.COM pRequest=PEM_read_bio_X509(i_pBio, NULL, NULL, NULL); 187*12720SWyllys.Ingersoll@Sun.COM 188*12720SWyllys.Ingersoll@Sun.COM if (pRequest == NULL) 189*12720SWyllys.Ingersoll@Sun.COM { 190*12720SWyllys.Ingersoll@Sun.COM // fixme: log: invalid certificate format 191*12720SWyllys.Ingersoll@Sun.COM return false; 192*12720SWyllys.Ingersoll@Sun.COM } 193*12720SWyllys.Ingersoll@Sun.COM //m_pNative = pRequest; 194*12720SWyllys.Ingersoll@Sun.COM i_pX509control->pX509 = pRequest; 195*12720SWyllys.Ingersoll@Sun.COM 196*12720SWyllys.Ingersoll@Sun.COM return true; 197*12720SWyllys.Ingersoll@Sun.COM } 198*12720SWyllys.Ingersoll@Sun.COM 199*12720SWyllys.Ingersoll@Sun.COM bool LoadX509CertFromFile( 200*12720SWyllys.Ingersoll@Sun.COM void* const i_pImplResource, 201*12720SWyllys.Ingersoll@Sun.COM const char * const i_pcFileName ) 202*12720SWyllys.Ingersoll@Sun.COM 203*12720SWyllys.Ingersoll@Sun.COM { 204*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*) i_pImplResource; 205*12720SWyllys.Ingersoll@Sun.COM if (pX509control == NULL) 206*12720SWyllys.Ingersoll@Sun.COM { 207*12720SWyllys.Ingersoll@Sun.COM return false; 208*12720SWyllys.Ingersoll@Sun.COM } 209*12720SWyllys.Ingersoll@Sun.COM 210*12720SWyllys.Ingersoll@Sun.COM BIO *pFileBio=NULL; 211*12720SWyllys.Ingersoll@Sun.COM bool bReturn; 212*12720SWyllys.Ingersoll@Sun.COM 213*12720SWyllys.Ingersoll@Sun.COM pFileBio=BIO_new(BIO_s_file()); 214*12720SWyllys.Ingersoll@Sun.COM if (pFileBio == NULL) 215*12720SWyllys.Ingersoll@Sun.COM { 216*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- no memory 217*12720SWyllys.Ingersoll@Sun.COM return false; 218*12720SWyllys.Ingersoll@Sun.COM } 219*12720SWyllys.Ingersoll@Sun.COM 220*12720SWyllys.Ingersoll@Sun.COM if (!BIO_read_filename(pFileBio,i_pcFileName)) 221*12720SWyllys.Ingersoll@Sun.COM { 222*12720SWyllys.Ingersoll@Sun.COM //fixme log -- could not open file 223*12720SWyllys.Ingersoll@Sun.COM BIO_free(pFileBio); 224*12720SWyllys.Ingersoll@Sun.COM return false; 225*12720SWyllys.Ingersoll@Sun.COM } 226*12720SWyllys.Ingersoll@Sun.COM 227*12720SWyllys.Ingersoll@Sun.COM bReturn = LoadCertFromBIO(pX509control, pFileBio); 228*12720SWyllys.Ingersoll@Sun.COM 229*12720SWyllys.Ingersoll@Sun.COM BIO_free(pFileBio); 230*12720SWyllys.Ingersoll@Sun.COM 231*12720SWyllys.Ingersoll@Sun.COM return bReturn; 232*12720SWyllys.Ingersoll@Sun.COM } 233*12720SWyllys.Ingersoll@Sun.COM 234*12720SWyllys.Ingersoll@Sun.COM 235*12720SWyllys.Ingersoll@Sun.COM bool LoadX509CertFromBuffer( 236*12720SWyllys.Ingersoll@Sun.COM void* const i_pImplResource, 237*12720SWyllys.Ingersoll@Sun.COM void* const i_pX509Cert, 238*12720SWyllys.Ingersoll@Sun.COM int i_iLength) 239*12720SWyllys.Ingersoll@Sun.COM { 240*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*)i_pImplResource; 241*12720SWyllys.Ingersoll@Sun.COM 242*12720SWyllys.Ingersoll@Sun.COM if(pX509control == NULL) 243*12720SWyllys.Ingersoll@Sun.COM { 244*12720SWyllys.Ingersoll@Sun.COM return false; 245*12720SWyllys.Ingersoll@Sun.COM } 246*12720SWyllys.Ingersoll@Sun.COM 247*12720SWyllys.Ingersoll@Sun.COM BIO *pMemBio; 248*12720SWyllys.Ingersoll@Sun.COM bool bReturn; 249*12720SWyllys.Ingersoll@Sun.COM // create a mem bio from the given buffer 250*12720SWyllys.Ingersoll@Sun.COM // Note that BIO_new_mem_buf() creates a BIO which never destroy the memory 251*12720SWyllys.Ingersoll@Sun.COM // attached to it. 252*12720SWyllys.Ingersoll@Sun.COM pMemBio = BIO_new_mem_buf(i_pX509Cert, i_iLength); 253*12720SWyllys.Ingersoll@Sun.COM if (pMemBio == NULL) 254*12720SWyllys.Ingersoll@Sun.COM { 255*12720SWyllys.Ingersoll@Sun.COM //fixme: log -- no memory 256*12720SWyllys.Ingersoll@Sun.COM return false; 257*12720SWyllys.Ingersoll@Sun.COM } 258*12720SWyllys.Ingersoll@Sun.COM bReturn = LoadCertFromBIO(pX509control, pMemBio); 259*12720SWyllys.Ingersoll@Sun.COM 260*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); 261*12720SWyllys.Ingersoll@Sun.COM 262*12720SWyllys.Ingersoll@Sun.COM return bReturn; 263*12720SWyllys.Ingersoll@Sun.COM } 264*12720SWyllys.Ingersoll@Sun.COM 265*12720SWyllys.Ingersoll@Sun.COM void FinalizeCertImpl( void* i_pImplResource ) 266*12720SWyllys.Ingersoll@Sun.COM { 267*12720SWyllys.Ingersoll@Sun.COM if ( i_pImplResource != NULL ) 268*12720SWyllys.Ingersoll@Sun.COM { 269*12720SWyllys.Ingersoll@Sun.COM free(i_pImplResource); 270*12720SWyllys.Ingersoll@Sun.COM } 271*12720SWyllys.Ingersoll@Sun.COM } 272*12720SWyllys.Ingersoll@Sun.COM 273*12720SWyllys.Ingersoll@Sun.COM bool PrintX509Cert( void* const i_pImplResource ) 274*12720SWyllys.Ingersoll@Sun.COM { 275*12720SWyllys.Ingersoll@Sun.COM BIO *pMemBio; 276*12720SWyllys.Ingersoll@Sun.COM char *pData; 277*12720SWyllys.Ingersoll@Sun.COM int iLength,i; 278*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*)i_pImplResource; 279*12720SWyllys.Ingersoll@Sun.COM pMemBio = BIO_new(BIO_s_mem()); 280*12720SWyllys.Ingersoll@Sun.COM if(pMemBio == NULL) 281*12720SWyllys.Ingersoll@Sun.COM { 282*12720SWyllys.Ingersoll@Sun.COM return false; 283*12720SWyllys.Ingersoll@Sun.COM } 284*12720SWyllys.Ingersoll@Sun.COM 285*12720SWyllys.Ingersoll@Sun.COM //X509_print(pMemBio,m_pNative); 286*12720SWyllys.Ingersoll@Sun.COM X509_print(pMemBio, pX509control->pX509); 287*12720SWyllys.Ingersoll@Sun.COM 288*12720SWyllys.Ingersoll@Sun.COM iLength = BIO_get_mem_data(pMemBio, &pData); 289*12720SWyllys.Ingersoll@Sun.COM 290*12720SWyllys.Ingersoll@Sun.COM for(i = 0; i < iLength; i++) 291*12720SWyllys.Ingersoll@Sun.COM { 292*12720SWyllys.Ingersoll@Sun.COM printf("%c", pData[i]); 293*12720SWyllys.Ingersoll@Sun.COM } 294*12720SWyllys.Ingersoll@Sun.COM 295*12720SWyllys.Ingersoll@Sun.COM BIO_free(pMemBio); 296*12720SWyllys.Ingersoll@Sun.COM 297*12720SWyllys.Ingersoll@Sun.COM return true; 298*12720SWyllys.Ingersoll@Sun.COM 299*12720SWyllys.Ingersoll@Sun.COM } 300*12720SWyllys.Ingersoll@Sun.COM #ifdef K_SOLARIS_PLATFORM 301*12720SWyllys.Ingersoll@Sun.COM void *GetCert(void* i_pImplResource ) 302*12720SWyllys.Ingersoll@Sun.COM { 303*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*)i_pImplResource; 304*12720SWyllys.Ingersoll@Sun.COM return ((void *)pX509control->pX509); 305*12720SWyllys.Ingersoll@Sun.COM } 306*12720SWyllys.Ingersoll@Sun.COM 307*12720SWyllys.Ingersoll@Sun.COM void SetCert(void* i_pImplResource, void *cert) 308*12720SWyllys.Ingersoll@Sun.COM { 309*12720SWyllys.Ingersoll@Sun.COM X509control* pX509control = (X509control*)i_pImplResource; 310*12720SWyllys.Ingersoll@Sun.COM pX509control->pX509 = (X509 *)cert; 311*12720SWyllys.Ingersoll@Sun.COM return; 312*12720SWyllys.Ingersoll@Sun.COM } 313*12720SWyllys.Ingersoll@Sun.COM #endif 314