source: libabac/abac_openssl.c @ 9e063cb

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

1) test out using encrypted private key to generate id credential

  • Property mode set to 100644
File size: 18.0 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
158/*** not used
159static char *_read_blob_from_file(char *fname, int *len)
160{
161    struct stat sb;
162    char *dptr=NULL;
163
164    int fd = open(fname, O_RDONLY);
165    if (fd == -1) { return NULL; }
166    if(stat(fname, &sb) == -1) {
167        close(fd);
168        return NULL;
169    }
170    dptr= (char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
171    close(fd);
172
173    if(dptr == MAP_FAILED) {
174        return NULL;
175    }
176    *len=sb.st_size;
177    return dptr;
178}
179***/
180
181/* Read ID in PEM */
182X509 *abac_load_id_from_fp(FILE *fp)
183{
184    X509 *cert=PEM_read_X509(fp,NULL,NULL,NULL);
185
186    if (cert == NULL) {
187        ERR_print_errors_fp (stderr);
188        return NULL;
189    }
190    return cert;
191}
192
193X509 *abac_load_id_from_chunk(unsigned char *chunk_ptr, int chunk_len)
194{
195    X509 *n509=NULL;
196    BIO *mbio=BIO_new(BIO_s_mem());
197
198    BIO_write(mbio,chunk_ptr,chunk_len);
199    BIO_flush(mbio);
200
201    PEM_read_bio_X509(mbio,&n509,NULL,NULL);
202
203    BIO_free_all(mbio);
204
205    if (n509 == NULL) {
206        ERR_print_errors_fp (stderr);
207        return NULL;
208    }
209    return n509;
210}
211
212int abac_write_id_to_fp(X509 *cert, FILE *fp)
213{
214    assert(cert);
215
216    if(!PEM_write_X509(fp,cert)) {
217        ERR_print_errors_fp (stderr);
218        return 1;
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/* not used, 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
299/* nost used, verify the signature..
300static int _verify_with_pubkey(EVP_PKEY *pubkey, char *data,
301unsigned char* signed_buf )
302{
303  int err;
304  int signed_len;
305  EVP_MD_CTX     md_ctx;
306
307  EVP_VerifyInit   (&md_ctx, EVP_sha1());
308  EVP_VerifyUpdate (&md_ctx, data, strlen((char*)data));
309  signed_len=sizeof(signed_buf);
310  err = EVP_VerifyFinal (&md_ctx,
311                         signed_buf,
312                         signed_len,
313                         pubkey);
314
315  if (err != 1) {
316        ERR_print_errors_fp (stderr);
317        return 1;
318  }
319  fprintf(stderr, "Signature Verified Ok.\n");
320  return 0;
321}
322***/
323
324
325#define PWLEN 128
326/* EVP_PKEY *PEM_read_PrivateKey(FILE *,EVP_PKEY **,pem_cb *,void *) */
327int _potato_cb(char *buf, int sz, int rwflag, void *u)
328{
329   int len;
330   char *prompt=NULL;
331   if(u)
332       asprintf(&prompt,"Enter passphrase for %s:", (char *)u);
333       else asprintf(&prompt,"Enter passphrase :");
334   char *secret = malloc(PWLEN);
335   memset(secret, '\0', PWLEN);
336   if(!secret) {
337        perror("malloc()");
338        free(prompt);
339        return 0;
340   }
341   if (readpassphrase( prompt, secret, PWLEN, RPP_ECHO_OFF) == NULL) {
342       perror("readpassphrase()");
343       memset(secret, '\0', PWLEN);
344       len=0;
345       } else {
346           len=strlen(secret);
347           memcpy(buf, secret, len);
348           memset(secret, '\0', len);
349   }
350   free(secret);
351   free(prompt);
352   return len;
353}
354
355EVP_PKEY *abac_load_privkey_from_fp(FILE *fp)
356{
357    assert(fp);
358
359    EVP_PKEY *privkey = PEM_read_PrivateKey(fp, NULL, _potato_cb, "privateKey from file");
360
361    if (privkey == NULL) {
362        ERR_print_errors_fp (stderr);
363        return NULL;
364    }
365    return privkey;
366}
367
368/* not adding passphrase */
369int abac_write_privkey_to_fp(EVP_PKEY *key, FILE *fp) {
370    assert(key);
371
372    if(!PEM_write_PrivateKey(fp,key,NULL,NULL,0,NULL, NULL)) {
373        ERR_print_errors_fp (stderr);
374        return 1;
375    }
376    return 0;
377}
378
379/* adding passphrase */
380int abac_write_encrypt_privkey_to_fp(EVP_PKEY *key, FILE *fp) {
381    assert(key);
382
383    if(!PEM_write_PrivateKey(fp,key,NULL,NULL,0,_potato_cb, "privateKey to file")) {
384        ERR_print_errors_fp (stderr);
385        return 1;
386    }
387    return 0;
388}
389
390EVP_PKEY *extract_pubkey_from_cert(X509 *cert)
391{
392    EVP_PKEY *pubkey=X509_get_pubkey(cert);
393    return pubkey;
394}
395
396
397/** not used,
398static void _callback(int p, int n, void *arg)
399{
400    char c='B';
401
402    if (p == 0) c='.';
403    if (p == 1) c='+';
404    if (p == 2) c='*';
405    if (p == 3) c='\n';
406    fputc(c,stderr);
407}
408***/
409
410/*
411RSA *RSA_generate_key(int num, unsigned long e,
412   void (*callback)(int,int,void *), void *cb_arg);
413The exponent is an odd number, typically 3, 17 or 65537
414*/
415EVP_PKEY* abac_generate_key()
416{
417    EVP_PKEY *pk=NULL;
418    int keysize=2048;
419
420    if((pk=EVP_PKEY_new()) == NULL){
421        ERR_print_errors_fp (stderr);
422        return NULL;
423    }
424
425//    RSA *rsa=RSA_generate_key(keysize,RSA_F4,_callback,NULL);
426    RSA *rsa=RSA_generate_key(keysize,RSA_F4,NULL,NULL); 
427    if (!EVP_PKEY_assign_RSA(pk,rsa)) {
428        ERR_print_errors_fp (stderr);
429        return NULL;
430    }
431    rsa=NULL;
432
433    return pk;
434}
435
436/* Add extension using V3 code: we can set the config file as NULL
437 * because we wont reference any other sections.
438 */
439static int _add_ext(X509 *cert, int nid, char *value)
440{
441    X509_EXTENSION *ex;
442    X509V3_CTX ctx;
443    /* This sets the 'context' of the extensions. */
444    /* No configuration database */
445    X509V3_set_ctx_nodb(&ctx);
446    /* Issuer and subject certs: both the target since it is self signed,
447     * no request and no CRL
448     */
449    X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
450    ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
451    if (!ex)
452        return 0;
453
454    X509_add_ext(cert,ex,-1);
455    X509_EXTENSION_free(ex);
456    return 1;
457}
458
459
460/**
461 * Generate ID certificate.
462 *
463 * validity is measured in seconds (as of 0.2.0)
464 */
465X509 *abac_generate_cert(EVP_PKEY *pkey, char *cn, long validity) {
466
467    /* must have a privkey before generating an ID cert */
468    assert(pkey);
469    X509 *cert=NULL;
470
471    if((cert=X509_new()) == NULL)
472            goto error;
473
474    unsigned char *serial=abac_generate_serial();
475
476    if(validity == 0) validity=(long)(60*60*24*(365));
477
478    X509_set_version(cert,2);
479
480    BIGNUM *bn=_make_bn_from_string(serial);
481    BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert));
482    /* this is prone to problem with very big days on 32 bit machines,
483       In newer openssl, can migrate to X509_time_adj_ex */ 
484    X509_gmtime_adj(X509_get_notBefore(cert),0);
485    X509_gmtime_adj(X509_get_notAfter(cert),validity);
486    X509_set_pubkey(cert,pkey);
487
488    X509_NAME *name=X509_get_subject_name(cert);
489
490    if(!name) goto error;
491
492    /* This function creates and adds the entry, working out the
493     * correct string type and performing checks on its length.
494     */
495    if(!(X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char *)cn, -1, -1, 0)))
496        goto error; // fail to add cn to cert
497
498    /* Self signed, set the issuer name to be the same as the subject. */
499    if(!(X509_set_issuer_name(cert,name)))
500        goto error; // fail to set issuer name on cert
501
502    /* Add various extensions: standard extensions */
503    if(!(_add_ext(cert, NID_basic_constraints, "critical,CA:TRUE")))
504        goto error; // fail to set basic constraint
505    if(!(_add_ext(cert, NID_key_usage, "critical,keyCertSign,cRLSign")))
506        goto error; // fail to set key usage
507    if(!(_add_ext(cert, NID_subject_key_identifier, "hash")))
508        goto error; // fail to set subject key identifier
509    if(!(_add_ext(cert, NID_authority_key_identifier, "keyid:always")))
510        goto error; // fail to set authority key identifier (self-signing)
511
512    /* make sure it is signed */
513    if (!X509_sign(cert,pkey,EVP_sha1()))
514        goto error;
515
516    return cert;
517
518error:
519    if(cert) X509_free(cert);
520    if(serial) free(serial);
521    if(bn) BN_free(bn);
522    return NULL;
523}
524
525/*** not used,
526static char *_time_in_string(ASN1_TIME *tm)
527{
528    char *ptr=NULL;
529    BIO *mbio=BIO_new(BIO_s_mem());
530    ASN1_TIME_print(mbio, tm);
531    BIO_flush(mbio);
532    int len=BIO_number_written(mbio);
533    ptr=(char *) malloc(sizeof(char *)*(len+1));
534    int ret=BIO_read(mbio, (void *)ptr, len);
535
536    BIO_free_all(mbio);
537    if(ret)
538        return ptr;
539        else return NULL;
540}
541***/
542
543
544/* atime->data, YYmmddHHMMSS or YYYYmmddHHMMSSZZ
545 *  V_ASN1_UTCTIME, V_ASN1_GENERALIZEDTIME
546 */
547static int _convert_time(struct tm *ttime, ASN1_TIME *atime) {
548    assert(atime); assert(atime->data);
549
550    int type=atime->type;
551    int len=strlen((char *)atime->data);
552    if(len==0) return 0;
553
554    char *astring=strndup((char *)atime->data,len);
555
556    /* setting ttime structure */
557    if (type == V_ASN1_UTCTIME) {
558           strptime(astring, "%y%m%d%H%M%S", ttime);
559        } else {
560        if (type == V_ASN1_GENERALIZEDTIME)
561           strptime(astring, "%Y%m%d%H%M%S", ttime);
562           else fprintf(stderr,"ERROR,.. unknown type in ASN1_TIME struct\n");
563    }
564
565    if(debug) {
566        char *tstring=asctime(ttime);
567        fprintf(stderr,"PPP final time string is %s\n",tstring);
568    }
569    return 1;
570}
571
572/* check whether the cert is still valid or not and also extract what its
573   not_before and not_after field, 0 is okay, 1 is not */
574int abac_check_validity(X509 *cert, struct tm *not_before, struct tm *not_after) {
575    assert(cert);
576
577    int valid=0;
578    int ret=0;
579    memset(not_before, 0, sizeof(struct tm));
580    memset(not_after, 0, sizeof(struct tm));
581    ASN1_TIME *notAfter= X509_get_notAfter(cert);
582    ASN1_TIME *notBefore=X509_get_notBefore(cert);
583
584    ret=_convert_time(not_before, notBefore);
585    if(ret==0) return 1;
586    ret=_convert_time(not_after, notAfter);
587    if(ret==0) return 1;
588
589    if((X509_cmp_current_time(notBefore) >=0) ||
590                  (X509_cmp_current_time(notAfter) <=0) )
591      valid=1;
592
593    if(valid) return 0;
594       else return 1;
595}
596
597/* check if cert is still valid at current time, 1 for yes, 0 for no*/
598int abac_still_valid(X509 *cert)
599{
600    ASN1_TIME *notAfter= X509_get_notAfter(cert);
601    ASN1_TIME *notBefore=X509_get_notBefore(cert);
602    if(0) {
603        fprintf(stderr,"((X509_cmp_current_time(notBefore) is %d\n", 
604                                  X509_cmp_current_time(notBefore));
605        fprintf(stderr,"((X509_cmp_current_time(notAfter) is %d\n", 
606                                  X509_cmp_current_time(notAfter));
607    }
608    if((X509_cmp_current_time(notBefore) >=0) ||
609                   (X509_cmp_current_time(notAfter) <=0) )
610      return 0;
611    return 1;
612}
613
614int abac_self_signed(X509 *cert)
615{
616    int selfsigned=0;
617    X509_STORE *store=X509_STORE_new();
618    X509_STORE_CTX *ctx=X509_STORE_CTX_new();
619    /* Check selfsigned CA signature */
620    X509_STORE_set_flags(store,X509_V_FLAG_CHECK_SS_SIGNATURE);
621    X509_STORE_CTX_init(ctx,store,cert,NULL);
622    int ret=X509_verify_cert(ctx);
623    if(ret!=1) { /* 1 is okay, 0 or negative value are failures */
624        if(debug) fprintf(stderr,"error %d at %d depth lookup:%s\n",
625                ctx->error, ctx->error_depth, X509_verify_cert_error_string(ctx->error));
626        } else {
627            selfsigned=1;
628    }
629
630    X509_STORE_CTX_free(ctx);
631    X509_STORE_free(store);
632    return selfsigned;
633}
634
635int abac_signed_by(X509 *cert, X509 *root)
636{
637    int signedby=0;
638    X509_STORE *store=X509_STORE_new();
639    X509_STORE_add_cert(store,root);
640    X509_STORE_CTX *ctx=X509_STORE_CTX_new();
641    X509_STORE_CTX_init(ctx,store,cert,NULL);
642    int ret=X509_verify_cert(ctx);
643    if(ret!=1) { /* 1 is okay, 0 or negative value are failures */
644        if(debug) fprintf(stderr,"error %d at %d depth lookup:%s\n",
645                ctx->error, ctx->error_depth, X509_verify_cert_error_string(ctx->error));
646        } else {
647            signedby=1;
648    }
649
650    X509_STORE_CTX_free(ctx);
651    X509_STORE_free(store);
652    return signedby;
653}
654
655
656
657char *abac_get_cn(X509 *cert)
658{
659   X509_NAME *nptr=X509_get_subject_name (cert);
660   int pos=X509_NAME_get_index_by_NID(nptr, NID_commonName,-1);
661   X509_NAME_ENTRY *ent=X509_NAME_get_entry(nptr,pos); 
662   ASN1_STRING *adata=X509_NAME_ENTRY_get_data(ent);
663   unsigned char *val=ASN1_STRING_data(adata);
664   if(debug) fprintf(stderr," cn:(%d)(%s)\n", strlen((char *)val), val);
665   return (char *) val;
666}
667
668char *abac_get_serial(X509 *cert)
669{
670   char *ret=NULL;
671   ASN1_INTEGER *num=X509_get_serialNumber(cert);
672   BIGNUM *bnser=ASN1_INTEGER_to_BN(num,NULL);
673   int n=BN_num_bytes(bnser);
674   unsigned char buf[n];
675   int b=BN_bn2bin(bnser,buf);
676   if(debug) { 
677       fprintf(stderr," extract serial number:->(%d)",b);
678       BN_print_fp(stderr,bnser);
679   } 
680   if(n)
681       ret=strndup((char *)buf,n);
682   return ret;
683}
684
685char *abac_get_subject(X509 *cert) 
686{
687   char *ptr=X509_NAME_oneline(X509_get_subject_name(cert),0,0);
688   return ptr;
689}
690
691char *abac_get_issuer(X509 *cert) 
692{   
693   char *ptr=X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
694   return ptr;
695}
696
697//  success: malloc'd copy with all hex digits lowercase
698//  fail: NULL
699unsigned char *abac_get_keyid(X509 *cert)
700{
701    ASN1_OCTET_STRING *ptr=X509_get_ext_d2i(cert,NID_subject_key_identifier,NULL,NULL);
702    char *str=i2s_ASN1_OCTET_STRING(NULL, ptr);
703    unsigned char *sha=(unsigned char *) malloc(sizeof(unsigned char)*SHA1_LENGTH+1);
704    memset(sha, '\0', SHA1_LENGTH);
705
706    int len=strlen(str);
707    int i=0;
708    int j=0;
709    for (j = 0; j < len; j++) {
710        if(str[j]==':')
711            continue;
712        sha[i] = tolower(str[j]);
713        if (!isxdigit(sha[i]))
714            goto error;
715        i++;
716        if(i>SHA1_LENGTH) 
717            goto error;
718    }
719    free(str);
720    sha[i]='\0';
721   
722    return sha;
723
724error:
725    free(str);
726    if(debug) fprintf(stderr,"SHA1 -> %s\n",sha);
727    free(sha);
728    return NULL;
729}
Note: See TracBrowser for help on using the repository browser.