@@ -25,6 +25,8 @@ DEALINGS IN THE SOFTWARE. */
2525#include <config.h>
2626
2727#include <ctype.h>
28+ #include <stdarg.h>
29+ #include <stdio.h>
2830#include <stdlib.h>
2931#include <string.h>
3032#include <time.h>
@@ -697,6 +699,89 @@ static int is_dns_compliant(const char *s0, const char *slim)
697699 return has_nondigit && len >= 3 && len <= 63 ;
698700}
699701
702+ static FILE * expand_tilde_open (const char * fname , const char * mode )
703+ {
704+ FILE * fp ;
705+
706+ if (strncmp (fname , "~/" , 2 ) == 0 ) {
707+ kstring_t full_fname = { 0 , 0 , NULL };
708+ const char * home = getenv ("HOME" );
709+ if (! home ) return NULL ;
710+
711+ kputs (home , & full_fname );
712+ kputs (& fname [1 ], & full_fname );
713+
714+ fp = fopen (full_fname .s , mode );
715+ free (full_fname .s );
716+ }
717+ else
718+ fp = fopen (fname , mode );
719+
720+ return fp ;
721+ }
722+
723+ static void parse_ini (const char * fname , const char * section , ...)
724+ {
725+ kstring_t line = { 0 , 0 , NULL };
726+ int active = 1 ; // Start active, so global properties are accepted
727+ char * s ;
728+
729+ FILE * fp = expand_tilde_open (fname , "r" );
730+ if (fp == NULL ) return ;
731+
732+ while (line .l = 0 , kgetline (& line , (kgets_func * ) fgets , fp ) >= 0 )
733+ if (line .s [0 ] == '[' && (s = strchr (line .s , ']' )) != NULL ) {
734+ * s = '\0' ;
735+ active = (strcmp (& line .s [1 ], section ) == 0 );
736+ }
737+ else if (active && (s = strpbrk (line .s , ":=" )) != NULL ) {
738+ const char * key = line .s , * value = & s [1 ], * akey ;
739+ va_list args ;
740+
741+ while (isspace (* key )) key ++ ;
742+ while (s > key && isspace (s [-1 ])) s -- ;
743+ * s = '\0' ;
744+
745+ while (isspace (* value )) value ++ ;
746+ while (line .l > 0 && isspace (line .s [line .l - 1 ]))
747+ line .s [-- line .l ] = '\0' ;
748+
749+ va_start (args , section );
750+ while ((akey = va_arg (args , const char * )) != NULL ) {
751+ kstring_t * avar = va_arg (args , kstring_t * );
752+ if (strcmp (key , akey ) == 0 ) { kputs (value , avar ); break ; }
753+ }
754+ va_end (args );
755+ }
756+
757+ fclose (fp );
758+ free (line .s );
759+ }
760+
761+ static void parse_simple (const char * fname , kstring_t * id , kstring_t * secret )
762+ {
763+ kstring_t text = { 0 , 0 , NULL };
764+ char * s ;
765+ size_t len ;
766+
767+ FILE * fp = expand_tilde_open (fname , "r" );
768+ if (fp == NULL ) return ;
769+
770+ while (kgetline (& text , (kgets_func * ) fgets , fp ) >= 0 )
771+ kputc (' ' , & text );
772+ fclose (fp );
773+
774+ s = text .s ;
775+ while (isspace (* s )) s ++ ;
776+ kputsn (s , len = strcspn (s , " \t" ), id );
777+
778+ s += len ;
779+ while (isspace (* s )) s ++ ;
780+ kputsn (s , strcspn (s , " \t" ), secret );
781+
782+ free (text .s );
783+ }
784+
700785static int
701786add_s3_settings (hFILE_libcurl * fp , const char * s3url , kstring_t * message )
702787{
@@ -706,8 +791,11 @@ add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
706791 CURLcode err ;
707792
708793 kstring_t url = { 0 , 0 , NULL };
794+ kstring_t profile = { 0 , 0 , NULL };
709795 kstring_t id = { 0 , 0 , NULL };
710796 kstring_t secret = { 0 , 0 , NULL };
797+ kstring_t token = { 0 , 0 , NULL };
798+ kstring_t token_hdr = { 0 , 0 , NULL };
711799 kstring_t auth_hdr = { 0 , 0 , NULL };
712800
713801 time_t now = time (NULL );
@@ -723,7 +811,7 @@ add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
723811 kputs (& date_hdr [6 ], message );
724812 kputc ('\n' , message );
725813
726- // Our S3 URL format is s3[+SCHEME]://[ID[:SECRET]@]BUCKET/PATH
814+ // Our S3 URL format is s3[+SCHEME]://[ID[:SECRET[:TOKEN] ]@]BUCKET/PATH
727815
728816 if (s3url [2 ] == '+' ) {
729817 bucket = strchr (s3url , ':' ) + 1 ;
@@ -737,10 +825,17 @@ add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
737825
738826 path = bucket + strcspn (bucket , "/?#@" );
739827 if (* path == '@' ) {
740- const char * colon = bucket + strcspn (bucket , ":@" );
741- urldecode_kput (bucket , colon - bucket , fp , & id );
742- if (* colon == ':' )
743- urldecode_kput (& colon [1 ], path - & colon [1 ], fp , & secret );
828+ const char * colon = strpbrk (bucket , ":@" );
829+ if (* colon != ':' ) {
830+ urldecode_kput (bucket , colon - bucket , fp , & profile );
831+ }
832+ else {
833+ const char * colon2 = strpbrk (& colon [1 ], ":@" );
834+ urldecode_kput (bucket , colon - bucket , fp , & id );
835+ urldecode_kput (& colon [1 ], colon2 - & colon [1 ], fp , & secret );
836+ if (* colon2 == ':' )
837+ urldecode_kput (& colon2 [1 ], path - & colon2 [1 ], fp , & token );
838+ }
744839
745840 bucket = & path [1 ];
746841 path = bucket + strcspn (bucket , "/?#" );
@@ -750,6 +845,11 @@ add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
750845 const char * v ;
751846 if ((v = getenv ("AWS_ACCESS_KEY_ID" )) != NULL ) kputs (v , & id );
752847 if ((v = getenv ("AWS_SECRET_ACCESS_KEY" )) != NULL ) kputs (v , & secret );
848+ if ((v = getenv ("AWS_SESSION_TOKEN" )) != NULL ) kputs (v , & token );
849+
850+ if ((v = getenv ("AWS_DEFAULT_PROFILE" )) != NULL ) kputs (v , & profile );
851+ else if ((v = getenv ("AWS_PROFILE" )) != NULL ) kputs (v , & profile );
852+ else kputs ("default" , & profile );
753853 }
754854
755855 // Use virtual hosted-style access if possible, otherwise path-style.
@@ -763,14 +863,34 @@ add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
763863 }
764864 kputs (path , & url );
765865
866+ if (id .l == 0 ) {
867+ const char * v = getenv ("AWS_SHARED_CREDENTIALS_FILE" );
868+ parse_ini (v ? v : "~/.aws/credentials" , profile .s ,
869+ "aws_access_key_id" , & id , "aws_secret_access_key" , & secret ,
870+ "aws_session_token" , & token , NULL );
871+ }
872+ if (id .l == 0 )
873+ parse_ini ("~/.s3cfg" , profile .s , "access_key" , & id ,
874+ "secret_key" , & secret , "access_token" , & token , NULL );
875+ if (id .l == 0 )
876+ parse_simple ("~/.awssecret" , & id , & secret );
877+
878+ if (token .l > 0 ) {
879+ kputs ("x-amz-security-token:" , message );
880+ kputs (token .s , message );
881+ kputc ('\n' , message );
882+
883+ kputs ("X-Amz-Security-Token: " , & token_hdr );
884+ kputs (token .s , & token_hdr );
885+ if (add_header (fp , token_hdr .s ) < 0 ) goto error ;
886+ }
887+
766888 kputc ('/' , message );
767889 kputs (bucket , message ); // CanonicalizedResource is '/' + bucket + path
768890
769891 err = curl_easy_setopt (fp -> easy , CURLOPT_URL , url .s );
770892 if (err != CURLE_OK ) { errno = easy_errno (fp -> easy , err ); goto error ; }
771893
772- // TODO Read id and secret from config files
773-
774894 // If we have no id/secret, we can't sign the request but will
775895 // still be able to access public data sets.
776896 if (id .l > 0 && secret .l > 0 ) {
@@ -794,8 +914,11 @@ add_s3_settings(hFILE_libcurl *fp, const char *s3url, kstring_t *message)
794914free_and_return :
795915 save = errno ;
796916 free (url .s );
917+ free (profile .s );
797918 free (id .s );
798919 free (secret .s );
920+ free (token .s );
921+ free (token_hdr .s );
799922 free (auth_hdr .s );
800923 free (message -> s );
801924 errno = save ;
0 commit comments