lua/lstate.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 23 Jun 2017 17:28:03 -0400
changeset 58 1390348facc7
parent 0 d7ee4e2ed49d
permissions -rw-r--r--
Command line tool that decrypts an OPVault keychain and dumps it to stdout.

To compile: gcc -o opvault opvault.c cJSON.c -lcrypto

Usage: ./opvault </path/to/mykeychain.opvault> <password>

This is just a proof of concept; I'll be recycling this into proper OPVault
support in 1pass later and deleting this tool.

This uses OpenSSL's libcrypto for the math instead of all the homegrown
crypto this project is otherwise using. I'll probably migrate the rest in
this direction, too, since this wasn't as bad as I expected to use and
gets you all the package-manager mojo of automatic bug fixes and security
patches and shared code, etc.

cJSON parses JSON in C. That is from https://github.com/DaveGamble/cJSON

An example OPVault keychain from AgileBits is available here:

https://cache.agilebits.com/security-kb/
icculus@0
     1
/*
icculus@0
     2
** $Id: lstate.c,v 2.99.1.2 2013/11/08 17:45:31 roberto Exp $
icculus@0
     3
** Global State
icculus@0
     4
** See Copyright Notice in lua.h
icculus@0
     5
*/
icculus@0
     6
icculus@0
     7
icculus@0
     8
#include <stddef.h>
icculus@0
     9
#include <string.h>
icculus@0
    10
icculus@0
    11
#define lstate_c
icculus@0
    12
#define LUA_CORE
icculus@0
    13
icculus@0
    14
#include "lua.h"
icculus@0
    15
icculus@0
    16
#include "lapi.h"
icculus@0
    17
#include "ldebug.h"
icculus@0
    18
#include "ldo.h"
icculus@0
    19
#include "lfunc.h"
icculus@0
    20
#include "lgc.h"
icculus@0
    21
#include "llex.h"
icculus@0
    22
#include "lmem.h"
icculus@0
    23
#include "lstate.h"
icculus@0
    24
#include "lstring.h"
icculus@0
    25
#include "ltable.h"
icculus@0
    26
#include "ltm.h"
icculus@0
    27
icculus@0
    28
icculus@0
    29
#if !defined(LUAI_GCPAUSE)
icculus@0
    30
#define LUAI_GCPAUSE	200  /* 200% */
icculus@0
    31
#endif
icculus@0
    32
icculus@0
    33
#if !defined(LUAI_GCMAJOR)
icculus@0
    34
#define LUAI_GCMAJOR	200  /* 200% */
icculus@0
    35
#endif
icculus@0
    36
icculus@0
    37
#if !defined(LUAI_GCMUL)
icculus@0
    38
#define LUAI_GCMUL	200 /* GC runs 'twice the speed' of memory allocation */
icculus@0
    39
#endif
icculus@0
    40
icculus@0
    41
icculus@0
    42
#define MEMERRMSG	"not enough memory"
icculus@0
    43
icculus@0
    44
icculus@0
    45
/*
icculus@0
    46
** a macro to help the creation of a unique random seed when a state is
icculus@0
    47
** created; the seed is used to randomize hashes.
icculus@0
    48
*/
icculus@0
    49
#if !defined(luai_makeseed)
icculus@0
    50
#include <time.h>
icculus@0
    51
#define luai_makeseed()		cast(unsigned int, time(NULL))
icculus@0
    52
#endif
icculus@0
    53
icculus@0
    54
icculus@0
    55
icculus@0
    56
/*
icculus@0
    57
** thread state + extra space
icculus@0
    58
*/
icculus@0
    59
typedef struct LX {
icculus@0
    60
#if defined(LUAI_EXTRASPACE)
icculus@0
    61
  char buff[LUAI_EXTRASPACE];
icculus@0
    62
#endif
icculus@0
    63
  lua_State l;
icculus@0
    64
} LX;
icculus@0
    65
icculus@0
    66
icculus@0
    67
/*
icculus@0
    68
** Main thread combines a thread state and the global state
icculus@0
    69
*/
icculus@0
    70
typedef struct LG {
icculus@0
    71
  LX l;
icculus@0
    72
  global_State g;
icculus@0
    73
} LG;
icculus@0
    74
icculus@0
    75
icculus@0
    76
icculus@0
    77
#define fromstate(L)	(cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
icculus@0
    78
icculus@0
    79
icculus@0
    80
/*
icculus@0
    81
** Compute an initial seed as random as possible. In ANSI, rely on
icculus@0
    82
** Address Space Layout Randomization (if present) to increase
icculus@0
    83
** randomness..
icculus@0
    84
*/
icculus@0
    85
#define addbuff(b,p,e) \
icculus@0
    86
  { size_t t = cast(size_t, e); \
icculus@0
    87
    memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); }
icculus@0
    88
icculus@0
    89
static unsigned int makeseed (lua_State *L) {
icculus@0
    90
  char buff[4 * sizeof(size_t)];
icculus@0
    91
  unsigned int h = luai_makeseed();
icculus@0
    92
  int p = 0;
icculus@0
    93
  addbuff(buff, p, L);  /* heap variable */
icculus@0
    94
  addbuff(buff, p, &h);  /* local variable */
icculus@0
    95
  addbuff(buff, p, luaO_nilobject);  /* global variable */
icculus@0
    96
  addbuff(buff, p, &lua_newstate);  /* public function */
icculus@0
    97
  lua_assert(p == sizeof(buff));
icculus@0
    98
  return luaS_hash(buff, p, h);
icculus@0
    99
}
icculus@0
   100
icculus@0
   101
icculus@0
   102
/*
icculus@0
   103
** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
icculus@0
   104
** invariant
icculus@0
   105
*/
icculus@0
   106
void luaE_setdebt (global_State *g, l_mem debt) {
icculus@0
   107
  g->totalbytes -= (debt - g->GCdebt);
icculus@0
   108
  g->GCdebt = debt;
icculus@0
   109
}
icculus@0
   110
icculus@0
   111
icculus@0
   112
CallInfo *luaE_extendCI (lua_State *L) {
icculus@0
   113
  CallInfo *ci = luaM_new(L, CallInfo);
icculus@0
   114
  lua_assert(L->ci->next == NULL);
icculus@0
   115
  L->ci->next = ci;
icculus@0
   116
  ci->previous = L->ci;
icculus@0
   117
  ci->next = NULL;
icculus@0
   118
  return ci;
icculus@0
   119
}
icculus@0
   120
icculus@0
   121
icculus@0
   122
void luaE_freeCI (lua_State *L) {
icculus@0
   123
  CallInfo *ci = L->ci;
icculus@0
   124
  CallInfo *next = ci->next;
icculus@0
   125
  ci->next = NULL;
icculus@0
   126
  while ((ci = next) != NULL) {
icculus@0
   127
    next = ci->next;
icculus@0
   128
    luaM_free(L, ci);
icculus@0
   129
  }
icculus@0
   130
}
icculus@0
   131
icculus@0
   132
icculus@0
   133
static void stack_init (lua_State *L1, lua_State *L) {
icculus@0
   134
  int i; CallInfo *ci;
icculus@0
   135
  /* initialize stack array */
icculus@0
   136
  L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue);
icculus@0
   137
  L1->stacksize = BASIC_STACK_SIZE;
icculus@0
   138
  for (i = 0; i < BASIC_STACK_SIZE; i++)
icculus@0
   139
    setnilvalue(L1->stack + i);  /* erase new stack */
icculus@0
   140
  L1->top = L1->stack;
icculus@0
   141
  L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
icculus@0
   142
  /* initialize first ci */
icculus@0
   143
  ci = &L1->base_ci;
icculus@0
   144
  ci->next = ci->previous = NULL;
icculus@0
   145
  ci->callstatus = 0;
icculus@0
   146
  ci->func = L1->top;
icculus@0
   147
  setnilvalue(L1->top++);  /* 'function' entry for this 'ci' */
icculus@0
   148
  ci->top = L1->top + LUA_MINSTACK;
icculus@0
   149
  L1->ci = ci;
icculus@0
   150
}
icculus@0
   151
icculus@0
   152
icculus@0
   153
static void freestack (lua_State *L) {
icculus@0
   154
  if (L->stack == NULL)
icculus@0
   155
    return;  /* stack not completely built yet */
icculus@0
   156
  L->ci = &L->base_ci;  /* free the entire 'ci' list */
icculus@0
   157
  luaE_freeCI(L);
icculus@0
   158
  luaM_freearray(L, L->stack, L->stacksize);  /* free stack array */
icculus@0
   159
}
icculus@0
   160
icculus@0
   161
icculus@0
   162
/*
icculus@0
   163
** Create registry table and its predefined values
icculus@0
   164
*/
icculus@0
   165
static void init_registry (lua_State *L, global_State *g) {
icculus@0
   166
  TValue mt;
icculus@0
   167
  /* create registry */
icculus@0
   168
  Table *registry = luaH_new(L);
icculus@0
   169
  sethvalue(L, &g->l_registry, registry);
icculus@0
   170
  luaH_resize(L, registry, LUA_RIDX_LAST, 0);
icculus@0
   171
  /* registry[LUA_RIDX_MAINTHREAD] = L */
icculus@0
   172
  setthvalue(L, &mt, L);
icculus@0
   173
  luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &mt);
icculus@0
   174
  /* registry[LUA_RIDX_GLOBALS] = table of globals */
icculus@0
   175
  sethvalue(L, &mt, luaH_new(L));
icculus@0
   176
  luaH_setint(L, registry, LUA_RIDX_GLOBALS, &mt);
icculus@0
   177
}
icculus@0
   178
icculus@0
   179
icculus@0
   180
/*
icculus@0
   181
** open parts of the state that may cause memory-allocation errors
icculus@0
   182
*/
icculus@0
   183
static void f_luaopen (lua_State *L, void *ud) {
icculus@0
   184
  global_State *g = G(L);
icculus@0
   185
  UNUSED(ud);
icculus@0
   186
  stack_init(L, L);  /* init stack */
icculus@0
   187
  init_registry(L, g);
icculus@0
   188
  luaS_resize(L, MINSTRTABSIZE);  /* initial size of string table */
icculus@0
   189
  luaT_init(L);
icculus@0
   190
  luaX_init(L);
icculus@0
   191
  /* pre-create memory-error message */
icculus@0
   192
  g->memerrmsg = luaS_newliteral(L, MEMERRMSG);
icculus@0
   193
  luaS_fix(g->memerrmsg);  /* it should never be collected */
icculus@0
   194
  g->gcrunning = 1;  /* allow gc */
icculus@0
   195
  g->version = lua_version(NULL);
icculus@0
   196
  luai_userstateopen(L);
icculus@0
   197
}
icculus@0
   198
icculus@0
   199
icculus@0
   200
/*
icculus@0
   201
** preinitialize a state with consistent values without allocating
icculus@0
   202
** any memory (to avoid errors)
icculus@0
   203
*/
icculus@0
   204
static void preinit_state (lua_State *L, global_State *g) {
icculus@0
   205
  G(L) = g;
icculus@0
   206
  L->stack = NULL;
icculus@0
   207
  L->ci = NULL;
icculus@0
   208
  L->stacksize = 0;
icculus@0
   209
  L->errorJmp = NULL;
icculus@0
   210
  L->nCcalls = 0;
icculus@0
   211
  L->hook = NULL;
icculus@0
   212
  L->hookmask = 0;
icculus@0
   213
  L->basehookcount = 0;
icculus@0
   214
  L->allowhook = 1;
icculus@0
   215
  resethookcount(L);
icculus@0
   216
  L->openupval = NULL;
icculus@0
   217
  L->nny = 1;
icculus@0
   218
  L->status = LUA_OK;
icculus@0
   219
  L->errfunc = 0;
icculus@0
   220
}
icculus@0
   221
icculus@0
   222
icculus@0
   223
static void close_state (lua_State *L) {
icculus@0
   224
  global_State *g = G(L);
icculus@0
   225
  luaF_close(L, L->stack);  /* close all upvalues for this thread */
icculus@0
   226
  luaC_freeallobjects(L);  /* collect all objects */
icculus@0
   227
  if (g->version)  /* closing a fully built state? */
icculus@0
   228
    luai_userstateclose(L);
icculus@0
   229
  luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
icculus@0
   230
  luaZ_freebuffer(L, &g->buff);
icculus@0
   231
  freestack(L);
icculus@0
   232
  lua_assert(gettotalbytes(g) == sizeof(LG));
icculus@0
   233
  (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0);  /* free main block */
icculus@0
   234
}
icculus@0
   235
icculus@0
   236
icculus@0
   237
LUA_API lua_State *lua_newthread (lua_State *L) {
icculus@0
   238
  lua_State *L1;
icculus@0
   239
  lua_lock(L);
icculus@0
   240
  luaC_checkGC(L);
icculus@0
   241
  L1 = &luaC_newobj(L, LUA_TTHREAD, sizeof(LX), NULL, offsetof(LX, l))->th;
icculus@0
   242
  setthvalue(L, L->top, L1);
icculus@0
   243
  api_incr_top(L);
icculus@0
   244
  preinit_state(L1, G(L));
icculus@0
   245
  L1->hookmask = L->hookmask;
icculus@0
   246
  L1->basehookcount = L->basehookcount;
icculus@0
   247
  L1->hook = L->hook;
icculus@0
   248
  resethookcount(L1);
icculus@0
   249
  luai_userstatethread(L, L1);
icculus@0
   250
  stack_init(L1, L);  /* init stack */
icculus@0
   251
  lua_unlock(L);
icculus@0
   252
  return L1;
icculus@0
   253
}
icculus@0
   254
icculus@0
   255
icculus@0
   256
void luaE_freethread (lua_State *L, lua_State *L1) {
icculus@0
   257
  LX *l = fromstate(L1);
icculus@0
   258
  luaF_close(L1, L1->stack);  /* close all upvalues for this thread */
icculus@0
   259
  lua_assert(L1->openupval == NULL);
icculus@0
   260
  luai_userstatefree(L, L1);
icculus@0
   261
  freestack(L1);
icculus@0
   262
  luaM_free(L, l);
icculus@0
   263
}
icculus@0
   264
icculus@0
   265
icculus@0
   266
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
icculus@0
   267
  int i;
icculus@0
   268
  lua_State *L;
icculus@0
   269
  global_State *g;
icculus@0
   270
  LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
icculus@0
   271
  if (l == NULL) return NULL;
icculus@0
   272
  L = &l->l.l;
icculus@0
   273
  g = &l->g;
icculus@0
   274
  L->next = NULL;
icculus@0
   275
  L->tt = LUA_TTHREAD;
icculus@0
   276
  g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
icculus@0
   277
  L->marked = luaC_white(g);
icculus@0
   278
  g->gckind = KGC_NORMAL;
icculus@0
   279
  preinit_state(L, g);
icculus@0
   280
  g->frealloc = f;
icculus@0
   281
  g->ud = ud;
icculus@0
   282
  g->mainthread = L;
icculus@0
   283
  g->seed = makeseed(L);
icculus@0
   284
  g->uvhead.u.l.prev = &g->uvhead;
icculus@0
   285
  g->uvhead.u.l.next = &g->uvhead;
icculus@0
   286
  g->gcrunning = 0;  /* no GC while building state */
icculus@0
   287
  g->GCestimate = 0;
icculus@0
   288
  g->strt.size = 0;
icculus@0
   289
  g->strt.nuse = 0;
icculus@0
   290
  g->strt.hash = NULL;
icculus@0
   291
  setnilvalue(&g->l_registry);
icculus@0
   292
  luaZ_initbuffer(L, &g->buff);
icculus@0
   293
  g->panic = NULL;
icculus@0
   294
  g->version = NULL;
icculus@0
   295
  g->gcstate = GCSpause;
icculus@0
   296
  g->allgc = NULL;
icculus@0
   297
  g->finobj = NULL;
icculus@0
   298
  g->tobefnz = NULL;
icculus@0
   299
  g->sweepgc = g->sweepfin = NULL;
icculus@0
   300
  g->gray = g->grayagain = NULL;
icculus@0
   301
  g->weak = g->ephemeron = g->allweak = NULL;
icculus@0
   302
  g->totalbytes = sizeof(LG);
icculus@0
   303
  g->GCdebt = 0;
icculus@0
   304
  g->gcpause = LUAI_GCPAUSE;
icculus@0
   305
  g->gcmajorinc = LUAI_GCMAJOR;
icculus@0
   306
  g->gcstepmul = LUAI_GCMUL;
icculus@0
   307
  for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
icculus@0
   308
  if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
icculus@0
   309
    /* memory allocation error: free partial state */
icculus@0
   310
    close_state(L);
icculus@0
   311
    L = NULL;
icculus@0
   312
  }
icculus@0
   313
  return L;
icculus@0
   314
}
icculus@0
   315
icculus@0
   316
icculus@0
   317
LUA_API void lua_close (lua_State *L) {
icculus@0
   318
  L = G(L)->mainthread;  /* only the main thread can be closed */
icculus@0
   319
  lua_lock(L);
icculus@0
   320
  close_state(L);
icculus@0
   321
}
icculus@0
   322
icculus@0
   323