Coverage Report

Created: 2022-11-18 06:58

/src/netcdf-c/libdispatch/drc.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
3
See COPYRIGHT for license information.
4
*/
5
6
#include "config.h"
7
#ifdef HAVE_UNISTD_H
8
#include <unistd.h>
9
#endif
10
#ifdef HAVE_STDARG_H
11
#include <stdarg.h>
12
#endif
13
#include <stdio.h>
14
#include <stdlib.h>
15
#include <string.h>
16
#include <assert.h>
17
18
#include "netcdf.h"
19
#include "ncbytes.h"
20
#include "ncuri.h"
21
#include "ncrc.h"
22
#include "nclog.h"
23
#include "ncauth.h"
24
#include "ncpathmgr.h"
25
#include "nc4internal.h"
26
#include "ncdispatch.h"
27
28
#ifndef nulldup
29
 #define nulldup(x) ((x)?strdup(x):(x))
30
#endif
31
32
#undef NOREAD
33
34
#undef DRCDEBUG
35
#undef LEXDEBUG
36
#undef PARSEDEBUG
37
#undef AWSDEBUG
38
39
0
#define RTAG ']'
40
0
#define LTAG '['
41
42
#undef MEMCHECK
43
#define MEMCHECK(x) if((x)==NULL) {goto nomem;} else {}
44
45
/* Alternate .aws directory location */
46
1
#define NC_TEST_AWS_DIR "NC_TEST_AWS_DIR"
47
48
/* Forward */
49
static int NC_rcload(void);
50
static char* rcreadline(char** nextlinep);
51
static void rctrim(char* text);
52
static void rcorder(NClist* rc);
53
static int rccompile(const char* path);
54
static int rcequal(NCRCentry* e1, NCRCentry* e2);
55
static int rclocatepos(const char* key, const char* hostport, const char* urlpath);
56
static struct NCRCentry* rclocate(const char* key, const char* hostport, const char* urlpath);
57
static int rcsearch(const char* prefix, const char* rcname, char** pathp);
58
static void rcfreeentries(NClist* rc);
59
static void rcfreeentry(NCRCentry* t);
60
#ifdef DRCDEBUG
61
static void storedump(char* msg, NClist* entrys);
62
#endif
63
static int aws_load_credentials(NCglobalstate*);
64
static void freeprofile(struct AWSprofile* profile);
65
static void freeprofilelist(NClist* profiles);
66
67
/* Define default rc files and aliases, also defines load order*/
68
static const char* rcfilenames[] = {".ncrc", ".daprc", ".dodsrc",NULL};
69
70
/* Read these files */
71
static const char* awsconfigfiles[] = {".aws/credentials",".aws/config",NULL};
72
73
static int NCRCinitialized = 0;
74
75
/**************************************************/
76
/* User API */
77
78
/**
79
The most common case is to get the most general value for a key,
80
where most general means that the urlpath and hostport are null
81
So this function returns the value associated with the key
82
where the .rc entry has the simple form "key=value".
83
If that entry is not found, then return NULL.
84
85
@param key table entry key field
86
@return value matching the key -- caller frees
87
@return NULL if no entry of the form key=value exists
88
*/
89
char*
90
nc_rc_get(const char* key)
91
0
{
92
0
    NCglobalstate* ncg = NULL;
93
0
    char* value = NULL;
94
95
0
    if(!NC_initialized) nc_initialize();
96
97
0
    ncg = NC_getglobalstate();
98
0
    assert(ncg != NULL && ncg->rcinfo != NULL && ncg->rcinfo->entries != NULL);
99
0
    if(ncg->rcinfo->ignore) return NC_NOERR;
100
0
    value = NC_rclookup(key,NULL,NULL);
101
0
    return nulldup(value);    
102
0
}
103
104
/**
105
Set simple key=value in .rc table.
106
Will overwrite any existing value.
107
108
@param key
109
@param value 
110
@return NC_NOERR if success
111
@return NC_EINVAL if fail
112
*/
113
int
114
nc_rc_set(const char* key, const char* value)
115
0
{
116
0
    int stat = NC_NOERR;
117
0
    NCglobalstate* ncg = NULL;
118
119
0
    if(!NC_initialized) nc_initialize();
120
121
0
    ncg = NC_getglobalstate();
122
0
    assert(ncg != NULL && ncg->rcinfo != NULL && ncg->rcinfo->entries != NULL);
123
0
    if(ncg->rcinfo->ignore) return NC_NOERR;
124
0
    stat = NC_rcfile_insert(key,NULL,NULL,value);
125
0
    return stat;
126
0
}
127
128
/**************************************************/
129
/* External Entry Points */
130
131
/*
132
Initialize defaults and load:
133
* .ncrc
134
* .daprc
135
* .dodsrc
136
* ${HOME}/.aws/config
137
* ${HOME}/.aws/credentials
138
139
For debugging support, it is possible
140
to change where the code looks for the .aws directory.
141
This is set by the environment variable NC_TEST_AWS_DIR.
142
143
*/
144
145
void
146
ncrc_initialize(void)
147
1
{
148
1
    int stat = NC_NOERR;
149
1
    NCglobalstate* ncg = NULL;
150
151
1
    if(NCRCinitialized) return;
152
1
    NCRCinitialized = 1; /* prevent recursion */
153
154
1
    ncg = NC_getglobalstate();
155
156
1
#ifndef NOREAD
157
    /* Load entrys */
158
1
    if((stat = NC_rcload())) {
159
0
        nclog(NCLOGWARN,".rc loading failed");
160
0
    }
161
    /* Load .aws/config */
162
1
    if((stat = aws_load_credentials(ncg))) {
163
1
        nclog(NCLOGWARN,"AWS config file not loaded");
164
1
    }
165
1
#endif
166
1
}
167
168
static void
169
ncrc_setrchome(void)
170
1
{
171
1
    const char* tmp = NULL;
172
1
    NCglobalstate* ncg = NC_getglobalstate();
173
1
    assert(ncg && ncg->home);
174
1
    if(ncg->rcinfo->rchome) return;
175
1
    tmp = getenv(NCRCENVHOME);
176
1
    if(tmp == NULL || strlen(tmp) == 0)
177
1
  tmp = ncg->home;
178
1
    ncg->rcinfo->rchome = strdup(tmp);
179
#ifdef DRCDEBUG
180
    fprintf(stderr,"ncrc_setrchome: %s\n",ncg->rcinfo->rchome);
181
#endif
182
1
}
183
184
void
185
NC_rcclear(NCRCinfo* info)
186
1
{
187
1
    if(info == NULL) return;
188
1
    nullfree(info->rcfile);
189
1
    nullfree(info->rchome);
190
1
    rcfreeentries(info->entries);
191
1
    freeprofilelist(info->s3profiles);
192
193
1
}
194
195
static void
196
rcfreeentry(NCRCentry* t)
197
0
{
198
0
  nullfree(t->host);
199
0
  nullfree(t->urlpath);
200
0
  nullfree(t->key);
201
0
  nullfree(t->value);
202
0
  free(t);
203
0
}
204
205
static void
206
rcfreeentries(NClist* rc)
207
1
{
208
1
    int i;
209
1
    for(i=0;i<nclistlength(rc);i++) {
210
0
  NCRCentry* t = (NCRCentry*)nclistget(rc,i);
211
0
  rcfreeentry(t);
212
0
    }
213
1
    nclistfree(rc);
214
1
}
215
216
/* locate, read and compile the rc files, if any */
217
static int
218
NC_rcload(void)
219
1
{
220
1
    int i,ret = NC_NOERR;
221
1
    char* path = NULL;
222
1
    NCglobalstate* globalstate = NULL;
223
1
    NClist* rcfileorder = nclistnew();
224
225
1
    if(!NCRCinitialized) ncrc_initialize();
226
1
    globalstate = NC_getglobalstate();
227
228
1
    if(globalstate->rcinfo->ignore) {
229
0
        nclog(NCLOGDBG,".rc file loading suppressed");
230
0
  goto done;
231
0
    }
232
1
    if(globalstate->rcinfo->loaded) goto done;
233
234
    /* locate the configuration files in order of use:
235
       1. Specified by NCRCENV_RC environment variable.
236
       2. If NCRCENV_RC is not set then merge the set of rc files in this order:
237
    1. $RCHOME/.ncrc
238
      2. $RCHOME/.daprc
239
    3. $RCHOME/.docsrc
240
    4. $CWD/.ncrc
241
      5. $CWD/.daprc
242
    6. $CWD/.docsrc
243
    Entry in later files override any of the earlier files
244
    */
245
1
    if(globalstate->rcinfo->rcfile != NULL) { /* always use this */
246
0
  nclistpush(rcfileorder,strdup(globalstate->rcinfo->rcfile));
247
1
    } else {
248
1
  const char** rcname;
249
1
  const char* dirnames[3];
250
1
  const char** dir;
251
252
        /* Make sure rcinfo.rchome is defined */
253
1
  ncrc_setrchome();
254
1
  dirnames[0] = globalstate->rcinfo->rchome;
255
1
  dirnames[1] = globalstate->cwd;
256
1
  dirnames[2] = NULL;
257
258
3
        for(dir=dirnames;*dir;dir++) {
259
8
      for(rcname=rcfilenames;*rcname;rcname++) {
260
6
          ret = rcsearch(*dir,*rcname,&path);
261
6
    if(ret == NC_NOERR && path != NULL)
262
0
        nclistpush(rcfileorder,path);
263
6
    path = NULL;
264
6
      }
265
2
  }
266
1
    }
267
1
    for(i=0;i<nclistlength(rcfileorder);i++) {
268
0
  path = (char*)nclistget(rcfileorder,i);
269
0
  if((ret=rccompile(path))) {
270
0
      nclog(NCLOGWARN, "Error parsing %s\n",path);
271
0
      ret = NC_NOERR; /* ignore it */
272
0
      goto done;
273
0
  }
274
0
    }
275
276
1
done:
277
1
    globalstate->rcinfo->loaded = 1; /* even if not exists */
278
1
    nclistfreeall(rcfileorder);
279
1
    return (ret);
280
1
}
281
282
/**
283
 * Locate a entry by property key and host+port (may be null)
284
 * If duplicate keys, first takes precedence.
285
 */
286
char*
287
NC_rclookup(const char* key, const char* hostport, const char* urlpath)
288
1
{
289
1
    struct NCRCentry* entry = NULL;
290
1
    if(!NCRCinitialized) ncrc_initialize();
291
1
    entry = rclocate(key,hostport,urlpath);
292
1
    return (entry == NULL ? NULL : entry->value);
293
1
}
294
295
/**
296
 * Locate a entry by property key and uri.
297
 * If duplicate keys, first takes precedence.
298
 */
299
char*
300
NC_rclookupx(NCURI* uri, const char* key)
301
0
{
302
0
    char* hostport = NULL;
303
0
    char* result = NULL;
304
305
0
    hostport = NC_combinehostport(uri);
306
0
    result = NC_rclookup(key,hostport,uri->path);
307
0
    nullfree(hostport);
308
0
    return result;
309
0
}
310
311
#if 0
312
/*!
313
Set the absolute path to use for the rc file.
314
WARNING: this MUST be called before any other
315
call in order for this to take effect.
316
317
\param[in] rcfile The path to use. If NULL then do not use any rcfile.
318
319
\retval OC_NOERR if the request succeeded.
320
\retval OC_ERCFILE if the file failed to load
321
*/
322
323
int
324
NC_set_rcfile(const char* rcfile)
325
{
326
    int stat = NC_NOERR;
327
    FILE* f = NULL;
328
    NCglobalstate* globalstate = NC_getglobalstate();
329
330
    if(rcfile != NULL && strlen(rcfile) == 0)
331
  rcfile = NULL;
332
    f = NCfopen(rcfile,"r");
333
    if(f == NULL) {
334
  stat = NC_ERCFILE;
335
        goto done;
336
    }
337
    fclose(f);
338
    nullfree(globalstate->rcinfo->rcfile);
339
    globalstate->rcinfo->rcfile = strdup(rcfile);
340
    /* Clear globalstate->rcinfo */
341
    NC_rcclear(&globalstate->rcinfo);
342
    /* (re) load the rcfile and esp the entriestore*/
343
    stat = NC_rcload();
344
done:
345
    return stat;
346
}
347
#endif
348
349
/**************************************************/
350
/* RC processing functions */
351
352
static char*
353
rcreadline(char** nextlinep)
354
0
{
355
0
    char* line;
356
0
    char* p;
357
358
0
    line = (p = *nextlinep);
359
0
    if(*p == '\0') return NULL; /*signal done*/
360
0
    for(;*p;p++) {
361
0
  if(*p == '\r' && p[1] == '\n') *p = '\0';
362
0
  else if(*p == '\n') break;
363
0
    }
364
0
    *p++ = '\0'; /* null terminate line; overwrite newline */
365
0
    *nextlinep = p;
366
0
    return line;
367
0
}
368
369
/* Trim TRIMCHARS from both ends of text; */
370
static void
371
rctrim(char* text)
372
0
{
373
0
    char* p;
374
0
    char* q;
375
0
    size_t len = 0;
376
0
    int i;
377
378
0
    if(text == NULL || *text == '\0') return;
379
380
0
    len = strlen(text);
381
382
    /* elide upto first non-trimchar */
383
0
    for(q=text,p=text;*p;p++) {
384
0
  if(*p != ' ' && *p != '\t' && *p != '\r') {*q++ = *p;}
385
0
    }
386
0
    len = strlen(p);
387
    /* locate last non-trimchar */
388
0
    if(len > 0) {
389
0
        for(i=(len-1);i>=0;i--) {
390
0
      p = &text[i];
391
0
      if(*p != ' ' && *p != '\t' && *p != '\r') {break;}
392
0
      *p = '\0'; /* elide trailing trimchars */
393
0
        }
394
0
    }
395
0
}
396
397
/* Order the entries: those with urls must be first,
398
   but otherwise relative order does not matter.
399
*/
400
static void
401
rcorder(NClist* rc)
402
0
{
403
0
    int i;
404
0
    int len = nclistlength(rc);
405
0
    NClist* tmprc = NULL;
406
0
    if(rc == NULL || len == 0) return;
407
0
    tmprc = nclistnew();
408
    /* Two passes: 1) pull entries with host */
409
0
    for(i=0;i<len;i++) {
410
0
        NCRCentry* ti = nclistget(rc,i);
411
0
  if(ti->host == NULL) continue;
412
0
  nclistpush(tmprc,ti);
413
0
    }
414
    /* pass 2 pull entries without host*/
415
0
    for(i=0;i<len;i++) {
416
0
        NCRCentry* ti = nclistget(rc,i);
417
0
  if(ti->host != NULL) continue;
418
0
  nclistpush(tmprc,ti);
419
0
    }
420
    /* Move tmp to rc */
421
0
    nclistsetlength(rc,0);
422
0
    for(i=0;i<len;i++) {
423
0
        NCRCentry* ti = nclistget(tmprc,i);
424
0
  nclistpush(rc,ti);
425
0
    }
426
#ifdef DRCDEBUG
427
    storedump("reorder:",rc);
428
#endif
429
0
    nclistfree(tmprc);
430
0
}
431
432
/* Merge a entry store from a file*/
433
static int
434
rccompile(const char* filepath)
435
0
{
436
0
    int ret = NC_NOERR;
437
0
    NClist* rc = NULL;
438
0
    char* contents = NULL;
439
0
    NCbytes* tmp = ncbytesnew();
440
0
    NCURI* uri = NULL;
441
0
    char* nextline = NULL;
442
0
    NCglobalstate* globalstate = NC_getglobalstate();
443
0
    char* bucket = NULL;
444
445
0
    if((ret=NC_readfile(filepath,tmp))) {
446
0
        nclog(NCLOGWARN, "Could not open configuration file: %s",filepath);
447
0
  goto done;
448
0
    }
449
0
    contents = ncbytesextract(tmp);
450
0
    if(contents == NULL) contents = strdup("");
451
    /* Either reuse or create new  */
452
0
    rc = globalstate->rcinfo->entries;
453
0
    if(rc == NULL) {
454
0
        rc = nclistnew();
455
0
        globalstate->rcinfo->entries = rc;
456
0
    }
457
0
    nextline = contents;
458
0
    for(;;) {
459
0
  char* line;
460
0
  char* key = NULL;
461
0
        char* value = NULL;
462
0
        char* host = NULL;
463
0
        char* urlpath = NULL;
464
0
  size_t llen;
465
0
        NCRCentry* entry;
466
467
0
  line = rcreadline(&nextline);
468
0
  if(line == NULL) break; /* done */
469
0
        rctrim(line);  /* trim leading and trailing blanks */
470
0
        if(line[0] == '#') continue; /* comment */
471
0
  if((llen=strlen(line)) == 0) continue; /* empty line */
472
0
  if(line[0] == LTAG) {
473
0
      char* url = ++line;
474
0
            char* rtag = strchr(line,RTAG);
475
0
            if(rtag == NULL) {
476
0
                nclog(NCLOGERR, "Malformed [url] in %s entry: %s",filepath,line);
477
0
    continue;
478
0
            }
479
0
            line = rtag + 1;
480
0
            *rtag = '\0';
481
            /* compile the url and pull out the host and protocol */
482
0
            if(uri) ncurifree(uri);
483
0
            if(ncuriparse(url,&uri)) {
484
0
                nclog(NCLOGERR, "Malformed [url] in %s entry: %s",filepath,line);
485
0
    continue;
486
0
            }
487
0
      { NCURI* newuri = NULL;
488
          /* Rebuild the url to S3 "path" format */
489
0
          nullfree(bucket);
490
0
          if((ret = NC_s3urlrebuild(uri,&newuri,&bucket,NULL))) goto done;
491
0
    ncurifree(uri);
492
0
    uri = newuri;
493
0
    newuri = NULL;
494
0
      }
495
      /* Get the host+port */
496
0
            ncbytesclear(tmp);
497
0
            ncbytescat(tmp,uri->host);
498
0
            if(uri->port != NULL) {
499
0
    ncbytesappend(tmp,':');
500
0
                ncbytescat(tmp,uri->port);
501
0
            }
502
0
            ncbytesnull(tmp);
503
0
            host = ncbytesextract(tmp);
504
0
      if(strlen(host)==0) /* nullify host */
505
0
    {free(host); host = NULL;}
506
      /* Get the url path part */
507
0
      urlpath = uri->path;
508
0
      if(urlpath && strlen(urlpath)==0) urlpath = NULL; /* nullify */
509
0
  }
510
        /* split off key and value */
511
0
        key=line;
512
0
        value = strchr(line, '=');
513
0
        if(value == NULL)
514
0
            value = line + strlen(line);
515
0
        else {
516
0
            *value = '\0';
517
0
            value++;
518
0
        }
519
  /* See if key already exists */
520
0
  entry = rclocate(key,host,urlpath);
521
0
  if(entry == NULL) {
522
0
      entry = (NCRCentry*)calloc(1,sizeof(NCRCentry));
523
0
      if(entry == NULL) {ret = NC_ENOMEM; goto done;}
524
0
      nclistpush(rc,entry);
525
0
      entry->host = host; host = NULL;
526
0
      entry->urlpath = nulldup(urlpath);
527
0
          entry->key = nulldup(key);
528
0
            rctrim(entry->host);
529
0
            rctrim(entry->urlpath);
530
0
            rctrim(entry->key);
531
0
  }
532
0
  nullfree(entry->value);
533
0
        entry->value = nulldup(value);
534
0
        rctrim(entry->value);
535
536
#ifdef DRCDEBUG
537
  fprintf(stderr,"rc: host=%s urlpath=%s key=%s value=%s\n",
538
    (entry->host != NULL ? entry->host : "<null>"),
539
    (entry->urlpath != NULL ? entry->urlpath : "<null>"),
540
    entry->key,entry->value);
541
#endif
542
0
  entry = NULL;
543
0
    }
544
#ifdef DRCDEBUG
545
    fprintf(stderr,"reorder.path=%s\n",filepath);
546
#endif
547
0
    rcorder(rc);
548
549
0
done:
550
0
    if(contents) free(contents);
551
0
    ncurifree(uri);
552
0
    ncbytesfree(tmp);
553
0
    return (ret);
554
0
}
555
556
/**
557
Encapsulate equality comparison: return 1|0
558
*/
559
static int
560
rcequal(NCRCentry* e1, NCRCentry* e2)
561
0
{
562
0
    int nulltest;
563
0
    if(e1->key == NULL || e2->key == NULL) return 0;
564
0
    if(strcmp(e1->key,e2->key) != 0) return 0;
565
    /* test hostport; take NULL into account*/
566
0
    nulltest = 0;
567
0
    if(e1->host == NULL) nulltest |= 1;
568
0
    if(e2->host == NULL) nulltest |= 2;
569
0
    switch (nulltest) {
570
0
    case 0: if(strcmp(e1->host,e2->host) != 0) {return 0;}  break;
571
0
    case 1: return 0;
572
0
    case 2: return 0;
573
0
    case 3: break;
574
0
    default: return 0;
575
0
    }
576
    /* test urlpath take NULL into account*/
577
0
    nulltest = 0;
578
0
    if(e1->urlpath == NULL) nulltest |= 1;
579
0
    if(e2->urlpath == NULL) nulltest |= 2;
580
0
    switch (nulltest) {
581
0
    case 0: if(strcmp(e1->urlpath,e2->urlpath) != 0) {return 0;} break;
582
0
    case 1: return 0;
583
0
    case 2: return 0;
584
0
    case 3: break;
585
0
    default: return 0;
586
0
    }
587
0
    return 1;
588
0
}
589
590
/**
591
 * (Internal) Locate a entry by property key and host+port (may be null) and urlpath (may be null)
592
 * If duplicate keys, first takes precedence.
593
 */
594
static int
595
rclocatepos(const char* key, const char* hostport, const char* urlpath)
596
1
{
597
1
    int i;
598
1
    NCglobalstate* globalstate = NC_getglobalstate();
599
1
    struct NCRCinfo* info = globalstate->rcinfo;
600
1
    NCRCentry* entry = NULL;
601
1
    NCRCentry candidate;
602
1
    NClist* rc = info->entries;
603
604
1
    if(info->ignore) return -1;
605
606
1
    candidate.key = (char*)key;
607
1
    candidate.value = (char*)NULL;
608
1
    candidate.host = (char*)hostport;
609
1
    candidate.urlpath = (char*)urlpath;
610
611
1
    for(i=0;i<nclistlength(rc);i++) {
612
0
      entry = (NCRCentry*)nclistget(rc,i);
613
0
      if(rcequal(entry,&candidate)) return i;
614
0
    }
615
1
    return -1;
616
1
}
617
618
/**
619
 * (Internal) Locate a entry by property key and host+port (may be null or "").
620
 * If duplicate keys, first takes precedence.
621
 */
622
static struct NCRCentry*
623
rclocate(const char* key, const char* hostport, const char* urlpath)
624
1
{
625
1
    int pos;
626
1
    NCglobalstate* globalstate = NC_getglobalstate();
627
1
    struct NCRCinfo* info = globalstate->rcinfo;
628
629
1
    if(globalstate->rcinfo->ignore) return NULL;
630
1
    if(key == NULL || info == NULL) return NULL;
631
1
    pos = rclocatepos(key,hostport,urlpath);
632
1
    if(pos < 0) return NULL;
633
0
    return NC_rcfile_ith(info,(size_t)pos);
634
1
}
635
636
/**
637
 * Locate rc file by searching in directory prefix.
638
 */
639
static
640
int
641
rcsearch(const char* prefix, const char* rcname, char** pathp)
642
6
{
643
6
    char* path = NULL;
644
6
    FILE* f = NULL;
645
6
    size_t plen = (prefix?strlen(prefix):0);
646
6
    size_t rclen = strlen(rcname);
647
6
    int ret = NC_NOERR;
648
649
6
    size_t pathlen = plen+rclen+1+1; /*+1 for '/' +1 for nul */
650
6
    path = (char*)malloc(pathlen); /* +1 for nul*/
651
6
    if(path == NULL) {ret = NC_ENOMEM; goto done;}
652
6
    snprintf(path, pathlen, "%s/%s", prefix, rcname);
653
    /* see if file is readable */
654
6
    f = NCfopen(path,"r");
655
6
    if(f != NULL)
656
0
        nclog(NCLOGDBG, "Found rc file=%s",path);
657
6
done:
658
6
    if(f == NULL || ret != NC_NOERR) {
659
6
  nullfree(path);
660
6
  path = NULL;
661
6
    }
662
6
    if(f != NULL)
663
0
      fclose(f);
664
6
    if(pathp != NULL)
665
6
      *pathp = path;
666
0
    else {
667
0
      nullfree(path);
668
0
      path = NULL;
669
0
    }
670
6
    errno = 0; /* silently ignore errors */
671
6
    return (ret);
672
6
}
673
674
int
675
NC_rcfile_insert(const char* key, const char* hostport, const char* urlpath, const char* value)
676
0
{
677
0
    int ret = NC_NOERR;
678
    /* See if this key already defined */
679
0
    struct NCRCentry* entry = NULL;
680
0
    NCglobalstate* globalstate = NULL;
681
0
    NClist* rc = NULL;
682
683
0
    if(!NCRCinitialized) ncrc_initialize();
684
685
0
    if(key == NULL || value == NULL)
686
0
        {ret = NC_EINVAL; goto done;}
687
688
0
    globalstate = NC_getglobalstate();
689
0
    rc = globalstate->rcinfo->entries;
690
691
0
    if(rc == NULL) {
692
0
  rc = nclistnew();
693
0
        globalstate->rcinfo->entries = rc;
694
0
  if(rc == NULL) {ret = NC_ENOMEM; goto done;}
695
0
    }
696
0
    entry = rclocate(key,hostport,urlpath);
697
0
    if(entry == NULL) {
698
0
  entry = (NCRCentry*)calloc(1,sizeof(NCRCentry));
699
0
  if(entry == NULL) {ret = NC_ENOMEM; goto done;}
700
0
  entry->key = strdup(key);
701
0
  entry->value = NULL;
702
0
        rctrim(entry->key);
703
0
        entry->host = nulldup(hostport);
704
0
        rctrim(entry->host);
705
0
        entry->urlpath = nulldup(urlpath);
706
0
        rctrim(entry->urlpath);
707
0
  nclistpush(rc,entry);
708
0
    }
709
0
    if(entry->value != NULL) free(entry->value);
710
0
    entry->value = strdup(value);
711
0
    rctrim(entry->value);
712
#ifdef DRCDEBUG
713
    storedump("NC_rcfile_insert",rc);
714
#endif    
715
0
done:
716
0
    return ret;
717
0
}
718
719
/* Obtain the count of number of entries */
720
size_t
721
NC_rcfile_length(NCRCinfo* info)
722
0
{
723
0
    return nclistlength(info->entries);
724
0
}
725
726
/* Obtain the ith entry; return NULL if out of range */
727
NCRCentry*
728
NC_rcfile_ith(NCRCinfo* info, size_t i)
729
0
{
730
0
    if(i >= nclistlength(info->entries))
731
0
  return NULL;
732
0
    return (NCRCentry*)nclistget(info->entries,i);
733
0
}
734
735
736
#ifdef DRCDEBUG
737
static void
738
storedump(char* msg, NClist* entries)
739
{
740
    int i;
741
742
    if(msg != NULL) fprintf(stderr,"%s\n",msg);
743
    if(entries == NULL || nclistlength(entries)==0) {
744
        fprintf(stderr,"<EMPTY>\n");
745
        return;
746
    }
747
    for(i=0;i<nclistlength(entries);i++) {
748
  NCRCentry* t = (NCRCentry*)nclistget(entries,i);
749
        fprintf(stderr,"\t%s\t%s\t%s\n",
750
                ((t->host == NULL || strlen(t->host)==0)?"--":t->host),t->key,t->value);
751
    }
752
    fflush(stderr);
753
}
754
#endif
755
756
/**************************************************/
757
/*
758
Get the current active profile. The priority order is as follows:
759
1. aws.profile key in mode flags
760
2. aws.profile in .rc entries
761
4. "default"
762
763
@param uri uri with mode flags, may be NULL
764
@param profilep return profile name here or NULL if none found
765
@return NC_NOERR if no error.
766
@return NC_EINVAL if something else went wrong.
767
*/
768
769
int
770
NC_getactives3profile(NCURI* uri, const char** profilep)
771
0
{
772
0
    int stat = NC_NOERR;
773
0
    const char* profile = NULL;
774
775
0
    profile = ncurifragmentlookup(uri,"aws.profile");
776
0
    if(profile == NULL)
777
0
        profile = NC_rclookupx(uri,"AWS.PROFILE");
778
0
    if(profile == NULL)
779
0
        profile = "default";
780
#ifdef AWSDEBUG
781
    fprintf(stderr,">>> activeprofile = %s\n",(profile?profile:"null"));
782
#endif
783
0
    if(profilep) *profilep = profile;
784
0
    return stat;
785
0
}
786
787
/*
788
Get the current default region. The search order is as follows:
789
1. aws.region key in mode flags
790
2. aws.region in .rc entries
791
3. aws_region key in current profile (only if profiles are being used)
792
4. "us-east-1"
793
794
@param uri uri with mode flags, may be NULL
795
@param regionp return region name here or NULL if none found
796
@return NC_NOERR if no error.
797
@return NC_EINVAL if something else went wrong.
798
*/
799
800
int
801
NC_getdefaults3region(NCURI* uri, const char** regionp)
802
0
{
803
0
    int stat = NC_NOERR;
804
0
    const char* region = NULL;
805
0
    const char* profile = NULL;
806
807
0
    region = ncurifragmentlookup(uri,"aws.region");
808
0
    if(region == NULL)
809
0
        region = NC_rclookupx(uri,"AWS.REGION");
810
0
    if(region == NULL) {/* See if we can find a profile */
811
0
        if((stat = NC_getactives3profile(uri,&profile))==NC_NOERR) {
812
0
      if(profile)
813
0
          (void)NC_s3profilelookup(profile,"aws_region",&region);
814
0
  }
815
0
    }
816
0
    if(region == NULL)
817
0
        region = "us-east-1";
818
#ifdef AWSDEBUG
819
    fprintf(stderr,">>> activeregion = %s\n",(region?region:"null"));
820
#endif
821
0
    if(regionp) *regionp = region;
822
0
    return stat;
823
0
}
824
825
/**
826
The .aws/config and .aws/credentials files
827
are in INI format (https://en.wikipedia.org/wiki/INI_file).
828
This format is not well defined, so the grammar used
829
here is restrictive. Here, the term "profile" is the same
830
as the INI term "section".
831
832
The grammar used is as follows:
833
834
Grammar:
835
836
inifile: profilelist ;
837
profilelist: profile | profilelist profile ;
838
profile: '[' profilename ']' EOL entries ;
839
entries: empty | entries entry ;
840
entry:  WORD = WORD EOL ;
841
profilename: WORD ;
842
Lexical:
843
WORD    sequence of printable characters - [ \[\]=]+
844
EOL '\n' | ';'
845
846
Note:
847
1. The semicolon at beginning of a line signals a comment.
848
2. # comments are not allowed
849
3. Duplicate profiles or keys are ignored.
850
4. Escape characters are not supported.
851
*/
852
853
0
#define AWS_EOF (-1)
854
0
#define AWS_ERR (0)
855
0
#define AWS_WORD (0x10001)
856
0
#define AWS_EOL (0x10002)
857
858
#ifdef LEXDEBUG
859
static const char*
860
tokenname(int token)
861
{
862
    static char num[32];
863
    switch(token) {
864
    case AWS_EOF: return "EOF";
865
    case AWS_ERR: return "ERR";
866
    case AWS_WORD: return "WORD";
867
    default: snprintf(num,sizeof(num),"%d",token); return num;
868
    }
869
    return "UNKNOWN";
870
}
871
#endif
872
873
typedef struct AWSparser {
874
    char* text;
875
    char* pos;
876
    size_t yylen; /* |yytext| */
877
    NCbytes* yytext;
878
    int token; /* last token found */
879
    int pushback; /* allow 1-token pushback */
880
} AWSparser;
881
882
static int
883
awslex(AWSparser* parser)
884
0
{
885
0
    int c;
886
0
    int token = 0;
887
0
    char* start;
888
0
    size_t count;
889
890
0
    parser->token = AWS_ERR;
891
0
    ncbytesclear(parser->yytext);
892
0
    ncbytesnull(parser->yytext);
893
894
0
    if(parser->pushback != AWS_ERR) {
895
0
  token = parser->pushback;
896
0
  parser->pushback = AWS_ERR;
897
0
  goto done;
898
0
    }
899
900
0
    while(token == 0) { /* avoid need to goto when retrying */
901
0
  c = *parser->pos;
902
0
  if(c == '\0') {
903
0
      token = AWS_EOF;
904
0
  } else if(c == '\n') {
905
0
      parser->pos++;
906
0
      token = AWS_EOL;
907
0
  } else if(c <= ' ' || c == '\177') {
908
0
      parser->pos++;
909
0
      continue; /* ignore whitespace */
910
0
  } else if(c == ';') {
911
0
      char* p = parser->pos - 1;
912
0
      if(*p == '\n') {
913
          /* Skip comment */
914
0
          do {p++;} while(*p != '\n' && *p != '\0');
915
0
          parser->pos = p;
916
0
          token = (*p == '\n'?AWS_EOL:AWS_EOF);
917
0
      } else {
918
0
          token = ';';
919
0
          ncbytesappend(parser->yytext,';');
920
0
    parser->pos++;
921
0
      }
922
0
  } else if(c == '[' || c == ']' || c == '=') {
923
0
      ncbytesappend(parser->yytext,c);
924
0
          ncbytesnull(parser->yytext);
925
0
      token = c;
926
0
      parser->pos++;
927
0
  } else { /*Assume a word*/
928
0
      start = parser->pos;
929
0
      for(;;) {
930
0
    c = *parser->pos++;
931
0
          if(c <= ' ' || c == '\177' || c == '[' || c == ']' || c == '=') break; /* end of word */
932
0
      }
933
      /* Pushback last char */
934
0
      parser->pos--;
935
0
      count = ((parser->pos) - start);
936
0
      ncbytesappendn(parser->yytext,start,count);
937
0
      ncbytesnull(parser->yytext);
938
0
      token = AWS_WORD;
939
0
  }
940
#ifdef LEXDEBUG
941
fprintf(stderr,"%s(%d): |%s|\n",tokenname(token),token,ncbytescontents(parser->yytext));
942
#endif
943
0
    } /*for(;;)*/
944
945
0
done:
946
0
    parser->token = token;
947
0
    return token;
948
0
}
949
950
/*
951
@param text of the aws credentials file
952
@param profiles list of form struct AWSprofile (see ncauth.h)
953
*/
954
955
0
#define LBR '['
956
0
#define RBR ']'
957
958
static int
959
awsparse(const char* text, NClist* profiles)
960
0
{
961
0
    int i,stat = NC_NOERR;
962
0
    size_t len;
963
0
    AWSparser* parser = NULL;
964
0
    struct AWSprofile* profile = NULL;
965
0
    int token;
966
0
    char* key = NULL;
967
0
    char* value = NULL;
968
969
0
    if(text == NULL) text = "";
970
971
0
    parser = calloc(1,sizeof(AWSparser));
972
0
    if(parser == NULL)
973
0
  {stat = (NC_ENOMEM); goto done;}
974
0
    len = strlen(text);
975
0
    parser->text = (char*)malloc(len+1+1+1); /* double nul term plus leading EOL */
976
0
    if(parser->text == NULL)
977
0
  {stat = (NCTHROW(NC_EINVAL)); goto done;}
978
0
    parser->pos = parser->text;
979
0
    parser->pos[0] = '\n'; /* So we can test for comment unconditionally */
980
0
    parser->pos++;
981
0
    strcpy(parser->text+1,text);
982
0
    parser->pos += len;
983
    /* Double nul terminate */
984
0
    parser->pos[0] = '\0';
985
0
    parser->pos[1] = '\0';
986
0
    parser->pos = &parser->text[0]; /* reset */
987
0
    parser->yytext = ncbytesnew();
988
0
    parser->pushback = AWS_ERR;
989
990
    /* Do not need recursion, use simple loops */
991
0
    for(;;) {
992
0
        token = awslex(parser); /* make token always be defined */
993
0
  if(token ==  AWS_EOF) break; /* finished */
994
0
  if(token ==  AWS_EOL) {continue;} /* blank line */
995
0
  if(token != LBR) {stat = NCTHROW(NC_EINVAL); goto done;}
996
  /* parse [profile name] */
997
0
        token = awslex(parser);
998
0
  if(token != AWS_WORD) {stat = NCTHROW(NC_EINVAL); goto done;}
999
0
  assert(profile == NULL);
1000
0
  if((profile = (struct AWSprofile*)calloc(1,sizeof(struct AWSprofile)))==NULL)
1001
0
      {stat = NC_ENOMEM; goto done;}
1002
0
  profile->name = ncbytesextract(parser->yytext);
1003
0
  profile->entries = nclistnew();
1004
0
        token = awslex(parser);
1005
0
  if(token != RBR) {stat = NCTHROW(NC_EINVAL); goto done;}
1006
#ifdef PARSEDEBUG
1007
fprintf(stderr,">>> parse: profile=%s\n",profile->name);
1008
#endif
1009
  /* The fields can be in any order */
1010
0
  for(;;) {
1011
0
      struct AWSentry* entry = NULL;
1012
0
            token = awslex(parser);
1013
0
      if(token == AWS_EOL) {
1014
0
          continue; /* ignore empty lines */
1015
0
      } else if(token == AWS_EOF) {
1016
0
          break;
1017
0
      } else if(token == LBR) {/* start of next profile */
1018
0
          parser->pushback = token;
1019
0
    break;
1020
0
      } else if(token ==  AWS_WORD) {
1021
0
        key = ncbytesextract(parser->yytext);
1022
0
    token = awslex(parser);
1023
0
          if(token != '=') {stat = NCTHROW(NC_EINVAL); goto done;}
1024
0
          token = awslex(parser);
1025
0
    if(token != AWS_EOL && token != AWS_WORD) {stat = NCTHROW(NC_EINVAL); goto done;}
1026
0
          value = ncbytesextract(parser->yytext);
1027
0
          if((entry = (struct AWSentry*)calloc(1,sizeof(struct AWSentry)))==NULL)
1028
0
              {stat = NC_ENOMEM; goto done;}
1029
0
          entry->key = key; key = NULL;
1030
0
              entry->value = value; value = NULL;
1031
#ifdef PARSEDEBUG
1032
fprintf(stderr,">>> parse: entry=(%s,%s)\n",entry->key,entry->value);
1033
#endif
1034
0
    nclistpush(profile->entries,entry); entry = NULL;
1035
0
    if(token == AWS_WORD) token = awslex(parser); /* finish the line */
1036
0
      } else
1037
0
          {stat = NCTHROW(NC_EINVAL); goto done;}
1038
0
  }
1039
1040
  /* If this profile already exists, then ignore new one */
1041
0
  for(i=0;i<nclistlength(profiles);i++) {
1042
0
      struct AWSprofile* p = (struct AWSprofile*)nclistget(profiles,i);
1043
0
      if(strcasecmp(p->name,profile->name)==0) {
1044
    /* reclaim and ignore */
1045
0
    freeprofile(profile);
1046
0
    profile = NULL;
1047
0
    break;
1048
0
      }
1049
0
  }
1050
0
  if(profile) nclistpush(profiles,profile);
1051
0
  profile = NULL;
1052
0
    }
1053
1054
0
done:
1055
0
    if(profile) freeprofile(profile);
1056
0
    nullfree(key);
1057
0
    nullfree(value);
1058
0
    if(parser != NULL) {
1059
0
  nullfree(parser->text);
1060
0
  ncbytesfree(parser->yytext);
1061
0
  free(parser);
1062
0
    }
1063
0
    return (stat);
1064
0
}
1065
1066
static void
1067
freeentry(struct AWSentry* e)
1068
0
{
1069
0
    if(e) {
1070
#ifdef AWSDEBUG
1071
fprintf(stderr,">>> freeentry: key=%s value=%s\n",e->key,e->value);
1072
#endif
1073
0
        nullfree(e->key);
1074
0
        nullfree(e->value);
1075
0
        nullfree(e);
1076
0
    }
1077
0
}
1078
1079
static void
1080
freeprofile(struct AWSprofile* profile)
1081
1
{
1082
1
    if(profile) {
1083
1
  int i;
1084
#ifdef AWSDEBUG
1085
fprintf(stderr,">>> freeprofile: %s\n",profile->name);
1086
#endif
1087
1
  for(i=0;i<nclistlength(profile->entries);i++) {
1088
0
      struct AWSentry* e = (struct AWSentry*)nclistget(profile->entries,i);
1089
0
      freeentry(e);
1090
0
  }
1091
1
        nclistfree(profile->entries);
1092
1
  nullfree(profile->name);
1093
1
  nullfree(profile);
1094
1
    }
1095
1
}
1096
1097
static void
1098
freeprofilelist(NClist* profiles)
1099
3
{
1100
3
    if(profiles) {
1101
2
  int i;
1102
3
  for(i=0;i<nclistlength(profiles);i++) {
1103
1
      struct AWSprofile* p = (struct AWSprofile*)nclistget(profiles,i);
1104
1
      freeprofile(p);
1105
1
  }
1106
2
  nclistfree(profiles);
1107
2
    }
1108
3
}
1109
1110
/* Find, load, and parse the aws credentials file */
1111
static int
1112
aws_load_credentials(NCglobalstate* gstate)
1113
1
{
1114
1
    int stat = NC_NOERR;
1115
1
    NClist* profiles = nclistnew();
1116
1
    const char** awscfg = awsconfigfiles;
1117
1
    const char* aws_root = getenv(NC_TEST_AWS_DIR);
1118
1
    NCbytes* buf = ncbytesnew();
1119
1
    char path[8192];
1120
1121
3
    for(;*awscfg;awscfg++) {
1122
        /* Construct the path ${HOME}/<file> or Windows equivalent. */
1123
2
  const char* cfg = *awscfg;
1124
1125
2
        snprintf(path,sizeof(path),"%s%s%s",
1126
2
      (aws_root?aws_root:gstate->home),
1127
2
      (*cfg == '/'?"":"/"),
1128
2
      cfg);
1129
2
  ncbytesclear(buf);
1130
2
        if((stat=NC_readfile(path,buf))) {
1131
2
            nclog(NCLOGWARN, "Could not open file: %s",path);
1132
2
        } else {
1133
            /* Parse the credentials file */
1134
0
      const char* text = ncbytescontents(buf);
1135
0
            if((stat = awsparse(text,profiles))) goto done;
1136
0
  }
1137
2
    }
1138
1139
    /* add a "none" credentials */
1140
1
    {
1141
1
  struct AWSprofile* noprof = (struct AWSprofile*)calloc(1,sizeof(struct AWSprofile));
1142
1
  noprof->name = strdup("none");
1143
1
  noprof->entries = nclistnew();
1144
1
  nclistpush(profiles,noprof); noprof = NULL;
1145
1
    }
1146
1147
1
    if(gstate->rcinfo->s3profiles)
1148
1
        freeprofilelist(gstate->rcinfo->s3profiles);
1149
1
    gstate->rcinfo->s3profiles = profiles; profiles = NULL;
1150
1151
#ifdef AWSDEBUG
1152
    {int i,j;
1153
  fprintf(stderr,">>> profiles:\n");
1154
  for(i=0;i<nclistlength(creds->profiles);i++) {
1155
      struct AWSprofile* p = (struct AWSprofile*)nclistget(creds->profiles,i);
1156
      fprintf(stderr,"    [%s]",p->name);
1157
      for(j=0;j<nclistlength(p->entries);j++) {
1158
          struct AWSentry* e = (struct AWSentry*)nclistget(p->entries,j);
1159
    fprintf(stderr," %s=%s",e->key,e->value);
1160
      }
1161
            fprintf(stderr,"\n");
1162
  }
1163
    }
1164
#endif
1165
1166
1
done:
1167
1
    ncbytesfree(buf);
1168
1
    freeprofilelist(profiles);
1169
1
    return stat;
1170
1
}
1171
1172
int
1173
NC_authgets3profile(const char* profilename, struct AWSprofile** profilep)
1174
0
{
1175
0
    int stat = NC_NOERR;
1176
0
    int i = -1;
1177
0
    NCglobalstate* gstate = NC_getglobalstate();
1178
1179
0
    for(i=0;i<nclistlength(gstate->rcinfo->s3profiles);i++) {
1180
0
  struct AWSprofile* profile = (struct AWSprofile*)nclistget(gstate->rcinfo->s3profiles,i);
1181
0
  if(strcmp(profilename,profile->name)==0)
1182
0
      {if(profilep) {*profilep = profile; goto done;}}
1183
0
    }
1184
0
    if(profilep) *profilep = NULL; /* not found */
1185
0
done:
1186
0
    return stat;
1187
0
}
1188
1189
int
1190
NC_s3profilelookup(const char* profile, const char* key, const char** valuep)
1191
0
{
1192
0
    int i,stat = NC_NOERR;
1193
0
    struct AWSprofile* awsprof = NULL;
1194
0
    const char* value = NULL;
1195
1196
0
    if(profile == NULL) return NC_ES3;
1197
0
    stat = NC_authgets3profile(profile,&awsprof);
1198
0
    if(stat == NC_NOERR && awsprof != NULL) {
1199
0
        for(i=0;i<nclistlength(awsprof->entries);i++) {
1200
0
      struct AWSentry* entry = (struct AWSentry*)nclistget(awsprof->entries,i);
1201
0
      if(strcasecmp(entry->key,key)==0) {
1202
0
    value = entry->value;
1203
0
          break;
1204
0
      }
1205
0
  }
1206
0
    }
1207
0
    if(valuep) *valuep = value;
1208
0
    return stat;
1209
0
}