src/platform_unix.c
author Neal H. Walfield <neal@pep.foundation>
Wed, 08 May 2019 12:20:37 +0200
branchemail_comparison
changeset 3649 1dd837adc30b
parent 2748 a8db3d62c2fd
child 2755 65c6b4bb11a6
child 3638 4ba9bd8b80c1
child 3901 cdfe2dddb5da
permissions -rw-r--r--
Fix public key.
vb@1513
     1
// This file is under GNU General Public License 3.0
vb@1513
     2
// see LICENSE.txt
vb@1513
     3
vb@1
     4
#define _POSIX_C_SOURCE 200809L
vb@1
     5
Edouard@1267
     6
#ifdef ANDROID
Edouard@1267
     7
#ifndef __LP64__ 
Edouard@1267
     8
#include <time64.h>
Edouard@1267
     9
#endif
Edouard@1267
    10
#endif
Edouard@1267
    11
Edouard@1136
    12
#include <time.h>
vb@1
    13
#include <string.h>
vb@1
    14
#include <stdlib.h>
vb@1
    15
#include <assert.h>
Edouard@158
    16
#include <sys/stat.h>
vb@149
    17
#include <sys/types.h>
Edouard@158
    18
#include <fcntl.h>
krista@1033
    19
#include <regex.h>
Edouard@158
    20
vb@1
    21
#include "platform_unix.h"
vb@1
    22
vb@1
    23
#define MAX_PATH 1024
vb@1089
    24
#ifndef LOCAL_DB_FILENAME
vb@1
    25
#define LOCAL_DB_FILENAME ".pEp_management.db"
vb@1089
    26
#endif
Edouard@347
    27
#define SYSTEM_DB_FILENAME "system.db"
vb@1
    28
vb@1
    29
#ifndef bool
vb@1
    30
#define bool int
vb@1
    31
#define true 1
vb@1
    32
#define false 0
vb@1
    33
#endif
vb@1
    34
Edouard@347
    35
#ifdef ANDROID
Edouard@1119
    36
#include <uuid.h>
Edouard@1267
    37
Edouard@1267
    38
/* FIXME : timegm will miss when linking for x86_64 on android, when supported */
Edouard@1267
    39
#ifndef __LP64__ 
Edouard@1267
    40
time_t timegm(struct tm* const t) {
Edouard@1267
    41
    static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
Edouard@1267
    42
    static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
Edouard@1267
    43
    time64_t result = timegm64(t);
Edouard@1267
    44
    if (result < kTimeMin || result > kTimeMax)
Edouard@1267
    45
        return -1;
Edouard@1267
    46
    return result;
Edouard@1267
    47
}
Edouard@1267
    48
#endif
Edouard@1267
    49
Edouard@347
    50
char *stpncpy(char *dst, const char *src, size_t n)
Edouard@333
    51
{
Edouard@333
    52
    if (n != 0) {
Edouard@333
    53
        char *d = dst;
Edouard@333
    54
        const char *s = src;
Edouard@333
    55
Edouard@333
    56
        dst = &dst[n];
Edouard@333
    57
        do {
Edouard@333
    58
            if ((*d++ = *s++) == 0) {
Edouard@333
    59
                dst = d - 1;
Edouard@333
    60
                /* NUL pad the remaining n-1 bytes */
Edouard@333
    61
                while (--n != 0)
Edouard@333
    62
                    *d++ = 0;
Edouard@333
    63
                break;
Edouard@333
    64
            }
Edouard@333
    65
        } while (--n != 0);
Edouard@333
    66
    }
Edouard@333
    67
    return (dst);
Edouard@333
    68
}
Edouard@333
    69
Edouard@338
    70
char *stpcpy(char *dst, const char *src)
Edouard@338
    71
{
Edouard@338
    72
    for (;; ++dst, ++src) {
Edouard@338
    73
        *dst = *src;
Edouard@338
    74
        if (*dst == 0)
Edouard@338
    75
            break;
Edouard@338
    76
    }
Edouard@338
    77
    return dst;
Edouard@338
    78
}
Edouard@338
    79
huss@1528
    80
/*
roker@438
    81
long int random(void)
roker@438
    82
{
Edouard@841
    83
    static bool seeded = false;
Edouard@838
    84
    static unsigned short xsubi[3];
Edouard@841
    85
    if(!seeded)
Edouard@841
    86
    {
roker@847
    87
        const long long t = (long long)time(NULL);
roker@847
    88
        xsubi[0] = (unsigned short)t;
roker@847
    89
        xsubi[1] = (unsigned short)(t>>16);
roker@847
    90
        xsubi[2] = (unsigned short)(t>>32);
Edouard@841
    91
        seeded = true;
Edouard@841
    92
    }
Edouard@838
    93
vb@436
    94
    return nrand48(xsubi);
huss@1528
    95
} */
Edouard@347
    96
Edouard@347
    97
const char *android_system_db(void)
Edouard@347
    98
{
Edouard@347
    99
    static char buffer[MAX_PATH];
Edouard@347
   100
    static bool done = false;
Edouard@347
   101
Edouard@347
   102
    if (!done) {
Edouard@349
   103
        char *tw_env;
Edouard@349
   104
        if(tw_env = getenv("TRUSTWORDS")){
Edouard@349
   105
            char *p = stpncpy(buffer, tw_env, MAX_PATH);
Edouard@814
   106
            ssize_t len = MAX_PATH - (p - buffer) - 2;
Edouard@347
   107
Edouard@347
   108
            if (len < strlen(SYSTEM_DB_FILENAME)) {
Edouard@347
   109
                assert(0);
Edouard@347
   110
                return NULL;
Edouard@347
   111
            }
Edouard@347
   112
Edouard@347
   113
            *p++ = '/';
Edouard@347
   114
            strncpy(p, SYSTEM_DB_FILENAME, len);
Edouard@347
   115
            done = true;
Edouard@347
   116
        }else{
Edouard@347
   117
            return NULL;
Edouard@347
   118
        }
Edouard@347
   119
Edouard@347
   120
    }
Edouard@347
   121
    return buffer;
Edouard@347
   122
}
Edouard@1119
   123
huss@1318
   124
markus@1226
   125
void uuid_generate_random(pEpUUID out)
Edouard@1119
   126
{
Edouard@1136
   127
    uuid_t *uuid;
Edouard@1136
   128
    uuid_rc_t rc_create;
huss@1318
   129
    size_t size = sizeof(uuid_string_t);
huss@1318
   130
    void *_out = out;
huss@1318
   131
	
Edouard@1136
   132
    if ((rc_create = uuid_create(&uuid)) != UUID_RC_OK ||
Edouard@1136
   133
        uuid_make(uuid, UUID_MAKE_V1) != UUID_RC_OK ||
huss@1318
   134
        uuid_export(uuid, UUID_FMT_BIN, &_out, &size) != UUID_RC_OK)
Edouard@1136
   135
    {
markus@1226
   136
        memset(out, 0, sizeof(pEpUUID));
Edouard@1136
   137
    }
Edouard@1136
   138
Edouard@1136
   139
    if (rc_create == UUID_RC_OK)
Edouard@1136
   140
    {
Edouard@1136
   141
        uuid_destroy(uuid);
Edouard@1136
   142
    }
Edouard@1119
   143
}
Edouard@1119
   144
huss@1318
   145
markus@1226
   146
void uuid_unparse_upper(pEpUUID uu, uuid_string_t out)
Edouard@1119
   147
{
Edouard@1136
   148
    uuid_t *uuid;
Edouard@1136
   149
    uuid_rc_t rc_create;
huss@1318
   150
    size_t size = sizeof(uuid_string_t);
huss@1318
   151
    void *_out = out;
Edouard@1136
   152
Edouard@1136
   153
    if ((rc_create = uuid_create(&uuid)) != UUID_RC_OK ||
Edouard@1262
   154
        uuid_import(uuid, UUID_FMT_BIN, uu, sizeof(pEpUUID)) != UUID_RC_OK ||
huss@1318
   155
        uuid_export(uuid, UUID_FMT_STR, &_out, &size) != UUID_RC_OK)
Edouard@1136
   156
    {
Edouard@1136
   157
        memset(out, 0, sizeof(uuid_string_t));
Edouard@1136
   158
    }
Edouard@1136
   159
    else 
Edouard@1136
   160
    {
Edouard@1136
   161
        out[sizeof(uuid_string_t) - 1] = 0;
Edouard@1136
   162
    }
Edouard@1136
   163
Edouard@1136
   164
    if (rc_create == UUID_RC_OK)
Edouard@1136
   165
    {
Edouard@1136
   166
        uuid_destroy(uuid);
Edouard@1136
   167
    }
Edouard@1119
   168
}
Edouard@1119
   169
Edouard@338
   170
#endif
Edouard@338
   171
vb@926
   172
#if !defined(BSD) && !defined(__APPLE__)
vb@926
   173
krista@918
   174
size_t strlcpy(char* dst, const	char* src, size_t size) {
krista@918
   175
    size_t retval = strlen(src);
krista@918
   176
    size_t size_to_copy = (retval < size ? retval : size - 1);
krista@918
   177
    
krista@918
   178
    // strlcpy doc says src and dst not allowed to overlap, as
krista@918
   179
    // it's undefined. So this is acceptable:
krista@918
   180
    memcpy((void*)dst, (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
krista@918
   181
    dst[size_to_copy] = '\0';
krista@918
   182
    return retval;
krista@918
   183
}
vb@926
   184
krista@918
   185
size_t strlcat(char* dst, const	char* src, size_t size) {
krista@918
   186
    size_t start_len = strnlen(dst, size);
krista@918
   187
    if (start_len == size)
krista@918
   188
        return size; // no copy, no null termination in size bytes, according to spec
krista@918
   189
    
krista@918
   190
    size_t add_len = strlen(src);
krista@918
   191
    size_t retval = start_len + add_len;
krista@918
   192
    size_t size_to_copy = (retval < size ? add_len : (size - start_len) - 1);
krista@918
   193
    
krista@918
   194
    // strlcat doc says src and dst not allowed to overlap, as
krista@918
   195
    // it's undefined. So this is acceptable:
krista@918
   196
    memcpy((void*)(dst + start_len), (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
krista@918
   197
    dst[start_len + size_to_copy] = '\0';
krista@918
   198
    return retval;
krista@918
   199
}
vb@926
   200
krista@1095
   201
#ifdef USE_NETPGP
krista@1033
   202
// FIXME: This may cause problems - this is a quick compatibility fix for netpgp code
krista@1033
   203
int regnexec(const regex_t* preg, const char* string,
krista@1033
   204
             size_t len, size_t nmatch, regmatch_t pmatch[], int eflags) {
krista@1033
   205
    return regexec(preg, string, nmatch, pmatch, eflags);
krista@1033
   206
}
krista@1095
   207
#endif
krista@1033
   208
krista@919
   209
#endif
krista@918
   210
krista@2684
   211
#ifdef NDEBUG
vb@1
   212
const char *unix_local_db(void)
krista@2684
   213
#else
krista@2684
   214
const char *unix_local_db(int reset)
krista@2684
   215
#endif
vb@1
   216
{
vb@1
   217
    static char buffer[MAX_PATH];
vb@1
   218
    static bool done = false;
vb@1
   219
krista@2684
   220
    #ifdef NDEBUG
krista@2684
   221
    if (!done)
krista@2684
   222
    #else
krista@2684
   223
    if ((!done) || reset)
krista@2684
   224
    #endif
krista@2684
   225
    {
Edouard@346
   226
        char *home_env;
vb@352
   227
        if((home_env = getenv("HOME"))){
Edouard@347
   228
            char *p = stpncpy(buffer, home_env, MAX_PATH);
Edouard@814
   229
            ssize_t len = MAX_PATH - (p - buffer) - 2;
vb@1
   230
Edouard@346
   231
            if (len < strlen(LOCAL_DB_FILENAME)) {
Edouard@346
   232
                assert(0);
Edouard@346
   233
                return NULL;
Edouard@346
   234
            }
Edouard@346
   235
Edouard@346
   236
            *p++ = '/';
Edouard@346
   237
            strncpy(p, LOCAL_DB_FILENAME, len);
Edouard@346
   238
            done = true;
Edouard@346
   239
        }else{
vb@1
   240
            return NULL;
vb@1
   241
        }
vb@1
   242
vb@1
   243
    }
vb@1
   244
    return buffer;
vb@1
   245
}
vb@1
   246
vb@1
   247
static const char *gpg_conf_path = ".gnupg";
vb@1
   248
static const char *gpg_conf_name = "gpg.conf";
krista@763
   249
static const char *gpg_agent_conf_name = "gpg-agent.conf";
Edouard@158
   250
static const char *gpg_conf_empty = "# Created by pEpEngine\n";
vb@1
   251
krista@2684
   252
#ifdef NDEBUG
Edouard@168
   253
static bool ensure_gpg_home(const char **conf, const char **home){
krista@2684
   254
#else
krista@2684
   255
static bool ensure_gpg_home(const char **conf, const char **home, int reset){
krista@2684
   256
#endif    
Edouard@168
   257
    static char path[MAX_PATH];
Edouard@158
   258
    static char dirname[MAX_PATH];
vb@1
   259
    static bool done = false;
vb@1
   260
krista@2684
   261
#ifdef NDEBUG
vb@1
   262
    if (!done) {
krista@2684
   263
#else
krista@2684
   264
    if (reset || !done) {
krista@2684
   265
#endif        
Edouard@168
   266
        char *p;
Edouard@814
   267
        ssize_t len;
Edouard@168
   268
        char *gpg_home_env = getenv("GNUPGHOME");
Edouard@346
   269
        char *home_env = getenv("HOME");
vb@1
   270
Edouard@168
   271
        if(gpg_home_env){
Edouard@168
   272
Edouard@347
   273
            p = stpncpy(path, gpg_home_env, MAX_PATH);
Edouard@168
   274
            len = MAX_PATH - (p - path) - 2;
Edouard@168
   275
Edouard@158
   276
            if (len < strlen(gpg_conf_name))
Edouard@158
   277
            {
Edouard@158
   278
                assert(0);
Edouard@168
   279
                return false;
Edouard@158
   280
            }
Edouard@158
   281
Edouard@346
   282
        }else if(home_env){
Edouard@158
   283
Edouard@347
   284
            p = stpncpy(path, home_env, MAX_PATH);
Edouard@168
   285
            len = MAX_PATH - (p - path) - 3;
Edouard@158
   286
Edouard@158
   287
            if (len < strlen(gpg_conf_path) + strlen(gpg_conf_name))
Edouard@158
   288
            {
Edouard@158
   289
                assert(0);
Edouard@168
   290
                return false;
Edouard@158
   291
            }
Edouard@158
   292
Edouard@158
   293
            *p++ = '/';
Edouard@158
   294
            strncpy(p, gpg_conf_path, len);
Edouard@158
   295
            p += strlen(gpg_conf_path);
Edouard@158
   296
            len -= strlen(gpg_conf_path) - 1;
Edouard@346
   297
Edouard@346
   298
        }else{
Edouard@346
   299
Edouard@346
   300
            assert(0);
Edouard@346
   301
            return false;
vb@1
   302
        }
vb@1
   303
Edouard@168
   304
        strncpy(dirname, path, MAX_PATH);
Edouard@168
   305
        *p++ = '/';
Edouard@168
   306
        strncpy(p, gpg_conf_name, len);
Edouard@168
   307
Edouard@168
   308
        if(access(path, F_OK)){ 
Edouard@158
   309
            int fd;
Edouard@158
   310
            if(access(dirname, F_OK )) { 
Edouard@158
   311
                mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR);
Edouard@158
   312
            }
vb@149
   313
Edouard@168
   314
            fd = open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
vb@149
   315
Edouard@158
   316
            if(fd>0) {
edouard@2118
   317
                ssize_t res;
edouard@2118
   318
                len = strlen(gpg_conf_empty);
edouard@2118
   319
                res = write(fd, gpg_conf_empty, len);
Edouard@158
   320
                close(fd);
edouard@2118
   321
                if(res < len) {
edouard@2118
   322
                    assert(0);
edouard@2118
   323
                    return false;
edouard@2118
   324
                }
Edouard@158
   325
            }
Edouard@158
   326
        }
vb@1
   327
vb@1
   328
        done = true;
vb@1
   329
    }
Edouard@168
   330
Edouard@168
   331
    if(conf) *conf=path;
Edouard@168
   332
    if(home) *home=dirname;
Edouard@168
   333
Edouard@168
   334
    return true;
vb@1
   335
}
Edouard@168
   336
krista@2684
   337
#ifdef NDEBUG
krista@763
   338
static bool ensure_gpg_agent_conf(const char **agent_conf){
krista@2684
   339
#else
krista@2684
   340
static bool ensure_gpg_agent_conf(const char **agent_conf, int reset){    
krista@2684
   341
#endif    
krista@763
   342
    static char agent_path[MAX_PATH];
krista@763
   343
    static bool done = false;
krista@763
   344
krista@2684
   345
#ifdef NDEBUG
krista@763
   346
    if (!done) {
krista@763
   347
        const char *dirname;
krista@763
   348
krista@763
   349
        if (!ensure_gpg_home(NULL, &dirname)) /* Then dirname won't be set. */
krista@763
   350
            return false;
krista@2684
   351
#else
krista@2684
   352
    if (reset || !done) {
krista@2684
   353
        const char *dirname;
krista@2684
   354
krista@2684
   355
        if (!ensure_gpg_home(NULL, &dirname, reset)) /* Then dirname won't be set. */
krista@2684
   356
            return false;
krista@2684
   357
#endif
krista@763
   358
roker@1559
   359
        char *p = stpncpy(agent_path, dirname, MAX_PATH);
krista@763
   360
        
Edouard@814
   361
        ssize_t len = MAX_PATH - (p - agent_path) - 2;
krista@763
   362
krista@763
   363
        if (len < strlen(gpg_agent_conf_name))
krista@763
   364
        {
krista@763
   365
            assert(0);
krista@763
   366
            return false;
krista@763
   367
        }
krista@763
   368
krista@763
   369
        *p++ = '/';
krista@763
   370
     
krista@763
   371
        strncpy(p, gpg_agent_conf_name, len);
krista@763
   372
krista@763
   373
        if(access(agent_path, F_OK)){ 
krista@763
   374
            int fd;
krista@763
   375
            if(access(dirname, F_OK )) { 
krista@763
   376
                mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR);
krista@763
   377
            }
krista@763
   378
krista@763
   379
            fd = open(agent_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
krista@763
   380
krista@763
   381
            if(fd>0) {
edouard@2118
   382
                ssize_t res;
edouard@2118
   383
                len = strlen(gpg_conf_empty);
edouard@2118
   384
                res = write(fd, gpg_conf_empty, len);
krista@763
   385
                close(fd);
edouard@2118
   386
                if(res < len) {
edouard@2118
   387
                    assert(0);
edouard@2118
   388
                    return false;
edouard@2118
   389
                }
krista@763
   390
            }
krista@763
   391
        }
krista@763
   392
        done = true;
krista@763
   393
    }
krista@763
   394
    if(agent_conf) *agent_conf=agent_path;
krista@763
   395
krista@763
   396
    return true;
krista@763
   397
}
krista@763
   398
krista@2684
   399
#ifdef NDEBUG
Edouard@168
   400
const char *gpg_conf(void)
Edouard@168
   401
{
Edouard@168
   402
    const char *conf;
Edouard@168
   403
    if(ensure_gpg_home(&conf, NULL))
Edouard@168
   404
        return conf;
Edouard@168
   405
    return NULL;
Edouard@168
   406
}
krista@2684
   407
#else
krista@2684
   408
const char *gpg_conf(int reset)
krista@2684
   409
{
krista@2684
   410
    const char *conf;
krista@2684
   411
    if(ensure_gpg_home(&conf, NULL, reset))
krista@2684
   412
        return conf;
krista@2684
   413
    return NULL;
krista@2684
   414
}
krista@2684
   415
#endif
Edouard@168
   416
krista@2684
   417
#ifdef NDEBUG
Edouard@168
   418
const char *gpg_home(void)
Edouard@168
   419
{
Edouard@168
   420
    const char *home;
Edouard@168
   421
    if(ensure_gpg_home(NULL, &home))
Edouard@168
   422
        return home;
Edouard@168
   423
    return NULL;
Edouard@168
   424
}
krista@2684
   425
#else
krista@2684
   426
const char *gpg_home(int reset)
krista@2684
   427
{
krista@2684
   428
    const char *home;
krista@2684
   429
    if(ensure_gpg_home(NULL, &home, reset))
krista@2684
   430
        return home;
krista@2684
   431
    return NULL;
krista@2684
   432
}
krista@2684
   433
#endif
krista@763
   434
krista@2684
   435
#ifdef NDEBUG
krista@763
   436
const char *gpg_agent_conf(void)
krista@763
   437
{
krista@763
   438
    const char *agent_conf;
krista@763
   439
    if(ensure_gpg_agent_conf(&agent_conf))
krista@763
   440
        return agent_conf;
krista@763
   441
    return NULL;
krista@763
   442
}
krista@2684
   443
#else
krista@2684
   444
const char *gpg_agent_conf(int reset)
krista@2684
   445
{
krista@2684
   446
    const char *agent_conf;
krista@2684
   447
    if(ensure_gpg_agent_conf(&agent_conf, reset))
krista@2684
   448
        return agent_conf;
krista@2684
   449
    return NULL;
krista@2684
   450
}
vb@2748
   451
#endif
vb@2748
   452