source: libabac/abac_openssl.c @ c75b2c2

abac0-leakabac0-meimei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since c75b2c2 was 461541a, checked in by Mei <mei@…>, 11 years ago

1) updated original rt0 to remove libstrongswan dependency

a) identity credential being made/accessed with openssl api calls

(X509/EVP_PKEY pem)

b) attribute credential being made/access via xmlsec1 (custom XML

structure)

2) refactored libcreddy into libabac and now one ABAC namespace for

libabac

3) added attribute_rule suboption to creddy's attribute as another way

to insert access rule

4) added some regression tests into example directory
5) updated some docs.

  • Property mode set to 100644
File size: 17.8 KB
Line 
1
2/* abac_openssl.c */
3
4#define _GNU_SOURCE
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include <ctype.h>
9#include <unistd.h>
10
11#include <fcntl.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <sys/mman.h>
15#include <time.h>
16
17#include <stdbool.h>
18
19#include <openssl/conf.h>
20#include <openssl/x509.h>
21#include <openssl/x509v3.h>
22#include <openssl/x509_vfy.h>
23
24#include <openssl/rsa.h>
25#include <openssl/evp.h>
26#include <openssl/err.h>
27#include <openssl/pem.h>
28#include <openssl/ssl.h>
29#include <openssl/rand.h>
30
31
32#ifdef HAVE_READPASSPHRASE
33# include <readpassphrase.h>
34#else
35# include "compat/readpassphrase.h"
36#endif
37
38#define SHA1_LENGTH 40
39
40static int debug=0;
41
42int _potato_cb(char *buf, int sz, int rwflag, void *u);
43
44/***********************************************************************/
45int init_openssl() {
46    ERR_load_crypto_strings();
47    OpenSSL_add_all_algorithms();
48    return 0;
49}
50
51int deinit_openssl() {
52    CRYPTO_cleanup_all_ex_data();
53    return 0;
54}
55
56/* int RAND_bytes(unsigned char *buf, int num); */
57unsigned char *abac_generate_serial() {
58    unsigned char *serial=(unsigned char *) malloc(sizeof(unsigned char)*8);
59
60    memset(serial, '\0', 8);
61
62    if(!RAND_bytes(serial,8)) {
63        fprintf(stderr,"RAT, RAN^D out of seeds!!!\n");
64        assert(0);
65    }
66    // zap leading 0's
67    while (serial[0] == 0)
68        RAND_bytes(&serial[0],1);
69
70    RAND_cleanup();
71    return serial;
72}
73
74
75static BIGNUM *_make_bn_from_string(unsigned char *str)
76{
77    assert(str);
78    BIGNUM *tmp;
79    tmp=BN_bin2bn(str,8,NULL);
80/* BN_print_fp(stderr,tmp); */
81    int n=BN_num_bytes(tmp);
82    if(n) return tmp;
83        return NULL;
84}
85
86unsigned char *_encode_m64(unsigned char *orig_ptr, int orig_len)
87{
88    BIO *mbio,*b64bio,*bio;
89
90    unsigned char *m64_ptr=NULL;
91    int m64_len=0;
92
93    unsigned char *ptr=NULL;
94
95    if(orig_len==0) return NULL;
96
97    /*bio pointing at b64->mem, the base64 bio encodes on
98      write and decodes on read */
99    mbio=BIO_new(BIO_s_mem());
100    b64bio=BIO_new(BIO_f_base64());
101    bio=BIO_push(b64bio,mbio);
102
103    BIO_write(bio,orig_ptr,orig_len);
104
105    /* We need to 'flush' things to push out the encoding of the
106    * last few bytes.  There is special encoding if it is not a
107    * multiple of 3
108    */
109    BIO_flush(bio);
110
111    /* pointer to the data and the number of elements. */
112    m64_len=(int)BIO_ctrl(mbio,BIO_CTRL_INFO,0,ptr);
113
114    if(m64_len!=0) {
115       m64_ptr=malloc(m64_len+1);
116       if(m64_ptr) {
117           strcpy((char *)m64_ptr, (char *)ptr);
118           } else { 
119               fprintf(stderr,"ERROR: malloc failed\n");
120       }
121    }
122
123    /* This call will walk the chain freeing all the BIOs */
124    BIO_free_all(bio);
125    return m64_ptr;
126}
127
128unsigned char *_decode_m64(unsigned char *m64_ptr, int m64_len)
129{
130    unsigned char *orig_ptr=NULL;
131    int orig_len=0;
132
133    BIO *b64bio, *mbio, *bio;
134    char *ptr=NULL;
135
136    if(m64_len==0) return NULL;
137
138    ptr = (char *)malloc(sizeof(char)*m64_len);
139    memset(ptr, '\0', m64_len);
140
141    b64bio = BIO_new(BIO_f_base64());
142    mbio = BIO_new_mem_buf(m64_ptr, m64_len);
143    bio = BIO_push(b64bio, mbio);
144
145    orig_len=BIO_read(bio, ptr, m64_len);
146   
147    if(orig_len) {
148        orig_ptr=malloc(orig_len+1);
149        if(orig_ptr)
150            strcpy((char *)orig_ptr, ptr);
151            else fprintf(stderr,"ERROR: malloc failed..\n");
152    }
153
154    BIO_free_all(bio);
155    return orig_ptr;
156}
157
158static char *_read_blob_from_file(char *fname, int *len)
159{
160    struct stat sb;
161    char *dptr=NULL;
162
163    int fd = open(fname, O_RDONLY);
164    if (fd == -1) { return NULL; }
165    if(stat(fname, &sb) == -1) {
166        close(fd);
167        return NULL;
168    }
169    dptr= (char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
170    close(fd);
171
172    if(dptr == MAP_FAILED) {
173        return NULL;
174    }
175    *len=sb.st_size;
176    return dptr;
177}
178
179/* Read ID in PEM */
180X509 *abac_load_id_from_fp(FILE *fp)
181{
182    X509 *cert=PEM_read_X509(fp,NULL,NULL,NULL);
183
184    if (cert == NULL) {
185        ERR_print_errors_fp (stderr);
186        return NULL;
187    }
188    return cert;
189}
190
191X509 *abac_load_id_from_chunk(unsigned char *chunk_ptr, int chunk_len)
192{
193    X509 *n509=NULL;
194    BIO *mbio=BIO_new(BIO_s_mem());
195
196    BIO_write(mbio,chunk_ptr,chunk_len);
197    BIO_flush(mbio);
198
199    PEM_read_bio_X509(mbio,&n509,NULL,NULL);
200
201    BIO_free_all(mbio);
202
203    if (n509 == NULL) {
204        ERR_print_errors_fp (stderr);
205        return NULL;
206    }
207    return n509;
208}
209
210int abac_write_id_to_fp(X509 *cert, FILE *fp)
211{
212    assert(cert);
213
214    int ret=PEM_write_X509(fp,cert);
215
216    if (ret) {
217        ERR_print_errors_fp (stderr);
218        return ret;
219    }
220    return 0;
221}
222
223/* make stringfy a private key PEM struct */
224unsigned char *abac_string_privkey(EVP_PKEY *key)
225{
226    unsigned char *ptr=NULL;
227    unsigned char *tmp=NULL;
228
229    assert(key);
230
231    BIO *mbio=BIO_new(BIO_s_mem());
232    /* PEM_write_PrivateKey(fp,key,NULL,NULL,0,_potato_cb, "privateKey to file"); */
233    PEM_write_bio_PrivateKey(mbio,key,NULL,NULL,0,_potato_cb,"stringify privateKey");
234    BIO_flush(mbio);
235    int len=(int)BIO_ctrl(mbio,BIO_CTRL_INFO,0,tmp);
236
237    if(debug) fprintf(stderr,"CHUNKING PrivateKey... %d\n",len);
238
239    if(len) {
240        ptr=(unsigned char *)malloc(sizeof(unsigned char *)*(len+1));
241        int ret=BIO_read(mbio, (void *)ptr, len);
242        if(ret==0)
243            fprintf(stderr," abac_string_privkey failed!!\n");
244        ptr[len]='\0';
245    }
246    BIO_free_all(mbio);
247    return ptr;
248}
249
250/* make stringfy a x509 PEM struct */
251unsigned char *abac_string_cert(X509 *cert) {
252    unsigned char *ptr=NULL;
253    unsigned char *tmp=NULL;
254
255    assert(cert);
256
257    BIO *mbio=BIO_new(BIO_s_mem());
258    PEM_write_bio_X509(mbio,cert);
259    BIO_flush(mbio);
260    int len=(int)BIO_ctrl(mbio,BIO_CTRL_INFO,0,tmp);
261    if(debug) fprintf(stderr,"CHUNKING X509... %d\n",len);
262
263    if(len) {
264        ptr=(unsigned char *)malloc(sizeof(unsigned char *)*(len+1));
265        int ret=BIO_read(mbio, (void *)ptr, len);
266        if(ret==0)
267            fprintf(stderr," abac_string_cert failed!!\n");
268        ptr[len]='\0';
269    }
270   
271    BIO_free_all(mbio);
272    return ptr;
273}
274
275
276/* sign data with privkey */ 
277static int _sign_with_privkey(EVP_PKEY *privkey, char *data,
278unsigned char* signed_buf)
279{
280  int err;
281  unsigned int signed_len;
282  EVP_MD_CTX     md_ctx;
283
284  EVP_SignInit   (&md_ctx, EVP_md5());
285  EVP_SignUpdate (&md_ctx, data, strlen(data));
286  signed_len = sizeof(signed_buf);
287  err = EVP_SignFinal (&md_ctx,
288                       signed_buf,
289                       &signed_len,
290                       privkey);
291  if (err != 1) {
292      ERR_print_errors_fp (stderr);
293      return 1;
294  }
295  return 0;
296}
297
298/* verify the signature.. */
299static int _verify_with_pubkey(EVP_PKEY *pubkey, char *data,
300unsigned char* signed_buf )
301{
302  int err;
303  int signed_len;
304  EVP_MD_CTX     md_ctx;
305
306  EVP_VerifyInit   (&md_ctx, EVP_sha1());
307  EVP_VerifyUpdate (&md_ctx, data, strlen((char*)data));
308  signed_len=sizeof(signed_buf);
309  err = EVP_VerifyFinal (&md_ctx,
310                         signed_buf,
311                         signed_len,
312                         pubkey);
313
314  if (err != 1) {
315        ERR_print_errors_fp (stderr);
316        return 1;
317  }
318  fprintf(stderr, "Signature Verified Ok.\n");
319  return 0;
320}
321
322
323#define PWLEN 128
324/* EVP_PKEY *PEM_read_PrivateKey(FILE *,EVP_PKEY **,pem_cb *,void *) */
325int _potato_cb(char *buf, int sz, int rwflag, void *u)
326{
327   int len;
328   char *prompt=NULL;
329   if(u)
330       asprintf(&prompt,"Enter passphrase for %s:", (char *)u);
331       else asprintf(&prompt,"Enter passphrase :");
332   char *secret = malloc(PWLEN);
333   memset(secret, '\0', PWLEN);
334   if(!secret) {
335        perror("malloc()");
336        free(prompt);
337        return 0;
338   }
339   if (readpassphrase( prompt, secret, PWLEN, RPP_ECHO_OFF) == NULL) {
340       perror("readpassphrase()");
341       memset(secret, '\0', PWLEN);
342       len=0;
343       } else {
344           len=strlen(secret);
345           memcpy(buf, secret, len);
346           memset(secret, '\0', len);
347   }
348   free(secret);
349   free(prompt);
350   return len;
351}
352
353EVP_PKEY *abac_load_privkey_from_fp(FILE *fp)
354{
355    assert(fp);
356
357    EVP_PKEY *privkey = PEM_read_PrivateKey(fp, NULL, _potato_cb, "privateKey from file");
358
359    if (privkey == NULL) {
360        ERR_print_errors_fp (stderr);
361        return NULL;
362    }
363    return privkey;
364}
365
366/* not adding passphrase */
367int abac_write_privkey_to_fp(EVP_PKEY *key, FILE *fp) {
368    assert(key);
369
370    int ret=PEM_write_PrivateKey(fp,key,NULL,NULL,0,NULL, NULL);
371    if(ret) return ret;
372    return 0;
373}
374
375/* adding passphrase */
376int abac_write_encrypt_privkey_to_fp(EVP_PKEY *key, FILE *fp) {
377    assert(key);
378
379    int ret=PEM_write_PrivateKey(fp,key,NULL,NULL,0,_potato_cb, "privateKey to file");
380    if(ret) return ret;
381    return 0;
382}
383
384EVP_PKEY *extract_pubkey_from_cert(X509 *cert)
385{
386    EVP_PKEY *pubkey=X509_get_pubkey(cert);
387    return pubkey;
388}
389
390
391static void _callback(int p, int n, void *arg)
392{
393    char c='B';
394
395    if (p == 0) c='.';
396    if (p == 1) c='+';
397    if (p == 2) c='*';
398    if (p == 3) c='\n';
399    fputc(c,stderr);
400}
401
402/*
403RSA *RSA_generate_key(int num, unsigned long e,
404   void (*callback)(int,int,void *), void *cb_arg);
405The exponent is an odd number, typically 3, 17 or 65537
406*/
407EVP_PKEY* abac_generate_key()
408{
409    EVP_PKEY *pk=NULL;
410    int keysize=2048;
411
412    if((pk=EVP_PKEY_new()) == NULL){
413        ERR_print_errors_fp (stderr);
414        return NULL;
415    }
416
417//    RSA *rsa=RSA_generate_key(keysize,RSA_F4,_callback,NULL);
418    RSA *rsa=RSA_generate_key(keysize,RSA_F4,NULL,NULL); 
419    if (!EVP_PKEY_assign_RSA(pk,rsa)) {
420        ERR_print_errors_fp (stderr);
421        return NULL;
422    }
423    rsa=NULL;
424
425    return pk;
426}
427
428/* Add extension using V3 code: we can set the config file as NULL
429 * because we wont reference any other sections.
430 */
431static int _add_ext(X509 *cert, int nid, char *value)
432{
433    X509_EXTENSION *ex;
434    X509V3_CTX ctx;
435    /* This sets the 'context' of the extensions. */
436    /* No configuration database */
437    X509V3_set_ctx_nodb(&ctx);
438    /* Issuer and subject certs: both the target since it is self signed,
439     * no request and no CRL
440     */
441    X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
442    ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
443    if (!ex)
444        return 0;
445
446    X509_add_ext(cert,ex,-1);
447    X509_EXTENSION_free(ex);
448    return 1;
449}
450
451
452/**
453 * Generate ID certificate.
454 *
455 * validity is measured in seconds (as of 0.2.0)
456 */
457X509 *abac_generate_cert(EVP_PKEY *pkey, char *cn, long validity) {
458
459    /* must have a privkey before generating an ID cert?? */
460    assert(pkey);
461    X509 *cert=NULL;
462
463    if((cert=X509_new()) == NULL)
464            goto error;
465
466    unsigned char *serial=abac_generate_serial();
467
468    if(validity == 0) validity=(long)(60*60*24*(365));
469
470    X509_set_version(cert,2);
471
472    BIGNUM *bn=_make_bn_from_string(serial);
473    BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert));
474    /* this is prone to problem with very big days on 32 bit machines,
475       In newer openssl, can migrate to X509_time_adj_ex */ 
476    X509_gmtime_adj(X509_get_notBefore(cert),0);
477    X509_gmtime_adj(X509_get_notAfter(cert),validity);
478    X509_set_pubkey(cert,pkey);
479
480    X509_NAME *name=X509_get_subject_name(cert);
481
482    if(!name) goto error;
483
484    /* This function creates and adds the entry, working out the
485     * correct string type and performing checks on its length.
486     */
487    if(!(X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char *)cn, -1, -1, 0)))
488        goto error; // fail to add cn to cert
489
490    /* Self signed, set the issuer name to be the same as the subject. */
491    if(!(X509_set_issuer_name(cert,name)))
492        goto error; // fail to set issuer name on cert
493
494    /* Add various extensions: standard extensions */
495    if(!(_add_ext(cert, NID_basic_constraints, "critical,CA:TRUE")))
496        goto error; // fail to set basic constraint
497    if(!(_add_ext(cert, NID_key_usage, "critical,keyCertSign,cRLSign")))
498        goto error; // fail to set key usage
499    if(!(_add_ext(cert, NID_subject_key_identifier, "hash")))
500        goto error; // fail to set subject key identifier
501    if(!(_add_ext(cert, NID_authority_key_identifier, "keyid:always")))
502        goto error; // fail to set authority key identifier (self-signing)
503
504    /* make sure it is signed */
505    if (!X509_sign(cert,pkey,EVP_sha1()))
506        goto error;
507
508    return cert;
509
510error:
511    if(cert) X509_free(cert);
512    if(serial) free(serial);
513    if(bn) BN_free(bn);
514    return NULL;
515}
516
517static char *_time_in_string(ASN1_TIME *tm)
518{
519    char *ptr=NULL;
520    BIO *mbio=BIO_new(BIO_s_mem());
521    ASN1_TIME_print(mbio, tm);
522    BIO_flush(mbio);
523    int len=BIO_number_written(mbio);
524    ptr=(char *) malloc(sizeof(char *)*(len+1));
525    int ret=BIO_read(mbio, (void *)ptr, len);
526
527    BIO_free_all(mbio);
528    if(ret)
529        return ptr; 
530        else return NULL;
531} 
532
533
534/* atime->data, YYmmddHHMMSS or YYYYmmddHHMMSSZZ
535 *  V_ASN1_UTCTIME, V_ASN1_GENERALIZEDTIME
536 */
537static int _convert_time(struct tm *ttime, ASN1_TIME *atime) {
538    assert(atime); assert(atime->data);
539
540    int type=atime->type;
541    int len=strlen((char *)atime->data);
542    if(len==0) return 0;
543
544    char *astring=strndup((char *)atime->data,len);
545
546    /* setting ttime structure */
547    if (type == V_ASN1_UTCTIME) {
548           strptime(astring, "%y%m%d%H%M%S", ttime);
549        } else {
550        if (type == V_ASN1_GENERALIZEDTIME)
551           strptime(astring, "%Y%m%d%H%M%S", ttime);
552           else fprintf(stderr,"ERROR,.. unknown type in ASN1_TIME struct\n");
553    }
554
555    if(debug) {
556        char *tstring=asctime(ttime);
557        fprintf(stderr,"PPP final time string is %s\n",tstring);
558    }
559    return 1;
560}
561
562/* check whether the cert is still valid or not and also extract what its
563   not_before and not_after field */
564int abac_check_validity(X509 *cert, struct tm *not_before, struct tm *not_after) {
565    assert(cert);
566
567    int valid=0;
568    int ret=0;
569    memset(not_before, 0, sizeof(struct tm));
570    memset(not_after, 0, sizeof(struct tm));
571    ASN1_TIME *notAfter= X509_get_notAfter(cert);
572    ASN1_TIME *notBefore=X509_get_notBefore(cert);
573
574    ret=_convert_time(not_before, notBefore);
575    if(ret==0) return 0;
576    ret=_convert_time(not_after, notAfter);
577    if(ret==0) return 0;
578
579    if((X509_cmp_current_time(notBefore) >=0) ||
580                  (X509_cmp_current_time(notAfter) <=0) )
581      valid=1;
582
583    if(valid) return 0;
584       else return 1;
585}
586
587/* check if cert is still valid at current time, 1 for yes, 0 for no*/
588int abac_still_valid(X509 *cert)
589{
590    ASN1_TIME *notAfter= X509_get_notAfter(cert);
591    ASN1_TIME *notBefore=X509_get_notBefore(cert);
592    if(0) {
593        fprintf(stderr,"((X509_cmp_current_time(notBefore) is %d\n", 
594                                  X509_cmp_current_time(notBefore));
595        fprintf(stderr,"((X509_cmp_current_time(notAfter) is %d\n", 
596                                  X509_cmp_current_time(notAfter));
597    }
598    if((X509_cmp_current_time(notBefore) >=0) ||
599                   (X509_cmp_current_time(notAfter) <=0) )
600      return 0;
601    return 1;
602}
603
604int abac_self_signed(X509 *cert)
605{
606    int selfsigned=0;
607    X509_STORE *store=X509_STORE_new();
608    X509_STORE_CTX *ctx=X509_STORE_CTX_new();
609    /* Check selfsigned CA signature */
610    X509_STORE_set_flags(store,X509_V_FLAG_CHECK_SS_SIGNATURE);
611    X509_STORE_CTX_init(ctx,store,cert,NULL);
612    int ret=X509_verify_cert(ctx);
613    if(ret!=1) { /* 1 is okay, 0 or negative value are failures */
614        if(debug) fprintf(stderr,"error %d at %d depth lookup:%s\n",
615                ctx->error, ctx->error_depth, X509_verify_cert_error_string(ctx->error));
616        } else {
617            selfsigned=1;
618    }
619
620    X509_STORE_CTX_free(ctx);
621    X509_STORE_free(store);
622    return selfsigned;
623}
624
625int abac_signed_by(X509 *cert, X509 *root)
626{
627    int signedby=0;
628    X509_STORE *store=X509_STORE_new();
629    X509_STORE_add_cert(store,root);
630    X509_STORE_CTX *ctx=X509_STORE_CTX_new();
631    X509_STORE_CTX_init(ctx,store,cert,NULL);
632    int ret=X509_verify_cert(ctx);
633    if(ret!=1) { /* 1 is okay, 0 or negative value are failures */
634        if(debug) fprintf(stderr,"error %d at %d depth lookup:%s\n",
635                ctx->error, ctx->error_depth, X509_verify_cert_error_string(ctx->error));
636        } else {
637            signedby=1;
638    }
639
640    X509_STORE_CTX_free(ctx);
641    X509_STORE_free(store);
642    return signedby;
643}
644
645
646
647char *abac_get_cn(X509 *cert)
648{
649   X509_NAME *nptr=X509_get_subject_name (cert);
650   int pos=X509_NAME_get_index_by_NID(nptr, NID_commonName,-1);
651   X509_NAME_ENTRY *ent=X509_NAME_get_entry(nptr,pos); 
652   ASN1_STRING *adata=X509_NAME_ENTRY_get_data(ent);
653   unsigned char *val=ASN1_STRING_data(adata);
654   if(debug) fprintf(stderr," cn:(%d)(%s)\n", strlen((char *)val), val);
655   return (char *) val;
656}
657
658char *abac_get_serial(X509 *cert)
659{
660   char *ret=NULL;
661   ASN1_INTEGER *num=X509_get_serialNumber(cert);
662   BIGNUM *bnser=ASN1_INTEGER_to_BN(num,NULL);
663   int n=BN_num_bytes(bnser);
664   unsigned char buf[n];
665   int b=BN_bn2bin(bnser,buf);
666   if(debug) { 
667       fprintf(stderr," extract serial number:->(%d)",b);
668       BN_print_fp(stderr,bnser);
669   } 
670   if(n)
671       ret=strndup((char *)buf,n);
672   return ret;
673}
674
675char *abac_get_subject(X509 *cert) 
676{
677   char *ptr=X509_NAME_oneline(X509_get_subject_name(cert),0,0);
678   return ptr;
679}
680
681char *abac_get_issuer(X509 *cert) 
682{   
683   char *ptr=X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
684   return ptr;
685}
686
687//  success: malloc'd copy with all hex digits lowercase
688//  fail: NULL
689unsigned char *abac_get_keyid(X509 *cert)
690{
691    ASN1_OCTET_STRING *ptr=X509_get_ext_d2i(cert,NID_subject_key_identifier,NULL,NULL);
692    char *str=i2s_ASN1_OCTET_STRING(NULL, ptr);
693    unsigned char *sha=(unsigned char *) malloc(sizeof(unsigned char)*SHA1_LENGTH+1);
694    memset(sha, '\0', SHA1_LENGTH);
695
696    int len=strlen(str);
697    int i=0;
698    int j=0;
699    for (j = 0; j < len; j++) {
700        if(str[j]==':')
701            continue;
702        sha[i] = tolower(str[j]);
703        if (!isxdigit(sha[i]))
704            goto error;
705        i++;
706        if(i>SHA1_LENGTH) 
707            goto error;
708    }
709    free(str);
710    sha[i]='\0';
711   
712    return sha;
713
714error:
715    free(str);
716    if(debug) fprintf(stderr,"SHA1 -> %s\n",sha);
717    free(sha);
718    return NULL;
719}
Note: See TracBrowser for help on using the repository browser.