From c21fc065608894f53356626f751d62ceb19cf251 Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 27 Jun 2012 21:28:18 +1000 Subject: [PATCH 1/5] define API option --api-groups --- cgminer.c | 15 ++++++++++++++- miner.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index 69181b00..c946b6a4 100644 --- a/cgminer.c +++ b/cgminer.c @@ -133,6 +133,7 @@ bool opt_autofan; bool opt_autoengine; bool opt_noadl; char *opt_api_allow = NULL; +char *opt_api_groups; char *opt_api_description = PACKAGE_STRING; int opt_api_port = 4028; bool opt_api_listen; @@ -682,6 +683,13 @@ static char *set_api_allow(const char *arg) return NULL; } +static char *set_api_groups(const char *arg) +{ + opt_set_charp(arg, &opt_api_groups); + + return NULL; +} + static char *set_api_description(const char *arg) { opt_set_charp(arg, &opt_api_description); @@ -732,10 +740,13 @@ static struct opt_table opt_config_table[] = { #endif OPT_WITH_ARG("--api-allow", set_api_allow, NULL, NULL, - "Allow API access only to the given list of IP[/Prefix] addresses[/subnets]"), + "Allow API access only to the given list of [G:]IP[/Prefix] addresses[/subnets]"), OPT_WITH_ARG("--api-description", set_api_description, NULL, NULL, "Description placed in the API status header, default: cgminer version"), + OPT_WITH_ARG("--api-groups", + set_api_groups, NULL, NULL, + "API one letter groups G:cmd:cmd[,P:cmd:*...] defining the cmds a groups can use"), OPT_WITHOUT_ARG("--api-listen", opt_set_bool, &opt_api_listen, "Enable API, default: disabled"), @@ -2871,6 +2882,8 @@ void write_config(FILE *fcfg) fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow); if (strcmp(opt_api_description, PACKAGE_STRING) != 0) fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description); + if (opt_api_groups) + fprintf(fcfg, ",\n\"api-groups\" : \"%s\"", opt_api_groups); if (opt_icarus_timing) fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", opt_icarus_timing); fputs("\n}", fcfg); diff --git a/miner.h b/miner.h index 5b44ceef..bb567598 100644 --- a/miner.h +++ b/miner.h @@ -524,6 +524,7 @@ extern bool opt_autofan; extern bool opt_autoengine; extern bool use_curses; extern char *opt_api_allow; +extern char *opt_api_groups; extern char *opt_api_description; extern int opt_api_port; extern bool opt_api_listen; From 659e50434ea2c84181d822bdd9719d5c35a9ef5b Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 28 Jun 2012 02:29:58 +1000 Subject: [PATCH 2/5] implement and document API option --api-groups --- API-README | 39 ++++++- README | 2 + api.c | 333 ++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 313 insertions(+), 61 deletions(-) diff --git a/API-README b/API-README index 6f5fbe12..fc71271e 100644 --- a/API-README +++ b/API-README @@ -19,8 +19,9 @@ IP addresses are automatically padded with extra '.0's as needed Without a /prefix is the same as specifying /32 0/0 means all IP addresses. The 'W:' on the front gives that address/subnet privileged access to commands -that modify cgminer. +that modify cgminer (thus all API commands) Without it those commands return an access denied status. +See --api-groups below to define other groups like W: Privileged access is checked in the order the IP addresses were supplied to "--api-allow" The first match determines the privilege level. @@ -28,6 +29,23 @@ Using the "--api-allow" option overides the "--api-network" option if they are both specified With "--api-allow", 127.0.0.1 is not by default given access unless specified +More groups (like the privileged group W:) can be defined using the +--api-groups command +Valid groups are only the letters A-V,X-Z and are not case sensitive +To give an IP address/subnet access to a group you the use group letter +in front of the IP address instead of W: e.g. P:192.168.0/32 +An IP address/subnet can only be a member of one group +A sample API group would be: + --api-groups P:switchpool:enablepool:addpool:disablepool:removepool:* +This would create a group 'P' that can do all current pool commands and all +non-priviliged commands - the '*' means all non-priviledged commands +Without the '*' the group would only have access to the pool commands +Defining multiple groups example: + --api-groups Q:quit:restart:*,S:save +This would define 2 groups: + Q: that can 'quit' and 'restart' as well as all non-priviledged commands + S: that can only 'save' and no other commands + The RPC API request can be either simple text or JSON. If the request is JSON (starts with '{'), it will reply with a JSON formatted @@ -86,8 +104,8 @@ The list of requests - a (*) means it requires privileged access - and replies a Request Reply Section Details ------- ------------- ------- - version VERSION CGMiner=cgminer version - API=API version + version VERSION CGMiner=cgminer, version + API=API| version config CONFIG Some miner configuration information: GPU Count=N, <- the number of GPUs @@ -243,6 +261,9 @@ The list of requests - a (*) means it requires privileged access - and replies a Device drivers are also able to add stats to the end of the details returned + check|cmd COMMAND Exists=Y/N, <- 'cmd' exists in this version + Access=Y/N| <- you have access to use 'cmd' + When you enable, disable or restart a GPU or PGA, you will also get Thread messages in the cgminer status window @@ -285,7 +306,17 @@ miner.php - an example web page to access the API Feature Changelog for external applications using the API: -API V1.12 +API V1.13 + +Added API commands: + 'checkcommand' + +Support was added to cgminer for API access groups with the --api-groups option +It's 100% backwards compatible with previous --api-access commands + +---------- + +API V1.12 (cgminer v2.4.3) Modified API commands: 'stats' - more pool stats added diff --git a/README b/README index 862ec544..58e89b93 100644 --- a/README +++ b/README @@ -119,6 +119,8 @@ Options for both config file and command line: This overrides --api-network and you must specify 127.0.0.1 if it is required W: in front of the IP address gives that address privileged access to all api commands --api-description Description placed in the API status header (default: cgminer version) +--api-groups API one letter groups G:cmd:cmd[,P:cmd:*...] + See API-README for usage --api-listen Listen for API requests (default: disabled) By default any command that does not just display data returns access denied See --api-allow to overcome this diff --git a/api.c b/api.c index ac5b9a6b..13cb11c9 100644 --- a/api.c +++ b/api.c @@ -158,6 +158,7 @@ static char *msg_buffer = NULL; static SOCKETTYPE sock = INVSOCK; static const char *UNAVAILABLE = " - API will not be available"; +static const char *GROUPDIS = " - groups will be disabled"; static const char *BLANK = ""; static const char *COMMA = ","; @@ -165,7 +166,7 @@ static const char SEPARATOR = '|'; #define SEPSTR "|" static const char GPUSEP = ','; -static const char *APIVERSION = "1.12"; +static const char *APIVERSION = "1.13"; static const char *DEAD = "Dead"; static const char *SICK = "Sick"; static const char *NOSTART = "NoStart"; @@ -243,6 +244,7 @@ static const char *OSINFO = #define _BYE "BYE" #define _RESTART "RESTART" #define _MINESTATS "STATS" +#define _CHECK "CHECK" static const char ISJSON = '{'; #define JSON0 "{" @@ -277,6 +279,7 @@ static const char ISJSON = '{'; #define JSON_RESTART JSON1 _RESTART JSON1 #define JSON_CLOSE JSON3 #define JSON_MINESTATS JSON1 _MINESTATS JSON2 +#define JSON_CHECK JSON1 _CHECK JSON2 #define JSON_END JSON4 static const char *JSON_COMMAND = "command"; @@ -364,6 +367,8 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_REMPOOL 68 #define MSG_DEVDETAILS 69 #define MSG_MINESTATS 70 +#define MSG_MISCHK 71 +#define MSG_CHECK 72 enum code_severity { SEVERITY_ERR, @@ -507,6 +512,8 @@ struct CODES { { SEVERITY_SUCC, MSG_NOTIFY, PARAM_NONE, "Notify" }, { SEVERITY_SUCC, MSG_DEVDETAILS,PARAM_NONE, "Device Details" }, { SEVERITY_SUCC, MSG_MINESTATS,PARAM_NONE, "CGMiner stats" }, + { SEVERITY_ERR, MSG_MISCHK, PARAM_NONE, "Missing check cmd" }, + { SEVERITY_SUCC, MSG_CHECK, PARAM_NONE, "Check command" }, { SEVERITY_FAIL, 0, 0, NULL } }; @@ -525,9 +532,25 @@ static time_t when = 0; // when the request occurred struct IP4ACCESS { in_addr_t ip; in_addr_t mask; - bool writemode; + char group; }; +#define GROUP(g) (toupper(g)) +#define PRIVGROUP GROUP('W') +#define NOPRIVGROUP GROUP('R') +#define ISPRIVGROUP(g) (GROUP(g) == PRIVGROUP) +#define GROUPOFFSET(g) (GROUP(g) - GROUP('A')) +#define VALIDGROUP(g) (GROUP(g) >= GROUP('A') && GROUP(g) <= GROUP('Z')) +#define COMMANDS(g) (apigroups[GROUPOFFSET(g)].commands) +#define DEFINEDGROUP(g) (ISPRIVGROUP(g) || COMMANDS(g) != NULL) + +struct APIGROUPS { + // This becomes a string like: "|cmd1|cmd2|cmd3|" so it's quick to search + char *commands; +} apigroups['Z' - 'A' + 1]; // only A=0 to Z=25 (R: noprivs, W: allprivs) + +static bool groups_enabled = false; + static struct IP4ACCESS *ipaccess = NULL; static int ips = 0; @@ -796,7 +819,7 @@ static char *message(int messageid, int paramid, char *param2, bool isjson) return msg_buffer; } -static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { sprintf(io_buffer, isjson ? "%s," JSON_VERSION "{\"CGMiner\":\"%s\",\"API\":\"%s\"}" JSON_CLOSE @@ -805,7 +828,7 @@ static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, VERSION, APIVERSION); } -static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; int gpucount = 0; @@ -1003,7 +1026,7 @@ static void cpustatus(int cpu, bool isjson) } #endif -static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { int devcount = 0; int numgpu = 0; @@ -1069,7 +1092,7 @@ static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, b } #ifdef HAVE_OPENCL -static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int id; @@ -1103,7 +1126,7 @@ static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) } #endif #ifdef HAVE_AN_FPGA -static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int numpga = numpgas(); int id; @@ -1137,7 +1160,7 @@ static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcat(io_buffer, JSON_CLOSE); } -static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int numpga = numpgas(); struct thr_info *thr; @@ -1193,7 +1216,7 @@ static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_PGAENA, id, NULL, isjson)); } -static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int numpga = numpgas(); int id; @@ -1234,7 +1257,7 @@ static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) #endif #ifdef WANT_CPUMINE -static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int id; @@ -1268,7 +1291,7 @@ static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) } #endif -static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; char *status, *lp; @@ -1345,7 +1368,7 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, strcat(io_buffer, JSON_CLOSE); } -static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { double utility, mhs; @@ -1379,7 +1402,7 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo #endif } #ifdef HAVE_OPENCL -static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct thr_info *thr; int gpu; @@ -1425,7 +1448,7 @@ static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_GPUREN, id, NULL, isjson)); } -static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int id; @@ -1455,7 +1478,7 @@ static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_GPUDIS, id, NULL, isjson)); } -static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int id; @@ -1480,7 +1503,7 @@ static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_GPUREI, id, NULL, isjson)); } #endif -static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; int numgpu = 0; @@ -1499,8 +1522,7 @@ static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo strcat(io_buffer, buf); } - -static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; int count = 0; @@ -1519,7 +1541,7 @@ static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo strcat(io_buffer, buf); } -static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; int count = 0; @@ -1538,7 +1560,7 @@ static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo strcat(io_buffer, buf); } -static void switchpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void switchpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct pool *pool; int id; @@ -1619,7 +1641,7 @@ exitsama: return false; } -static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { char *url, *user, *pass; char *ptr; @@ -1647,7 +1669,7 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) ptr = NULL; } -static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct pool *pool; int id; @@ -1681,7 +1703,7 @@ static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_ENAPOOL, id, NULL, isjson)); } -static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct pool *pool; int id; @@ -1720,7 +1742,7 @@ static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_DISPOOL, id, NULL, isjson)); } -static void removepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void removepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct pool *pool; char *rpc_url; @@ -1806,7 +1828,7 @@ static bool splitgpuvalue(char *param, int *gpu, char **value, bool isjson) return true; } -static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { int id; char *value; @@ -1835,7 +1857,7 @@ static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_GPUINT, id, intensitystr, isjson)); } -static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { #ifdef HAVE_ADL int id; @@ -1856,7 +1878,7 @@ static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool #endif } -static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { #ifdef HAVE_ADL int id; @@ -1877,7 +1899,7 @@ static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, b #endif } -static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { #ifdef HAVE_ADL int id; @@ -1898,7 +1920,7 @@ static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool #endif } -static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { #ifdef HAVE_ADL int id; @@ -1919,7 +1941,7 @@ static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo #endif } #endif -void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { if (isjson) strcpy(io_buffer, JSON_START JSON_BYE); @@ -1930,7 +1952,7 @@ void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson do_a_quit = true; } -void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { if (isjson) strcpy(io_buffer, JSON_START JSON_RESTART); @@ -1941,12 +1963,12 @@ void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isj do_a_restart = true; } -void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { strcpy(io_buffer, message(MSG_ACCOK, 0, NULL, isjson)); } -void notifystatus(int device, struct cgpu_info *cgpu, bool isjson) +void notifystatus(int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; char *reason; @@ -2000,7 +2022,7 @@ void notifystatus(int device, struct cgpu_info *cgpu, bool isjson) strcat(io_buffer, buf); } -static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { int i; @@ -2017,13 +2039,13 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool } for (i = 0; i < total_devices; i++) - notifystatus(i, devices[i], isjson); + notifystatus(i, devices[i], isjson, group); if (isjson) strcat(io_buffer, JSON_CLOSE); } -static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) +static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char buf[TMPBUFSIZ]; struct cgpu_info *cgpu; @@ -2059,7 +2081,7 @@ static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, strcat(io_buffer, JSON_CLOSE); } -void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { char filename[PATH_MAX]; FILE *fcfg; @@ -2135,7 +2157,8 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, struct cgmine return i; } -static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) + +static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { char extra[TMPBUFSIZ]; char id[20]; @@ -2174,10 +2197,12 @@ static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, strcat(io_buffer, JSON_CLOSE); } +static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group); + struct CMDS { char *name; - void (*func)(SOCKETTYPE, char *, bool); - bool requires_writemode; + void (*func)(SOCKETTYPE, char *, bool, char); + bool iswritemode; } cmds[] = { { "version", apiversion, false }, { "config", minerconfig, false }, @@ -2220,9 +2245,50 @@ struct CMDS { { "devdetails", devdetails, false }, { "restart", dorestart, true }, { "stats", minerstats, false }, + { "check", checkcommand, false }, { NULL, NULL, false } }; +static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group) +{ + char buf[TMPBUFSIZ]; + char cmdbuf[100]; + bool found, access; + int i; + + if (param == NULL || *param == '\0') { + strcpy(io_buffer, message(MSG_MISCHK, 0, NULL, isjson)); + return; + } + + found = false; + access = false; + for (i = 0; cmds[i].name != NULL; i++) { + if (strcmp(cmds[i].name, param) == 0) { + found = true; + + sprintf(cmdbuf, "|%s|", param); + if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) + access = true; + + break; + } + } + + strcpy(io_buffer, message(MSG_CHECK, 0, NULL, isjson)); + + sprintf(buf, isjson + ? "," JSON_CHECK "{\"Exists\":\"%s\",\"Access\":\"%s\"}" JSON_CLOSE + : _CHECK ",Exists=%s,Access=%s" SEPSTR, + found ? YES : NO, + access ? YES : NO); + + strcat(io_buffer, buf); + + if (isjson) + strcat(io_buffer, JSON_CLOSE); +} + static void send_result(SOCKETTYPE c, bool isjson) { int n; @@ -2277,7 +2343,154 @@ static void tidyup(__maybe_unused void *arg) } /* - * Interpret [R|W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option + * Interpret --api-groups G:cmd1:cmd2:cmd3,P:cmd4,*,... + */ +static void setup_groups() +{ + char *buf, *ptr, *next, *colon; + char group; + char commands[TMPBUFSIZ]; + char cmdbuf[100]; + char *cmd; + bool addstar, did; + int i; + + buf = malloc(strlen(opt_api_groups) + 1); + if (unlikely(!buf)) + quit(1, "Failed to malloc ipgroups buf"); + + strcpy(buf, opt_api_groups); + + next = buf; + // for each group defined + while (next && *next) { + ptr = next; + next = strchr(ptr, ','); + if (next) + *(next++) = '\0'; + + // Validate the group + if (*(ptr+1) != ':') { + colon = strchr(ptr, ':'); + if (colon) + *colon = '\0'; + applog(LOG_WARNING, "API invalid group name '%s'%s", ptr, GROUPDIS); + goto shin; + } + + group = GROUP(*ptr); + if (!VALIDGROUP(group)) { + applog(LOG_WARNING, "API invalid group name '%c'%s", *ptr, GROUPDIS); + goto shin; + } + + if (group == PRIVGROUP) { + applog(LOG_WARNING, "API group name can't be '%c'%s", PRIVGROUP, GROUPDIS); + goto shin; + } + + if (group == NOPRIVGROUP) { + applog(LOG_WARNING, "API group name can't be '%c'%s", NOPRIVGROUP, GROUPDIS); + goto shin; + } + + if (apigroups[GROUPOFFSET(group)].commands != NULL) { + applog(LOG_WARNING, "API duplicate group name '%c'%s", *ptr, GROUPDIS); + goto shin; + } + + ptr += 2; + + // Validate the command list (and handle '*') + cmd = &(commands[0]); + *(cmd++) = SEPARATOR; + *cmd = '\0'; + addstar = false; + while (ptr && *ptr) { + colon = strchr(ptr, ':'); + if (colon) + *(colon++) = '\0'; + + if (strcmp(ptr, "*") == 0) + addstar = true; + else { + did = false; + for (i = 0; cmds[i].name != NULL; i++) { + if (strcasecmp(ptr, cmds[i].name) == 0) { + did = true; + break; + } + } + if (did) { + // skip duplicates + sprintf(cmdbuf, "|%s|", cmds[i].name); + if (strstr(commands, cmdbuf) == NULL) { + strcpy(cmd, cmds[i].name); + cmd += strlen(cmds[i].name); + *(cmd++) = SEPARATOR; + *cmd = '\0'; + } + } else { + applog(LOG_WARNING, "API unknown command '%s' in group '%c'%s", ptr, group, GROUPDIS); + goto shin; + } + } + + ptr = colon; + } + + // * = allow all non-iswritemode commands + if (addstar) { + for (i = 0; cmds[i].name != NULL; i++) { + if (cmds[i].iswritemode == false) { + // skip duplicates + sprintf(cmdbuf, "|%s|", cmds[i].name); + if (strstr(commands, cmdbuf) == NULL) { + strcpy(cmd, cmds[i].name); + cmd += strlen(cmds[i].name); + *(cmd++) = SEPARATOR; + *cmd = '\0'; + } + } + } + } + + ptr = apigroups[GROUPOFFSET(group)].commands = malloc(strlen(commands) + 1); + if (unlikely(!ptr)) + quit(1, "Failed to malloc group commands buf"); + + strcpy(ptr, commands); + } + + // Now define R (NOPRIVGROUP) as all non-iswritemode commands + cmd = &(commands[0]); + *(cmd++) = SEPARATOR; + *cmd = '\0'; + for (i = 0; cmds[i].name != NULL; i++) { + if (cmds[i].iswritemode == false) { + strcpy(cmd, cmds[i].name); + cmd += strlen(cmds[i].name); + *(cmd++) = SEPARATOR; + *cmd = '\0'; + } + } + + ptr = apigroups[GROUPOFFSET(NOPRIVGROUP)].commands = malloc(strlen(commands) + 1); + if (unlikely(!ptr)) + quit(1, "Failed to malloc noprivgroup commands buf"); + + strcpy(ptr, commands); + + // W (PRIVGROUP) is handled as a special case since it simply means all commands + + groups_enabled = true; +shin: + free(buf); + return; +} + +/* + * Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option * special case of 0/0 allows /0 (means all IP addresses) */ #define ALLIP4 "0/0" @@ -2288,7 +2501,7 @@ static void setup_ipaccess() { char *buf, *ptr, *comma, *slash, *dot; int ipcount, mask, octet, i; - bool writemode; + char group; buf = malloc(strlen(opt_api_allow) + 1); if (unlikely(!buf)) @@ -2322,16 +2535,16 @@ static void setup_ipaccess() if (comma) *(comma++) = '\0'; - writemode = false; + group = NOPRIVGROUP; if (isalpha(*ptr) && *(ptr+1) == ':') { - if (tolower(*ptr) == 'w') - writemode = true; + if (DEFINEDGROUP(*ptr)) + group = GROUP(*ptr); ptr += 2; } - ipaccess[ips].writemode = writemode; + ipaccess[ips].group = group; if (strcmp(ptr, ALLIP4) == 0) ipaccess[ips].ip = ipaccess[ips].mask = 0; @@ -2421,10 +2634,11 @@ void api(int api_thr_id) struct sockaddr_in serv; struct sockaddr_in cli; socklen_t clisiz; + char cmdbuf[100]; char *cmd; char *param; bool addrok; - bool writemode; + char group; json_error_t json_err; json_t *json_config; json_t *json_val; @@ -2445,6 +2659,9 @@ void api(int api_thr_id) return; } + if (opt_api_groups) + setup_groups(); + if (opt_api_allow) { setup_ipaccess(); @@ -2523,15 +2740,16 @@ void api(int api_thr_id) connectaddr = inet_ntoa(cli.sin_addr); addrok = false; - writemode = false; + group = NOPRIVGROUP; if (opt_api_allow) { - for (i = 0; i < ips; i++) { - if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) { - addrok = true; - writemode = ipaccess[i].writemode; - break; + if (groups_enabled) + for (i = 0; i < ips; i++) { + if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) { + addrok = true; + group = ipaccess[i].group; + break; + } } - } } else { if (opt_api_network) addrok = true; @@ -2622,12 +2840,13 @@ void api(int api_thr_id) if (!did) for (i = 0; cmds[i].name != NULL; i++) { if (strcmp(cmd, cmds[i].name) == 0) { - if (cmds[i].requires_writemode && !writemode) { + sprintf(cmdbuf, "|%s|", cmd); + if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) + (cmds[i].func)(c, param, isjson, group); + else { strcpy(io_buffer, message(MSG_ACCDENY, 0, cmds[i].name, isjson)); applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name); } - else - (cmds[i].func)(c, param, isjson); send_result(c, isjson); did = true; From 39ff15753cd863aceae7f578ad203de1ed4039fc Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 28 Jun 2012 03:34:16 +1000 Subject: [PATCH 3/5] API-README --api-groups corrections --- API-README | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/API-README b/API-README index fc71271e..e523f4ea 100644 --- a/API-README +++ b/API-README @@ -31,8 +31,11 @@ With "--api-allow", 127.0.0.1 is not by default given access unless specified More groups (like the privileged group W:) can be defined using the --api-groups command -Valid groups are only the letters A-V,X-Z and are not case sensitive -To give an IP address/subnet access to a group you the use group letter +Valid groups are only the letters A-Z (except R & W are predfined) and are +not case sensitive +The R: group is the same as not privileged access +The W: group is (as stated) privileged access (thus all API commands) +To give an IP address/subnet access to a group you use the group letter in front of the IP address instead of W: e.g. P:192.168.0/32 An IP address/subnet can only be a member of one group A sample API group would be: @@ -309,7 +312,7 @@ Feature Changelog for external applications using the API: API V1.13 Added API commands: - 'checkcommand' + 'check' Support was added to cgminer for API access groups with the --api-groups option It's 100% backwards compatible with previous --api-access commands From 05b8f5b0996e20cca41bbac8088b9f65409c42e3 Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 28 Jun 2012 05:59:31 +1000 Subject: [PATCH 4/5] api.c fix json already closed --- api.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/api.c b/api.c index 13cb11c9..c4a0b9f9 100644 --- a/api.c +++ b/api.c @@ -2284,9 +2284,6 @@ static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, access ? YES : NO); strcat(io_buffer, buf); - - if (isjson) - strcat(io_buffer, JSON_CLOSE); } static void send_result(SOCKETTYPE c, bool isjson) From f0c2f8dd93d586e7d3b97767eab141399594f7c9 Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 28 Jun 2012 07:43:25 +1000 Subject: [PATCH 5/5] API-README spelling --- API-README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API-README b/API-README index e523f4ea..52f67ae4 100644 --- a/API-README +++ b/API-README @@ -31,7 +31,7 @@ With "--api-allow", 127.0.0.1 is not by default given access unless specified More groups (like the privileged group W:) can be defined using the --api-groups command -Valid groups are only the letters A-Z (except R & W are predfined) and are +Valid groups are only the letters A-Z (except R & W are predefined) and are not case sensitive The R: group is the same as not privileged access The W: group is (as stated) privileged access (thus all API commands)