src/platform_unix.c
author Claudio Luck <claudio.luck@pep.foundation>
Mon, 20 May 2019 13:13:46 +0200
branchENGINE-524
changeset 3736 b28bcc5b8c0c
parent 3734 73fbd2821d46
child 3836 f7ec3ffc9487
permissions -rw-r--r--
WIP: ENGINE-524: use new fn signature
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #define _POSIX_C_SOURCE 200809L
     5 
     6 #ifdef ANDROID
     7 #ifndef __LP64__ 
     8 #include <time64.h>
     9 #endif
    10 #endif
    11 
    12 #include <time.h>
    13 #include <string.h>
    14 #include <stdlib.h>
    15 #include <assert.h>
    16 #include <sys/stat.h>
    17 #include <sys/types.h>
    18 #include <errno.h>
    19 #include <fcntl.h>
    20 #include <regex.h>
    21 
    22 #include "platform_unix.h"
    23 
    24 #define MAX_PATH 1024
    25 #ifndef LOCAL_DB_FILENAME
    26 #define LOCAL_DB_FILENAME "management.db"      /* ".pEp_" prefix now added in *_local_db() */
    27 #endif
    28 #ifndef LOCAL_KEYS_DB_FILENAME
    29 #define LOCAL_KEYS_DB_FILENAME "keys.db"
    30 #endif
    31 #define SYSTEM_DB_FILENAME "system.db"
    32 #ifndef SYSTEM_DB_PREFIX
    33 #define SYSTEM_DB_PREFIX "/usr/local/share/pEp"
    34 #endif
    35 
    36 #ifndef bool
    37 #define bool int
    38 #define true 1
    39 #define false 0
    40 #endif
    41 
    42 #ifdef ANDROID
    43 #include <uuid.h>
    44 
    45 /* FIXME : timegm will miss when linking for x86_64 on android, when supported */
    46 #ifndef __LP64__ 
    47 time_t timegm(struct tm* const t) {
    48     static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
    49     static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
    50     time64_t result = timegm64(t);
    51     if (result < kTimeMin || result > kTimeMax)
    52         return -1;
    53     return result;
    54 }
    55 #endif
    56 
    57 char *stpncpy(char *dst, const char *src, size_t n)
    58 {
    59     if (n != 0) {
    60         char *d = dst;
    61         const char *s = src;
    62 
    63         dst = &dst[n];
    64         do {
    65             if ((*d++ = *s++) == 0) {
    66                 dst = d - 1;
    67                 /* NUL pad the remaining n-1 bytes */
    68                 while (--n != 0)
    69                     *d++ = 0;
    70                 break;
    71             }
    72         } while (--n != 0);
    73     }
    74     return (dst);
    75 }
    76 
    77 char *stpcpy(char *dst, const char *src)
    78 {
    79     for (;; ++dst, ++src) {
    80         *dst = *src;
    81         if (*dst == 0)
    82             break;
    83     }
    84     return dst;
    85 }
    86 
    87 /*
    88 long int random(void)
    89 {
    90     static bool seeded = false;
    91     static unsigned short xsubi[3];
    92     if(!seeded)
    93     {
    94         const long long t = (long long)time(NULL);
    95         xsubi[0] = (unsigned short)t;
    96         xsubi[1] = (unsigned short)(t>>16);
    97         xsubi[2] = (unsigned short)(t>>32);
    98         seeded = true;
    99     }
   100 
   101     return nrand48(xsubi);
   102 } */
   103 
   104 const char *android_system_db(void)
   105 {
   106     static char buffer[MAX_PATH];
   107     static bool done = false;
   108 
   109     if (!done) {
   110         char *tw_env;
   111         if(tw_env = getenv("TRUSTWORDS")){
   112             char *p = stpncpy(buffer, tw_env, MAX_PATH);
   113             ssize_t len = MAX_PATH - (p - buffer) - 2;
   114 
   115             if (len < strlen(SYSTEM_DB_FILENAME)) {
   116                 assert(0);
   117                 return NULL;
   118             }
   119 
   120             *p++ = '/';
   121             strncpy(p, SYSTEM_DB_FILENAME, len);
   122             done = true;
   123         }else{
   124             return NULL;
   125         }
   126 
   127     }
   128     return buffer;
   129 }
   130 
   131 
   132 void uuid_generate_random(pEpUUID out)
   133 {
   134     uuid_t *uuid;
   135     uuid_rc_t rc_create;
   136     size_t size = sizeof(uuid_string_t);
   137     void *_out = out;
   138 	
   139     if ((rc_create = uuid_create(&uuid)) != UUID_RC_OK ||
   140         uuid_make(uuid, UUID_MAKE_V1) != UUID_RC_OK ||
   141         uuid_export(uuid, UUID_FMT_BIN, &_out, &size) != UUID_RC_OK)
   142     {
   143         memset(out, 0, sizeof(pEpUUID));
   144     }
   145 
   146     if (rc_create == UUID_RC_OK)
   147     {
   148         uuid_destroy(uuid);
   149     }
   150 }
   151 
   152 
   153 void uuid_unparse_upper(pEpUUID uu, uuid_string_t out)
   154 {
   155     uuid_t *uuid;
   156     uuid_rc_t rc_create;
   157     size_t size = sizeof(uuid_string_t);
   158     void *_out = out;
   159 
   160     if ((rc_create = uuid_create(&uuid)) != UUID_RC_OK ||
   161         uuid_import(uuid, UUID_FMT_BIN, uu, sizeof(pEpUUID)) != UUID_RC_OK ||
   162         uuid_export(uuid, UUID_FMT_STR, &_out, &size) != UUID_RC_OK)
   163     {
   164         memset(out, 0, sizeof(uuid_string_t));
   165     }
   166     else 
   167     {
   168         out[sizeof(uuid_string_t) - 1] = 0;
   169     }
   170 
   171     if (rc_create == UUID_RC_OK)
   172     {
   173         uuid_destroy(uuid);
   174     }
   175 }
   176 
   177 #endif
   178 
   179 #ifdef NDEBUG
   180 const char *unix_system_db(void)
   181 #else
   182 const char *unix_system_db(int reset)
   183 #endif
   184 {
   185     static char buffer[MAX_PATH];
   186     static bool done = false;
   187 
   188     #ifdef NDEBUG
   189     if (!done)
   190     #else
   191     if ((!done) || reset)
   192     #endif
   193     {
   194         const char *home_env;
   195         const char *subdir;
   196         const char * const confvars[] = { "TRUSTWORDS", "PEP_HOME", "PEPHOME", NULL,             "HOME",   NULL };
   197         const char * const confvals[] = { NULL,         NULL,       NULL,      SYSTEM_DB_PREFIX, NULL,     NULL };
   198         const char * const confsdir[] = { "/",          "/",        "/",       "/",              "/.pEp/", NULL };
   199         const bool automkdir[]        = { false,        true,       true,      false,            true,     false }; // use this on dirs only!
   200         const bool enforceifset[]     = { true,         false,      false,     false,            false,    false };
   201         int cf_i;
   202 
   203         for (cf_i = 0; confvars[cf_i] || confvals[cf_i]; cf_i++) {
   204             if (((home_env = confvals[cf_i]) || (home_env = getenv (confvars[cf_i]))) && (subdir = confsdir[cf_i])) {
   205                 char *p = stpncpy (buffer, home_env, MAX_PATH);
   206                 ssize_t len = MAX_PATH - (p - buffer) - 2;
   207 
   208                 if (len < strlen (SYSTEM_DB_FILENAME) + strlen (confsdir[cf_i])) {
   209                     assert(0);
   210                     return NULL;
   211                 }
   212 
   213                 p = stpncpy(p, confsdir[cf_i], len);
   214                 if (automkdir[cf_i]) {
   215                     if (mkdir(buffer, S_IRUSR | S_IWUSR | S_IXUSR) != 0 && errno != EEXIST) {
   216                         perror(SYSTEM_DB_FILENAME);
   217                         return false;
   218                     }
   219                 }
   220 
   221                 strncpy(p, SYSTEM_DB_FILENAME, len);
   222                 if (access (buffer, R_OK) == 0) {
   223                     done = true;
   224                     return buffer;
   225                 }
   226                 else if (enforceifset[cf_i])
   227                     return NULL;
   228             }
   229         }
   230         return NULL;
   231     }
   232     return buffer;
   233 }
   234 
   235 #if !defined(BSD) && !defined(__APPLE__)
   236 
   237 size_t strlcpy(char* dst, const	char* src, size_t size) {
   238     size_t retval = strlen(src);
   239     size_t size_to_copy = (retval < size ? retval : size - 1);
   240     
   241     // strlcpy doc says src and dst not allowed to overlap, as
   242     // it's undefined. So this is acceptable:
   243     memcpy((void*)dst, (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
   244     dst[size_to_copy] = '\0';
   245     return retval;
   246 }
   247 
   248 size_t strlcat(char* dst, const	char* src, size_t size) {
   249     size_t start_len = strnlen(dst, size);
   250     if (start_len == size)
   251         return size; // no copy, no null termination in size bytes, according to spec
   252     
   253     size_t add_len = strlen(src);
   254     size_t retval = start_len + add_len;
   255     size_t size_to_copy = (retval < size ? add_len : (size - start_len) - 1);
   256     
   257     // strlcat doc says src and dst not allowed to overlap, as
   258     // it's undefined. So this is acceptable:
   259     memcpy((void*)(dst + start_len), (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
   260     dst[start_len + size_to_copy] = '\0';
   261     return retval;
   262 }
   263 
   264 #ifdef USE_NETPGP
   265 // FIXME: This may cause problems - this is a quick compatibility fix for netpgp code
   266 int regnexec(const regex_t* preg, const char* string,
   267              size_t len, size_t nmatch, regmatch_t pmatch[], int eflags) {
   268     return regexec(preg, string, nmatch, pmatch, eflags);
   269 }
   270 #endif
   271 
   272 #endif
   273 
   274 #ifdef NDEBUG
   275 PEP_STATUS unix_local_db_file(const char *buffer, const char *fname)
   276 #else
   277 PEP_STATUS unix_local_db_file(const char *buffer, const char *fname, int reset)
   278 #endif
   279 {
   280     /*
   281      * TODO: move this to libpEpAdapter and create an have an export symbol here only.
   282      */
   283     const char *home_env;
   284     const char *subdir;
   285     /* Note: in HOME, a dot and pEp_ is prepended to the file (~/.pEp_management.db, vs ~/.pEp/management.db) */
   286     const char * const confvars[] = { "PEP_HOME", "PEPHOME", "HOME",   "HOME",   NULL };
   287     const char * const confvals[] = { NULL,       NULL,      NULL,     NULL,     NULL };
   288     const char * const confsdir[] = { "/",        "/",       "/.pEp_", "/.pEp/", NULL };
   289     const bool automkdir[]        = { true,       true,      false,    true,     false }; // use this on dirs only!
   290     const bool debugonly[]        = { true,       true,      false,    false,    false };
   291     const bool enforceifset[]     = { true,       true,      false,    true,     false };
   292     int cf_i;
   293 
   294     for (cf_i = 0; confvars[cf_i] || confvals[cf_i]; cf_i++) {
   295         if (((home_env = confvals[cf_i]) || (home_env = getenv (confvars[cf_i]))) && (subdir = confsdir[cf_i])) {
   296             char *p = stpncpy (buffer, home_env, MAX_PATH);
   297             ssize_t len = MAX_PATH - (p - buffer) - 1;
   298 
   299             if (len < strlen (fname) + strlen (confsdir[cf_i])) {
   300                 assert(0);
   301                 return PEP_OUT_OF_MEMORY;
   302             }
   303 
   304 #ifdef NDEBUG
   305             if (debugonly[cf_i] && enforceifset[cf_i]) {
   306                 printf("WARNING: Variable '%s' MUST NOT be used in production!\n", confvars[cf_i]);
   307                 // return PEP_INIT_CANNOT_OPEN_DB;
   308             }
   309 #endif
   310 
   311             p = stpncpy(p, confsdir[cf_i], len);
   312             if (automkdir[cf_i]) {
   313                 if (mkdir(buffer, S_IRUSR | S_IWUSR | S_IXUSR) != 0 && errno != EEXIST) {
   314                     printf("ERROR: mkdir '%s' FAIL %d\n", confvals[cf_i], errno);
   315                     return PEP_INIT_CANNOT_OPEN_DB;
   316                 }
   317             }
   318 
   319             strncpy(p, fname, len);
   320             if (enforceifset[cf_i] || (access (buffer, R_OK) == 0)) {
   321                 return PEP_STATUS_OK;
   322             }
   323         }
   324     }
   325     return PEP_UNKNOWN_DB_ERROR;
   326 }
   327 
   328 #ifdef NDEBUG
   329 const char *unix_local_db(void)
   330 #else
   331 const char *unix_local_db(int reset)
   332 #endif
   333 {
   334     static char buffer[MAX_PATH];
   335     static bool done = false;
   336 
   337     #ifdef NDEBUG
   338     if (!done)
   339         done = (PEP_STATUS_OK == unix_local_db_file(buffer, LOCAL_DB_FILENAME));
   340     #else
   341     if ((!done) || reset)
   342         done = (PEP_STATUS_OK == unix_local_db_file(buffer, LOCAL_DB_FILENAME, reset));
   343     #endif
   344 
   345     if (done)
   346         return buffer;
   347     return NULL;
   348 }
   349 
   350 #ifdef NDEBUG
   351 const char *unix_local_keys_db(void)
   352 #else
   353 const char *unix_local_keys_db(int reset)
   354 #endif
   355 {
   356     static char buffer[MAX_PATH];
   357     static bool done = false;
   358 
   359     #ifdef NDEBUG
   360     if (!done)
   361         done = (PEP_STATUS_OK == unix_local_db_file(buffer, LOCAL_KEYS_DB_FILENAME));
   362     #else
   363     if ((!done) || reset)
   364         done = (PEP_STATUS_OK == unix_local_db_file(buffer, LOCAL_KEYS_DB_FILENAME, reset));
   365     #endif
   366 
   367     if (done) {
   368         return buffer;
   369     }
   370     return NULL;
   371 }
   372 
   373 
   374 static const char *gpg_conf_path = ".gnupg";
   375 static const char *gpg_conf_name = "gpg.conf";
   376 static const char *gpg_agent_conf_name = "gpg-agent.conf";
   377 static const char *gpg_conf_empty = "# Created by pEpEngine\n";
   378 
   379 #ifdef NDEBUG
   380 static bool ensure_gpg_home(const char **conf, const char **home){
   381 #else
   382 static bool ensure_gpg_home(const char **conf, const char **home, int reset){
   383 #endif    
   384     static char path[MAX_PATH];
   385     static char dirname[MAX_PATH];
   386     static bool done = false;
   387 
   388 #ifdef NDEBUG
   389     if (!done) {
   390 #else
   391     if (reset || !done) {
   392 #endif        
   393         char *p;
   394         ssize_t len;
   395         char *gpg_home_env = getenv("GNUPGHOME");
   396         char *home_env = getenv("HOME");
   397 
   398         if(gpg_home_env){
   399 
   400             p = stpncpy(path, gpg_home_env, MAX_PATH);
   401             len = MAX_PATH - (p - path) - 2;
   402 
   403             if (len < strlen(gpg_conf_name))
   404             {
   405                 assert(0);
   406                 return false;
   407             }
   408 
   409         }else if(home_env){
   410 
   411             p = stpncpy(path, home_env, MAX_PATH);
   412             len = MAX_PATH - (p - path) - 3;
   413 
   414             if (len < strlen(gpg_conf_path) + strlen(gpg_conf_name))
   415             {
   416                 assert(0);
   417                 return false;
   418             }
   419 
   420             *p++ = '/';
   421             strncpy(p, gpg_conf_path, len);
   422             p += strlen(gpg_conf_path);
   423             len -= strlen(gpg_conf_path) - 1;
   424 
   425         }else{
   426 
   427             assert(0);
   428             return false;
   429         }
   430 
   431         strncpy(dirname, path, MAX_PATH);
   432         *p++ = '/';
   433         strncpy(p, gpg_conf_name, len);
   434 
   435         if(access(path, F_OK)){ 
   436             int fd;
   437             if(access(dirname, F_OK )) { 
   438                 mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR);
   439             }
   440 
   441             fd = open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
   442 
   443             if(fd>0) {
   444                 ssize_t res;
   445                 len = strlen(gpg_conf_empty);
   446                 res = write(fd, gpg_conf_empty, len);
   447                 close(fd);
   448                 if(res < len) {
   449                     assert(0);
   450                     return false;
   451                 }
   452             }
   453         }
   454 
   455         done = true;
   456     }
   457 
   458     if(conf) *conf=path;
   459     if(home) *home=dirname;
   460 
   461     return true;
   462 }
   463 
   464 #ifdef NDEBUG
   465 static bool ensure_gpg_agent_conf(const char **agent_conf){
   466 #else
   467 static bool ensure_gpg_agent_conf(const char **agent_conf, int reset){    
   468 #endif    
   469     static char agent_path[MAX_PATH];
   470     static bool done = false;
   471 
   472 #ifdef NDEBUG
   473     if (!done) {
   474         const char *dirname;
   475 
   476         if (!ensure_gpg_home(NULL, &dirname)) /* Then dirname won't be set. */
   477             return false;
   478 #else
   479     if (reset || !done) {
   480         const char *dirname;
   481 
   482         if (!ensure_gpg_home(NULL, &dirname, reset)) /* Then dirname won't be set. */
   483             return false;
   484 #endif
   485 
   486         char *p = stpncpy(agent_path, dirname, MAX_PATH);
   487         
   488         ssize_t len = MAX_PATH - (p - agent_path) - 2;
   489 
   490         if (len < strlen(gpg_agent_conf_name))
   491         {
   492             assert(0);
   493             return false;
   494         }
   495 
   496         *p++ = '/';
   497      
   498         strncpy(p, gpg_agent_conf_name, len);
   499 
   500         if(access(agent_path, F_OK)){ 
   501             int fd;
   502             if(access(dirname, F_OK )) { 
   503                 mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR);
   504             }
   505 
   506             fd = open(agent_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
   507 
   508             if(fd>0) {
   509                 ssize_t res;
   510                 len = strlen(gpg_conf_empty);
   511                 res = write(fd, gpg_conf_empty, len);
   512                 close(fd);
   513                 if(res < len) {
   514                     assert(0);
   515                     return false;
   516                 }
   517             }
   518         }
   519         done = true;
   520     }
   521     if(agent_conf) *agent_conf=agent_path;
   522 
   523     return true;
   524 }
   525 
   526 #ifdef NDEBUG
   527 const char *gpg_conf(void)
   528 {
   529     const char *conf;
   530     if(ensure_gpg_home(&conf, NULL))
   531         return conf;
   532     return NULL;
   533 }
   534 #else
   535 const char *gpg_conf(int reset)
   536 {
   537     const char *conf;
   538     if(ensure_gpg_home(&conf, NULL, reset))
   539         return conf;
   540     return NULL;
   541 }
   542 #endif
   543 
   544 #ifdef NDEBUG
   545 const char *gpg_home(void)
   546 {
   547     const char *home;
   548     if(ensure_gpg_home(NULL, &home))
   549         return home;
   550     return NULL;
   551 }
   552 #else
   553 const char *gpg_home(int reset)
   554 {
   555     const char *home;
   556     if(ensure_gpg_home(NULL, &home, reset))
   557         return home;
   558     return NULL;
   559 }
   560 #endif
   561 
   562 #ifdef NDEBUG
   563 const char *gpg_agent_conf(void)
   564 {
   565     const char *agent_conf;
   566     if(ensure_gpg_agent_conf(&agent_conf))
   567         return agent_conf;
   568     return NULL;
   569 }
   570 #else
   571 const char *gpg_agent_conf(int reset)
   572 {
   573     const char *agent_conf;
   574     if(ensure_gpg_agent_conf(&agent_conf, reset))
   575         return agent_conf;
   576     return NULL;
   577 }
   578 #endif
   579