Coverage Report

Created: 2023-05-28 06:42

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