lua/ldblib.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: ldblib.c,v 1.132.1.1 2013/04/12 18:48:47 roberto Exp $
icculus@0
     3
** Interface from Lua to its debug API
icculus@0
     4
** See Copyright Notice in lua.h
icculus@0
     5
*/
icculus@0
     6
icculus@0
     7
icculus@0
     8
#include <stdio.h>
icculus@0
     9
#include <stdlib.h>
icculus@0
    10
#include <string.h>
icculus@0
    11
icculus@0
    12
#define ldblib_c
icculus@0
    13
#define LUA_LIB
icculus@0
    14
icculus@0
    15
#include "lua.h"
icculus@0
    16
icculus@0
    17
#include "lauxlib.h"
icculus@0
    18
#include "lualib.h"
icculus@0
    19
icculus@0
    20
icculus@0
    21
#define HOOKKEY		"_HKEY"
icculus@0
    22
icculus@0
    23
icculus@0
    24
icculus@0
    25
static int db_getregistry (lua_State *L) {
icculus@0
    26
  lua_pushvalue(L, LUA_REGISTRYINDEX);
icculus@0
    27
  return 1;
icculus@0
    28
}
icculus@0
    29
icculus@0
    30
icculus@0
    31
static int db_getmetatable (lua_State *L) {
icculus@0
    32
  luaL_checkany(L, 1);
icculus@0
    33
  if (!lua_getmetatable(L, 1)) {
icculus@0
    34
    lua_pushnil(L);  /* no metatable */
icculus@0
    35
  }
icculus@0
    36
  return 1;
icculus@0
    37
}
icculus@0
    38
icculus@0
    39
icculus@0
    40
static int db_setmetatable (lua_State *L) {
icculus@0
    41
  int t = lua_type(L, 2);
icculus@0
    42
  luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
icculus@0
    43
                    "nil or table expected");
icculus@0
    44
  lua_settop(L, 2);
icculus@0
    45
  lua_setmetatable(L, 1);
icculus@0
    46
  return 1;  /* return 1st argument */
icculus@0
    47
}
icculus@0
    48
icculus@0
    49
icculus@0
    50
static int db_getuservalue (lua_State *L) {
icculus@0
    51
  if (lua_type(L, 1) != LUA_TUSERDATA)
icculus@0
    52
    lua_pushnil(L);
icculus@0
    53
  else
icculus@0
    54
    lua_getuservalue(L, 1);
icculus@0
    55
  return 1;
icculus@0
    56
}
icculus@0
    57
icculus@0
    58
icculus@0
    59
static int db_setuservalue (lua_State *L) {
icculus@0
    60
  if (lua_type(L, 1) == LUA_TLIGHTUSERDATA)
icculus@0
    61
    luaL_argerror(L, 1, "full userdata expected, got light userdata");
icculus@0
    62
  luaL_checktype(L, 1, LUA_TUSERDATA);
icculus@0
    63
  if (!lua_isnoneornil(L, 2))
icculus@0
    64
    luaL_checktype(L, 2, LUA_TTABLE);
icculus@0
    65
  lua_settop(L, 2);
icculus@0
    66
  lua_setuservalue(L, 1);
icculus@0
    67
  return 1;
icculus@0
    68
}
icculus@0
    69
icculus@0
    70
icculus@0
    71
static void settabss (lua_State *L, const char *i, const char *v) {
icculus@0
    72
  lua_pushstring(L, v);
icculus@0
    73
  lua_setfield(L, -2, i);
icculus@0
    74
}
icculus@0
    75
icculus@0
    76
icculus@0
    77
static void settabsi (lua_State *L, const char *i, int v) {
icculus@0
    78
  lua_pushinteger(L, v);
icculus@0
    79
  lua_setfield(L, -2, i);
icculus@0
    80
}
icculus@0
    81
icculus@0
    82
icculus@0
    83
static void settabsb (lua_State *L, const char *i, int v) {
icculus@0
    84
  lua_pushboolean(L, v);
icculus@0
    85
  lua_setfield(L, -2, i);
icculus@0
    86
}
icculus@0
    87
icculus@0
    88
icculus@0
    89
static lua_State *getthread (lua_State *L, int *arg) {
icculus@0
    90
  if (lua_isthread(L, 1)) {
icculus@0
    91
    *arg = 1;
icculus@0
    92
    return lua_tothread(L, 1);
icculus@0
    93
  }
icculus@0
    94
  else {
icculus@0
    95
    *arg = 0;
icculus@0
    96
    return L;
icculus@0
    97
  }
icculus@0
    98
}
icculus@0
    99
icculus@0
   100
icculus@0
   101
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
icculus@0
   102
  if (L == L1) {
icculus@0
   103
    lua_pushvalue(L, -2);
icculus@0
   104
    lua_remove(L, -3);
icculus@0
   105
  }
icculus@0
   106
  else
icculus@0
   107
    lua_xmove(L1, L, 1);
icculus@0
   108
  lua_setfield(L, -2, fname);
icculus@0
   109
}
icculus@0
   110
icculus@0
   111
icculus@0
   112
static int db_getinfo (lua_State *L) {
icculus@0
   113
  lua_Debug ar;
icculus@0
   114
  int arg;
icculus@0
   115
  lua_State *L1 = getthread(L, &arg);
icculus@0
   116
  const char *options = luaL_optstring(L, arg+2, "flnStu");
icculus@0
   117
  if (lua_isnumber(L, arg+1)) {
icculus@0
   118
    if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
icculus@0
   119
      lua_pushnil(L);  /* level out of range */
icculus@0
   120
      return 1;
icculus@0
   121
    }
icculus@0
   122
  }
icculus@0
   123
  else if (lua_isfunction(L, arg+1)) {
icculus@0
   124
    lua_pushfstring(L, ">%s", options);
icculus@0
   125
    options = lua_tostring(L, -1);
icculus@0
   126
    lua_pushvalue(L, arg+1);
icculus@0
   127
    lua_xmove(L, L1, 1);
icculus@0
   128
  }
icculus@0
   129
  else
icculus@0
   130
    return luaL_argerror(L, arg+1, "function or level expected");
icculus@0
   131
  if (!lua_getinfo(L1, options, &ar))
icculus@0
   132
    return luaL_argerror(L, arg+2, "invalid option");
icculus@0
   133
  lua_createtable(L, 0, 2);
icculus@0
   134
  if (strchr(options, 'S')) {
icculus@0
   135
    settabss(L, "source", ar.source);
icculus@0
   136
    settabss(L, "short_src", ar.short_src);
icculus@0
   137
    settabsi(L, "linedefined", ar.linedefined);
icculus@0
   138
    settabsi(L, "lastlinedefined", ar.lastlinedefined);
icculus@0
   139
    settabss(L, "what", ar.what);
icculus@0
   140
  }
icculus@0
   141
  if (strchr(options, 'l'))
icculus@0
   142
    settabsi(L, "currentline", ar.currentline);
icculus@0
   143
  if (strchr(options, 'u')) {
icculus@0
   144
    settabsi(L, "nups", ar.nups);
icculus@0
   145
    settabsi(L, "nparams", ar.nparams);
icculus@0
   146
    settabsb(L, "isvararg", ar.isvararg);
icculus@0
   147
  }
icculus@0
   148
  if (strchr(options, 'n')) {
icculus@0
   149
    settabss(L, "name", ar.name);
icculus@0
   150
    settabss(L, "namewhat", ar.namewhat);
icculus@0
   151
  }
icculus@0
   152
  if (strchr(options, 't'))
icculus@0
   153
    settabsb(L, "istailcall", ar.istailcall);
icculus@0
   154
  if (strchr(options, 'L'))
icculus@0
   155
    treatstackoption(L, L1, "activelines");
icculus@0
   156
  if (strchr(options, 'f'))
icculus@0
   157
    treatstackoption(L, L1, "func");
icculus@0
   158
  return 1;  /* return table */
icculus@0
   159
}
icculus@0
   160
icculus@0
   161
icculus@0
   162
static int db_getlocal (lua_State *L) {
icculus@0
   163
  int arg;
icculus@0
   164
  lua_State *L1 = getthread(L, &arg);
icculus@0
   165
  lua_Debug ar;
icculus@0
   166
  const char *name;
icculus@0
   167
  int nvar = luaL_checkint(L, arg+2);  /* local-variable index */
icculus@0
   168
  if (lua_isfunction(L, arg + 1)) {  /* function argument? */
icculus@0
   169
    lua_pushvalue(L, arg + 1);  /* push function */
icculus@0
   170
    lua_pushstring(L, lua_getlocal(L, NULL, nvar));  /* push local name */
icculus@0
   171
    return 1;
icculus@0
   172
  }
icculus@0
   173
  else {  /* stack-level argument */
icculus@0
   174
    if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar))  /* out of range? */
icculus@0
   175
      return luaL_argerror(L, arg+1, "level out of range");
icculus@0
   176
    name = lua_getlocal(L1, &ar, nvar);
icculus@0
   177
    if (name) {
icculus@0
   178
      lua_xmove(L1, L, 1);  /* push local value */
icculus@0
   179
      lua_pushstring(L, name);  /* push name */
icculus@0
   180
      lua_pushvalue(L, -2);  /* re-order */
icculus@0
   181
      return 2;
icculus@0
   182
    }
icculus@0
   183
    else {
icculus@0
   184
      lua_pushnil(L);  /* no name (nor value) */
icculus@0
   185
      return 1;
icculus@0
   186
    }
icculus@0
   187
  }
icculus@0
   188
}
icculus@0
   189
icculus@0
   190
icculus@0
   191
static int db_setlocal (lua_State *L) {
icculus@0
   192
  int arg;
icculus@0
   193
  lua_State *L1 = getthread(L, &arg);
icculus@0
   194
  lua_Debug ar;
icculus@0
   195
  if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar))  /* out of range? */
icculus@0
   196
    return luaL_argerror(L, arg+1, "level out of range");
icculus@0
   197
  luaL_checkany(L, arg+3);
icculus@0
   198
  lua_settop(L, arg+3);
icculus@0
   199
  lua_xmove(L, L1, 1);
icculus@0
   200
  lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
icculus@0
   201
  return 1;
icculus@0
   202
}
icculus@0
   203
icculus@0
   204
icculus@0
   205
static int auxupvalue (lua_State *L, int get) {
icculus@0
   206
  const char *name;
icculus@0
   207
  int n = luaL_checkint(L, 2);
icculus@0
   208
  luaL_checktype(L, 1, LUA_TFUNCTION);
icculus@0
   209
  name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
icculus@0
   210
  if (name == NULL) return 0;
icculus@0
   211
  lua_pushstring(L, name);
icculus@0
   212
  lua_insert(L, -(get+1));
icculus@0
   213
  return get + 1;
icculus@0
   214
}
icculus@0
   215
icculus@0
   216
icculus@0
   217
static int db_getupvalue (lua_State *L) {
icculus@0
   218
  return auxupvalue(L, 1);
icculus@0
   219
}
icculus@0
   220
icculus@0
   221
icculus@0
   222
static int db_setupvalue (lua_State *L) {
icculus@0
   223
  luaL_checkany(L, 3);
icculus@0
   224
  return auxupvalue(L, 0);
icculus@0
   225
}
icculus@0
   226
icculus@0
   227
icculus@0
   228
static int checkupval (lua_State *L, int argf, int argnup) {
icculus@0
   229
  lua_Debug ar;
icculus@0
   230
  int nup = luaL_checkint(L, argnup);
icculus@0
   231
  luaL_checktype(L, argf, LUA_TFUNCTION);
icculus@0
   232
  lua_pushvalue(L, argf);
icculus@0
   233
  lua_getinfo(L, ">u", &ar);
icculus@0
   234
  luaL_argcheck(L, 1 <= nup && nup <= ar.nups, argnup, "invalid upvalue index");
icculus@0
   235
  return nup;
icculus@0
   236
}
icculus@0
   237
icculus@0
   238
icculus@0
   239
static int db_upvalueid (lua_State *L) {
icculus@0
   240
  int n = checkupval(L, 1, 2);
icculus@0
   241
  lua_pushlightuserdata(L, lua_upvalueid(L, 1, n));
icculus@0
   242
  return 1;
icculus@0
   243
}
icculus@0
   244
icculus@0
   245
icculus@0
   246
static int db_upvaluejoin (lua_State *L) {
icculus@0
   247
  int n1 = checkupval(L, 1, 2);
icculus@0
   248
  int n2 = checkupval(L, 3, 4);
icculus@0
   249
  luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
icculus@0
   250
  luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
icculus@0
   251
  lua_upvaluejoin(L, 1, n1, 3, n2);
icculus@0
   252
  return 0;
icculus@0
   253
}
icculus@0
   254
icculus@0
   255
icculus@0
   256
#define gethooktable(L)	luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)
icculus@0
   257
icculus@0
   258
icculus@0
   259
static void hookf (lua_State *L, lua_Debug *ar) {
icculus@0
   260
  static const char *const hooknames[] =
icculus@0
   261
    {"call", "return", "line", "count", "tail call"};
icculus@0
   262
  gethooktable(L);
icculus@0
   263
  lua_pushthread(L);
icculus@0
   264
  lua_rawget(L, -2);
icculus@0
   265
  if (lua_isfunction(L, -1)) {
icculus@0
   266
    lua_pushstring(L, hooknames[(int)ar->event]);
icculus@0
   267
    if (ar->currentline >= 0)
icculus@0
   268
      lua_pushinteger(L, ar->currentline);
icculus@0
   269
    else lua_pushnil(L);
icculus@0
   270
    lua_assert(lua_getinfo(L, "lS", ar));
icculus@0
   271
    lua_call(L, 2, 0);
icculus@0
   272
  }
icculus@0
   273
}
icculus@0
   274
icculus@0
   275
icculus@0
   276
static int makemask (const char *smask, int count) {
icculus@0
   277
  int mask = 0;
icculus@0
   278
  if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
icculus@0
   279
  if (strchr(smask, 'r')) mask |= LUA_MASKRET;
icculus@0
   280
  if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
icculus@0
   281
  if (count > 0) mask |= LUA_MASKCOUNT;
icculus@0
   282
  return mask;
icculus@0
   283
}
icculus@0
   284
icculus@0
   285
icculus@0
   286
static char *unmakemask (int mask, char *smask) {
icculus@0
   287
  int i = 0;
icculus@0
   288
  if (mask & LUA_MASKCALL) smask[i++] = 'c';
icculus@0
   289
  if (mask & LUA_MASKRET) smask[i++] = 'r';
icculus@0
   290
  if (mask & LUA_MASKLINE) smask[i++] = 'l';
icculus@0
   291
  smask[i] = '\0';
icculus@0
   292
  return smask;
icculus@0
   293
}
icculus@0
   294
icculus@0
   295
icculus@0
   296
static int db_sethook (lua_State *L) {
icculus@0
   297
  int arg, mask, count;
icculus@0
   298
  lua_Hook func;
icculus@0
   299
  lua_State *L1 = getthread(L, &arg);
icculus@0
   300
  if (lua_isnoneornil(L, arg+1)) {
icculus@0
   301
    lua_settop(L, arg+1);
icculus@0
   302
    func = NULL; mask = 0; count = 0;  /* turn off hooks */
icculus@0
   303
  }
icculus@0
   304
  else {
icculus@0
   305
    const char *smask = luaL_checkstring(L, arg+2);
icculus@0
   306
    luaL_checktype(L, arg+1, LUA_TFUNCTION);
icculus@0
   307
    count = luaL_optint(L, arg+3, 0);
icculus@0
   308
    func = hookf; mask = makemask(smask, count);
icculus@0
   309
  }
icculus@0
   310
  if (gethooktable(L) == 0) {  /* creating hook table? */
icculus@0
   311
    lua_pushstring(L, "k");
icculus@0
   312
    lua_setfield(L, -2, "__mode");  /** hooktable.__mode = "k" */
icculus@0
   313
    lua_pushvalue(L, -1);
icculus@0
   314
    lua_setmetatable(L, -2);  /* setmetatable(hooktable) = hooktable */
icculus@0
   315
  }
icculus@0
   316
  lua_pushthread(L1); lua_xmove(L1, L, 1);
icculus@0
   317
  lua_pushvalue(L, arg+1);
icculus@0
   318
  lua_rawset(L, -3);  /* set new hook */
icculus@0
   319
  lua_sethook(L1, func, mask, count);  /* set hooks */
icculus@0
   320
  return 0;
icculus@0
   321
}
icculus@0
   322
icculus@0
   323
icculus@0
   324
static int db_gethook (lua_State *L) {
icculus@0
   325
  int arg;
icculus@0
   326
  lua_State *L1 = getthread(L, &arg);
icculus@0
   327
  char buff[5];
icculus@0
   328
  int mask = lua_gethookmask(L1);
icculus@0
   329
  lua_Hook hook = lua_gethook(L1);
icculus@0
   330
  if (hook != NULL && hook != hookf)  /* external hook? */
icculus@0
   331
    lua_pushliteral(L, "external hook");
icculus@0
   332
  else {
icculus@0
   333
    gethooktable(L);
icculus@0
   334
    lua_pushthread(L1); lua_xmove(L1, L, 1);
icculus@0
   335
    lua_rawget(L, -2);   /* get hook */
icculus@0
   336
    lua_remove(L, -2);  /* remove hook table */
icculus@0
   337
  }
icculus@0
   338
  lua_pushstring(L, unmakemask(mask, buff));
icculus@0
   339
  lua_pushinteger(L, lua_gethookcount(L1));
icculus@0
   340
  return 3;
icculus@0
   341
}
icculus@0
   342
icculus@0
   343
icculus@0
   344
static int db_debug (lua_State *L) {
icculus@0
   345
  for (;;) {
icculus@0
   346
    char buffer[250];
icculus@0
   347
    luai_writestringerror("%s", "lua_debug> ");
icculus@0
   348
    if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
icculus@0
   349
        strcmp(buffer, "cont\n") == 0)
icculus@0
   350
      return 0;
icculus@0
   351
    if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
icculus@0
   352
        lua_pcall(L, 0, 0, 0))
icculus@0
   353
      luai_writestringerror("%s\n", lua_tostring(L, -1));
icculus@0
   354
    lua_settop(L, 0);  /* remove eventual returns */
icculus@0
   355
  }
icculus@0
   356
}
icculus@0
   357
icculus@0
   358
icculus@0
   359
static int db_traceback (lua_State *L) {
icculus@0
   360
  int arg;
icculus@0
   361
  lua_State *L1 = getthread(L, &arg);
icculus@0
   362
  const char *msg = lua_tostring(L, arg + 1);
icculus@0
   363
  if (msg == NULL && !lua_isnoneornil(L, arg + 1))  /* non-string 'msg'? */
icculus@0
   364
    lua_pushvalue(L, arg + 1);  /* return it untouched */
icculus@0
   365
  else {
icculus@0
   366
    int level = luaL_optint(L, arg + 2, (L == L1) ? 1 : 0);
icculus@0
   367
    luaL_traceback(L, L1, msg, level);
icculus@0
   368
  }
icculus@0
   369
  return 1;
icculus@0
   370
}
icculus@0
   371
icculus@0
   372
icculus@0
   373
static const luaL_Reg dblib[] = {
icculus@0
   374
  {"debug", db_debug},
icculus@0
   375
  {"getuservalue", db_getuservalue},
icculus@0
   376
  {"gethook", db_gethook},
icculus@0
   377
  {"getinfo", db_getinfo},
icculus@0
   378
  {"getlocal", db_getlocal},
icculus@0
   379
  {"getregistry", db_getregistry},
icculus@0
   380
  {"getmetatable", db_getmetatable},
icculus@0
   381
  {"getupvalue", db_getupvalue},
icculus@0
   382
  {"upvaluejoin", db_upvaluejoin},
icculus@0
   383
  {"upvalueid", db_upvalueid},
icculus@0
   384
  {"setuservalue", db_setuservalue},
icculus@0
   385
  {"sethook", db_sethook},
icculus@0
   386
  {"setlocal", db_setlocal},
icculus@0
   387
  {"setmetatable", db_setmetatable},
icculus@0
   388
  {"setupvalue", db_setupvalue},
icculus@0
   389
  {"traceback", db_traceback},
icculus@0
   390
  {NULL, NULL}
icculus@0
   391
};
icculus@0
   392
icculus@0
   393
icculus@0
   394
LUAMOD_API int luaopen_debug (lua_State *L) {
icculus@0
   395
  luaL_newlib(L, dblib);
icculus@0
   396
  return 1;
icculus@0
   397
}
icculus@0
   398