/************************************************************************* > File Name: cert_session.c > Author: > Mail: > Created Time: Fri 01 Jun 2018 02:00:56 AM PDT ************************************************************************/ #define _GNU_SOURCE #include #include #include #include #include #include /* openssl**/ #include #include #include #include #include #include #include #include #include #include "rt_string.h" #include "rt_common.h" #include "rt_stdlib.h" #include "rt_file.h" #include "rt_time.h" #include "rt_tmr.h" #include "bufferevent.h" #include "listener.h" #include "event_compat.h" #include "http.h" #include "buffer.h" #include "util-internal.h" enum x509_input_file{ INPUT_FILE_CERT, INPUT_FILE_KEY, INPUT_FILE_CRL, INPUT_FILE_LIST, INPUT_FILE_CHECK, INPUT_FILE_HOST, INPUT_FILE_ALGO, INPUT_FILE_CHAIN, }; #define LOCAL_USER_PEN 1 #define LOCAL_USER_DER 2 #define LOCAL_USER_P12 3 BIO *bio_err = NULL; static const struct value_string format_vals[] = { {LOCAL_USER_PEN, "PEM TEXT FILE"}, {LOCAL_USER_DER, "DER BINARY FILE"}, {LOCAL_USER_P12, "P12 BINARY FILE"}, }; static void help() { printf("Welcome to x509 %s\n", "1.1.1"); printf("x509 <-incert |-inkey | -incrl | -inlist> arg\n" "Usage:\n" " -incert | input certificate file\n" " -inkey | input private key file\n" " -incrl | input certificate revocation list\n" " -inlist | input certificate list file,format = pem\n" " -incheck | input certificate file and intpu key file\n" " -inhost | input san file and intpu fqdn file\n" " -inalgo | input certificate file and public key algorithm\n"); } static X509* base_load_pkcs12(BIO *in, EVP_PKEY **pkey, X509 **x, STACK_OF(X509) **ca) { PKCS12 *p12; const char *pass = ""; X509 *_x = NULL; EVP_PKEY *_pkey; STACK_OF(X509) *_ca = NULL; OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); p12 = d2i_PKCS12_bio(in, NULL); if (p12 == NULL) { goto finish; } if (!PKCS12_parse(p12, pass, &_pkey, &_x, &_ca)) { goto finish; } if (x) *x = _x; if (pkey) *pkey = _pkey; if (ca) *ca = _ca; finish: if (p12) PKCS12_free(p12); return _x; } static void cert_base_load_stack_info(BIO * in_bio, STACK_OF(X509) **stack_ca) { int x509_cnt = 0; X509_INFO *x509_info; STACK_OF(X509) *stack_x509 = NULL; STACK_OF(X509_INFO) *stack_x509_info = NULL; if ((stack_x509 = sk_X509_new_null()) == NULL) { X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); goto finish; } stack_x509_info = PEM_X509_INFO_read_bio(in_bio, NULL, NULL, NULL); if (stack_x509_info == NULL) { X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB); goto finish; } while (sk_X509_INFO_num(stack_x509_info)) { x509_info = sk_X509_INFO_shift(stack_x509_info); if (x509_info->x509 != NULL) { sk_X509_push(stack_x509, x509_info->x509); x509_info->x509 = NULL; x509_cnt++; } X509_INFO_free(x509_info); } if (x509_cnt >= 1) *stack_ca = stack_x509; finish: if (stack_x509_info != NULL) sk_X509_INFO_free(stack_x509_info); return; } int x509_get_last_ca(const char *file, X509 *cx509) { int last = 0; X509 *x = NULL; BIO *bio = NULL; if ((bio = BIO_new(BIO_s_file())) == NULL) { goto finish; } if (BIO_read_filename(bio, file) <= 0) { goto finish; } while(NULL!=(x=PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL))) { if (0 == X509_NAME_cmp(X509_get_issuer_name(x), X509_get_subject_name(cx509))) { last = 1; X509_free(x); break; }; X509_free(x); } BIO_free (bio); finish: return last; } X509* x509_get_root_ca(const char *file, STACK_OF(X509) **stack_ca) { int x509_cnt = 0; X509 *x = NULL, *node = NULL, *root = NULL; BIO *bio = NULL; STACK_OF(X509) *stack_x509 = NULL; if ((bio = BIO_new(BIO_s_file())) == NULL) { goto finish; } if (BIO_read_filename(bio, file) <= 0) { goto finish; } if ((stack_x509 = sk_X509_new_null()) == NULL) { X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); goto finish; } while(NULL!=(x=PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL))) { if (0 == X509_NAME_cmp(X509_get_issuer_name(x), X509_get_subject_name(x))) { /*This is root ca**/ root = x; continue; }; /*This is last ca*/ if (x509_get_last_ca(file, x) == 0) { node = x; continue; } sk_X509_push(stack_x509, x); x509_cnt++; X509_free(x); } if (x509_cnt >= 1) *stack_ca = stack_x509; if (node != NULL) X509_free(root); else node = root; BIO_free (bio); finish: return node; } static X509 * cert_base_load_x509 (BIO * in_bio, STACK_OF(X509) **stack_ca, int iFormat) { X509 *x = NULL; switch (iFormat) { case LOCAL_USER_DER: x = d2i_X509_bio (in_bio, NULL); break; case LOCAL_USER_PEN: x = PEM_read_bio_X509 (in_bio, NULL, NULL, NULL); cert_base_load_stack_info(in_bio, stack_ca); break; case LOCAL_USER_P12: x = base_load_pkcs12(in_bio, NULL, &x, stack_ca); break; default: break; } return x; } static X509 * cert_load_x509(char *file, int *informat, STACK_OF(X509) **stack_ca) { BIO *in = NULL; X509 *x509 = NULL; if(!file){ goto finish; } if ((in = BIO_new(BIO_s_file())) == NULL) { goto finish; } if (BIO_read_filename(in, file) <= 0) { goto finish; } /**try pem */ if ((x509 = cert_base_load_x509(in, stack_ca, LOCAL_USER_PEN)) != NULL){ *informat = LOCAL_USER_PEN; goto end; } #if 0 (void)BIO_reset (in); if ((x509 = cert_base_load_x509(in, stack_ca, LOCAL_USER_P12)) != NULL){ *informat = LOCAL_USER_P12; goto end; } (void)BIO_reset (in); if ((x509 = cert_base_load_x509(in, stack_ca, LOCAL_USER_DER)) != NULL){ *informat = LOCAL_USER_DER; goto end; } #endif end: BIO_free (in); in = NULL; finish: return x509; } EVP_PKEY * cert_base_key_x509 (BIO * bio, int iFormat, char *strPwd) { EVP_PKEY *pkey = NULL; switch (iFormat){ case LOCAL_USER_PEN: pkey = PEM_read_bio_PrivateKey (bio, NULL, NULL, strPwd); break; case LOCAL_USER_P12: base_load_pkcs12(bio, &pkey, NULL, NULL); break; default: break; } return pkey; } EVP_PKEY * cert_load_key(char *keyfile, int *informat) { EVP_PKEY *pkey = NULL; BIO *in = NULL; if(!keyfile){ goto finish; } if ((in = BIO_new(BIO_s_file())) == NULL) { goto finish; } if (BIO_read_filename(in, keyfile) <= 0) { goto finish; } if ((pkey = cert_base_key_x509 (in, LOCAL_USER_PEN, "")) != NULL){ *informat = LOCAL_USER_PEN; goto finish; } (void)BIO_reset (in); if ((pkey = cert_base_key_x509 (in, LOCAL_USER_P12, "")) != NULL){ *informat = LOCAL_USER_P12; goto finish; } finish: if (in != NULL) BIO_free (in); return pkey; } int x509_get_ValidDate(X509 *x509) { BIO *STDout = NULL; STDout = BIO_new_fp(stdout, BIO_NOCLOSE); printf("CA notBefore : "); ASN1_TIME_print(STDout, X509_get_notBefore(x509)); printf("\n"); printf("CA notAfter : "); ASN1_TIME_print(STDout, X509_get_notAfter(x509)); printf("\n"); BIO_free_all(STDout); return 0; } static char* x509_get_alt_name(X509 *x509) { int i, size = 0, gtype = 0; char *gnname = NULL; GENERAL_NAMES* subjectAltNames = (GENERAL_NAMES*)X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL); int cnt = sk_GENERAL_NAME_num(subjectAltNames); if (cnt < 0) goto finish; int gnnamelen = cnt * 64; gnname = (char *)malloc(gnnamelen); if (!gnname) goto finish; memset(gnname, 0, gnnamelen); for (i = 0; i < cnt; i++) { GENERAL_NAME* generalName = sk_GENERAL_NAME_value(subjectAltNames, i); ASN1_STRING *uri = GENERAL_NAME_get0_value(generalName, >ype); if (gtype == GEN_DNS) { size += snprintf(gnname + size, gnnamelen, "%s;", ASN1_STRING_data(uri)); if (size < 0) continue; } } finish: return gnname; } void x509_get_name(X509_NAME *name, int obase) { BIO *out = NULL; out = BIO_new(BIO_s_file()); if (out == NULL) { ERR_print_errors(bio_err); goto finish; } BIO_set_fp(out, stdout, BIO_NOCLOSE); X509_NAME_print(out, name, obase); finish: if (out != NULL) BIO_free_all(out); return; } char *x509_get_cn(X509 *x509) { char *CName = NULL; int iLen = 0, CNlen = 256; X509_NAME *pSubName = NULL; pSubName = X509_get_subject_name(x509); if (!pSubName){ goto finish; } CName = kmalloc(CNlen, MPF_CLR, -1); if (!CName){ goto finish; } iLen = X509_NAME_get_text_by_NID(pSubName, NID_commonName, CName, CNlen-1); if (iLen > 0){ return CName; } finish: return NULL; } char *x509_get_SubjectName(X509 *x509) { int iLen = 0; char *csSubName = NULL; char csBuf[256] = {0}; X509_NAME *pSubName = NULL; csSubName = (char *)malloc(1024); if (!csSubName) goto finish; pSubName = X509_get_subject_name(x509); if (!pSubName){ goto finish; } memset(csBuf, 0, 256); memset(csSubName, 0, 1024); iLen = X509_NAME_get_text_by_NID(pSubName, NID_countryName, csBuf, 255); if (iLen > 0){ strcat(csSubName, "C="); strcat(csSubName, csBuf); strcat(csSubName, ", "); } memset(csBuf, 0, 256); iLen = X509_NAME_get_text_by_NID(pSubName, NID_organizationName, csBuf, 255); if (iLen > 0){ strcat(csSubName, "O="); strcat(csSubName, csBuf); strcat(csSubName, ", "); } memset(csBuf, 0, 256); iLen = X509_NAME_get_text_by_NID(pSubName, NID_organizationalUnitName, csBuf, 255); if (iLen > 0) { strcat(csSubName, "OU="); strcat(csSubName, csBuf); strcat(csSubName, ", "); } memset(csBuf, 0, 256); iLen = X509_NAME_get_text_by_NID(pSubName, NID_commonName, csBuf, 255); if (iLen > 0){ strcat(csSubName, "CN="); strcat(csSubName, csBuf); } finish: return csSubName; } #define R_RSA_ALGO_1024 1024 #define R_RSA_ALGO_2048 2048 #define R_RSA_ALGO_4096 4096 typedef struct { const char *name; /* NIST Name of curve */ int nid; /* Curve NID */ } x509_algo_name; static x509_algo_name algo_name[] = { {"rsa1024", R_RSA_ALGO_1024}, {"rsa2048", R_RSA_ALGO_2048}, {"rsa4096", R_RSA_ALGO_4096}, {"secp256r1", NID_X9_62_prime256v1}, {"secp384r1",NID_secp384r1} }; static size_t x509_algo_str2idx(const char *public_algo) { size_t i = 0; if(public_algo == NULL) { goto finish; } for (i = 0; i < sizeof(algo_name) / sizeof(x509_algo_name); i++) { if (0 == strcasecmp(public_algo, algo_name[i].name)) { return algo_name[i].nid; } } finish: return R_RSA_ALGO_2048; } int x509_check_pubKeytype(X509 *x509, const char *algo) { int xret = 1, nid = 0; EVP_PKEY *pkey = NULL; pkey = X509_get_pubkey(x509); if (pkey == NULL) { printf("Unable to load Public Key\n"); } switch(pkey->type) { case EVP_PKEY_RSA: xret = 1; break; case EVP_PKEY_EC: nid = x509_algo_str2idx(algo); switch(nid) { case R_RSA_ALGO_1024: case R_RSA_ALGO_2048: case R_RSA_ALGO_4096: xret = 0; break; case NID_X9_62_prime256v1: case NID_secp384r1: xret = 1; break; default: xret = 0; break; } break; default: xret = 0; break; } return xret; } void x509_get_pubKeytype(X509 *x509) { EVP_PKEY *pkey = NULL; pkey = X509_get_pubkey(x509); if (pkey == NULL) { printf("Unable to load Public Key\n"); } const char *type = OBJ_nid2ln(pkey->type); printf("PKey Algorithm : %s\n", type); if (pkey->type == EVP_PKEY_EC) { EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); int nid; const char *cname, *asnl; nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); cname = EC_curve_nid2nist(nid); asnl = OBJ_nid2sn(nid); printf("ASN1 OID : %s\n", cname); printf("NIST CURVE : %s\n", asnl); } } char* x509_get_ExtBasicConstraints(X509 *x509) { int crit = 0; char value[512] = {0}; BASIC_CONSTRAINTS *bcons = NULL; if (!x509) return NULL; bcons = (BASIC_CONSTRAINTS*)X509_get_ext_d2i(x509, NID_basic_constraints, &crit, NULL); if (!bcons) return NULL; if (!bcons->ca) { strcat(value, "Subject Type=End Entity; "); strcat(value, "Path Length Constraint=None"); } else { char temp[128] = {0}; if (bcons->pathlen != NULL) snprintf(temp, 128, "Path Length Constraint=%d", bcons->pathlen->type); else snprintf(temp, 128, "Path Length Constraint=None"); strcat(value, "Subject Type=CA; "); strcat(value, temp); } BASIC_CONSTRAINTS_free(bcons); char *base_cons = NULL; int base_cons_len = strlen(value) + 1; base_cons = (char *)malloc(base_cons_len); if (!base_cons) return NULL; strncpy(base_cons, value, base_cons_len); return base_cons; } static char* x509_get_fingerprint(X509 *x509) { char *rkey = NULL; unsigned int len = 0, i = 0; char hex[EVP_MAX_MD_SIZE] = {0}; unsigned char fdig[EVP_MAX_MD_SIZE] = {0}; rkey = (char *)malloc(512); if (!rkey) goto finish; X509_digest(x509, EVP_sha1(), fdig, &len); for (i = 0; i < len ; ++i){ sprintf(hex + i * sizeof(unsigned char) * 2, "%02x", fdig[i]); } snprintf(rkey, 511, "%s", hex); finish: return rkey; } char *x509_get_sn(X509 *x509) { ASN1_INTEGER *asn1_i = NULL; BIGNUM *bignum = NULL; char *serial = NULL; asn1_i = X509_get_serialNumber(x509); bignum = ASN1_INTEGER_to_BN(asn1_i, NULL); if (bignum == NULL) { goto finish; } serial = BN_bn2hex(bignum); if (serial == NULL) { goto finish; } BN_free(bignum); finish: return serial; } char *x509_get_version(X509 *x509) { unsigned int v = 0; v = X509_get_version(x509); switch(v){ case 0: return "V1"; case 1: return "V2"; case 2: return "V3"; default: break; } return NULL; } int X509_check_valid_date(X509 *x509) { int day, sec; /*Certificate is issued earlier than the current time*/ if (ASN1_TIME_diff(&day, &sec, X509_get_notBefore(x509), NULL) == 0) return -1; if (day <= 0 && sec <= 0) return -1; /*Certificate expiration is less than the current time*/ if (ASN1_TIME_diff(&day, &sec, NULL, X509_get_notAfter(x509)) == 0) return -1; if (day <= 0 && sec <= 0) return -1; /*Certificate expires less than the date of issue*/ if (ASN1_TIME_diff(&day, &sec, X509_get_notBefore(x509), X509_get_notAfter(x509)) == 0) return -1; if (day <= 0 && sec <= 0) return -1; return 0; } int x509_parse_cert(char *certfile, char *host) { int xret = -1; int informat = 0; X509 *x509 = NULL; STACK_OF(X509) *stack_ca = NULL; x509 = cert_load_x509(certfile, &informat, &stack_ca); if (!x509){ printf("unable to load certificate\n"); goto finish; } printf("Successful certificate conversion\n"); printf("Ca Format : %s\n", val_to_str(informat, format_vals)); printf("Ca Constraints : %s\n", (x509_get_ExtBasicConstraints(x509) != NULL)?x509_get_ExtBasicConstraints(x509): "NULL"); if (informat == LOCAL_USER_P12 || informat == LOCAL_USER_PEN){ if (stack_ca){ printf("Chain Length : %d\n", sk_X509_num(stack_ca) + 1); }else{ printf("Chain Length : %d\n", 1); } } printf("Ca Version : %s\n", (x509_get_version(x509) != NULL)?x509_get_version(x509) : "NULL"); printf("Ca Serial : %s\n", (x509_get_sn(x509) != NULL)?x509_get_sn(x509) : "NULL"); printf("Ca Issuer : "); x509_get_name(X509_get_issuer_name(x509), 16); printf("\n"); printf("Ca SubjectName : "); x509_get_name(X509_get_subject_name(x509), 16); printf("\n"); char *alt_name = x509_get_alt_name(x509); printf("Ca AltName : %s\n", (alt_name != NULL)?alt_name:"NULL"); free(alt_name); printf("Ca Fingerprint : %s\n", x509_get_fingerprint(x509)); x509_get_ValidDate(x509); printf("Ca valid date : %s\n", (X509_check_valid_date(x509) == 0)?"valid":"expire"); x509_get_pubKeytype(x509); /* self testing***/ if (host != NULL) { if (X509_check_host(x509, host, strlen(host), 0, NULL) == 1) printf("Match host name: %s\n", "Successful matching"); else printf("Match host name: %s\n", "Matching failure"); } xret = 0; finish: return xret; } int x509_parse_key(char *keyfile) { int xret = -1; int informat = 0; EVP_PKEY *pkey = NULL; pkey = cert_load_key(keyfile, &informat); if (!pkey){ printf("unable to load private key\n"); goto finish; } printf("Successful private key conversion\n"); printf("Key Format : %s\n", val_to_str(informat, format_vals)); xret = 0; finish: return xret; } static X509_CRL * x509_load_crl(char *crlfile, int *informat) { BIO *in = NULL; X509_CRL *x = NULL; in = BIO_new(BIO_s_file()); if (in == NULL) { ERR_print_errors(bio_err); goto free; } if (BIO_read_filename(in, crlfile) <= 0) { perror(crlfile); goto finish; } if ((x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL)) != NULL){ *informat = LOCAL_USER_PEN; goto finish; } if ((x = d2i_X509_CRL_bio(in, NULL)) != NULL){ *informat = LOCAL_USER_DER; goto finish; } free: BIO_free(in); finish: return x; } char *x509_get_crlNumber(X509_CRL *crl) { ASN1_INTEGER *crlnum; BIGNUM *bignum = NULL; char *crl_number = NULL; crlnum = X509_CRL_get_ext_d2i(crl, NID_crl_number, NULL, NULL); bignum = ASN1_INTEGER_to_BN(crlnum, NULL); if (bignum == NULL) { goto finish; } crl_number = BN_bn2dec(bignum); if (crl_number == NULL) { goto finish; } BN_free(bignum); finish: return crl_number; } int x509_parse_crl(char *crlfile) { X509_CRL *crl = NULL; int informat = 0; int xret = -1; crl = x509_load_crl(crlfile, &informat); if (!crl){ printf("unable to load Certificate Revocation List\n"); goto finish; } printf("Successful certificate revocation list conversion\n"); long l = 0; l = X509_CRL_get_version(crl); printf("CRL Format : %s\n", val_to_str(informat, format_vals)); printf("CRL Version : %lu\n", l + 1); printf("CRL Issuer : "); x509_get_name(X509_CRL_get_issuer(crl), 16); printf("\n"); printf("CRL Number : %s\n", x509_get_crlNumber(crl)); finish: return xret; } static int x509_parse_cert_list(char *certlist) { int xret = 0; BIO *in = NULL; X509* x = NULL; int count = 0; in = BIO_new(BIO_s_file()); if (in == NULL) { ERR_print_errors(bio_err); goto finish; } if (BIO_read_filename(in, certlist) <= 0) { perror(certlist); goto err; } printf("Certificate List:\n"); for (;;) { x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL); if (x == NULL) { if (count == 0) printf("bad input format specified for input cert list\n"); goto err; } printf("Cert list Issuer : "); x509_get_name(X509_get_issuer_name(x), 16); printf("\n"); count++; X509_free(x); x = NULL; } err: if (x != NULL) X509_free(x); if (in != NULL) BIO_free(in); printf("Certificate List Number : %d\n", count); finish: return xret; } static int decoder_argv_parser(int argc, char **argv, char **infile, char **infile2) { int i = 0; int iformat = -1; for (i = 0; argv[i] != NULL; i++){ /** run version parser */ if (STRCMP (argv[i], "-inkey") == 0){ if (--argc < 1) goto help; *infile = argv[i+1]; iformat = INPUT_FILE_KEY; break; } if (STRCMP(argv[i], "-incert") == 0){ if (--argc < 1) goto help; *infile = argv[i+1]; *infile2 = argv[i+2]; iformat = INPUT_FILE_CERT; break; } if (STRCMP(argv[i], "-incrl") == 0){ if (--argc < 1) goto help; *infile = argv[i+1]; iformat = INPUT_FILE_CRL; break; } if (STRCMP(argv[i], "-inlist")== 0){ if (--argc < 1) goto help; *infile = argv[i+1]; iformat = INPUT_FILE_LIST; break; } if (STRCMP(argv[i], "-incheck") == 0){ if (--argc < 1) goto help; *infile = argv[i+1]; *infile2 = argv[i+2]; iformat = INPUT_FILE_CHECK; break; } if (STRCMP(argv[i], "-inhost") == 0){ if (--argc < 1) goto help; *infile = argv[i+1]; *infile2 = argv[i+2]; iformat = INPUT_FILE_HOST; break; } if (STRCMP(argv[i], "-inalgo") == 0){ if (--argc < 1) goto help; *infile = argv[i+1]; *infile2 = argv[i+2]; iformat = INPUT_FILE_ALGO; break; } } goto finish; help: help(); finish: return iformat; } static int x509_parse_check(char *cafile, char *keyfile) { int informat = 0; EVP_PKEY *pkey = NULL; pkey = cert_load_key(keyfile, &informat); if (!pkey){ printf("unable to load private key\n"); goto finish; } X509 *x509 = NULL; STACK_OF(X509) *stack_ca = NULL; x509 = x509_get_root_ca(cafile, &stack_ca); if (!x509){ printf("unable to load certificate\n"); goto finish; } if (!X509_check_private_key(x509, pkey)) { printf("Matching failure\n"); }else{ printf("Successful matching\n"); } finish: return 0; } static X509 *make_cert() { X509 *x509 = X509_new(); if (x509 == NULL) goto out; if (!X509_set_version(x509, 3)) goto out; return x509; out: return NULL; } char *execute_read_file(const char *filename) { FILE *file = NULL; long length = 0; char *content = NULL; size_t read_chars = 0; file = fopen(filename, "rb"); if (file == NULL) { goto cleanup; } if (fseek(file, 0, SEEK_END) != 0) { goto cleanup; } length = ftell(file); if (length < 0) { goto cleanup; } if (fseek(file, 0, SEEK_SET) != 0) { goto cleanup; } /* allocate content buffer */ content = (char*)malloc((size_t)length + sizeof("")); if (content == NULL) { goto cleanup; } /* read the file into memory */ read_chars = fread(content, sizeof(char), (size_t)length, file); if ((long)read_chars != length) { free(content); content = NULL; goto cleanup; } content[read_chars] = '\0'; cleanup: if (file != NULL) { fclose(file); } return content; } char *str_trim(const char *str) { unsigned int uLen = strlen(str); if(0 == uLen){ return '\0'; } char *strRet = (char *)malloc(uLen + 1); memset(strRet, 0, uLen+1); unsigned int i = 0, j = 0; for(i=0; i