1 /* 2 * rpcd - UBUS RPC server 3 * 4 * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <stdbool.h> 20 #include <libubus.h> 21 22 #include <rpcd/exec.h> 23 #include <rpcd/plugin.h> 24 #include <rpcd/session.h> 25 #include <sys/reboot.h> 26 27 static const struct rpc_daemon_ops *ops; 28 29 enum { 30 RPC_P_USER, 31 RPC_P_PASSWORD, 32 __RPC_P_MAX 33 }; 34 35 static const struct blobmsg_policy rpc_password_policy[__RPC_P_MAX] = { 36 [RPC_P_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING }, 37 [RPC_P_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, 38 }; 39 40 enum { 41 RPC_UPGRADE_KEEP, 42 __RPC_UPGRADE_MAX 43 }; 44 45 static const struct blobmsg_policy rpc_upgrade_policy[__RPC_UPGRADE_MAX] = { 46 [RPC_UPGRADE_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL }, 47 }; 48 49 enum { 50 RPC_PACKAGELIST_ALL, 51 __RPC_PACKAGELIST_MAX 52 }; 53 54 static const struct blobmsg_policy rpc_packagelist_policy[__RPC_PACKAGELIST_MAX] = { 55 [RPC_PACKAGELIST_ALL] = { .name = "all", .type = BLOBMSG_TYPE_BOOL }, 56 }; 57 58 static int 59 rpc_errno_status(void) 60 { 61 switch (errno) 62 { 63 case EACCES: 64 return UBUS_STATUS_PERMISSION_DENIED; 65 66 case ENOTDIR: 67 return UBUS_STATUS_INVALID_ARGUMENT; 68 69 case ENOENT: 70 return UBUS_STATUS_NOT_FOUND; 71 72 case EINVAL: 73 return UBUS_STATUS_INVALID_ARGUMENT; 74 75 default: 76 return UBUS_STATUS_UNKNOWN_ERROR; 77 } 78 } 79 80 static int 81 rpc_cgi_password_set(struct ubus_context *ctx, struct ubus_object *obj, 82 struct ubus_request_data *req, const char *method, 83 struct blob_attr *msg) 84 { 85 pid_t pid; 86 int fd, fds[2]; 87 struct stat s; 88 struct blob_attr *tb[__RPC_P_MAX]; 89 ssize_t n; 90 int ret; 91 const char *const passwd = "/bin/passwd"; 92 const struct timespec ts = {0, 100 * 1000 * 1000}; 93 94 blobmsg_parse(rpc_password_policy, __RPC_P_MAX, tb, 95 blob_data(msg), blob_len(msg)); 96 97 if (!tb[RPC_P_USER] || !tb[RPC_P_PASSWORD]) 98 return UBUS_STATUS_INVALID_ARGUMENT; 99 100 if (stat(passwd, &s)) 101 return UBUS_STATUS_NOT_FOUND; 102 103 if (!(s.st_mode & S_IXUSR)) 104 return UBUS_STATUS_PERMISSION_DENIED; 105 106 if (pipe(fds)) 107 return rpc_errno_status(); 108 109 switch ((pid = fork())) 110 { 111 case -1: 112 close(fds[0]); 113 close(fds[1]); 114 return rpc_errno_status(); 115 116 case 0: 117 uloop_done(); 118 119 dup2(fds[0], 0); 120 close(fds[0]); 121 close(fds[1]); 122 123 if ((fd = open("/dev/null", O_RDWR)) > -1) 124 { 125 dup2(fd, 1); 126 dup2(fd, 2); 127 close(fd); 128 } 129 130 ret = chdir("/"); 131 if (ret < 0) 132 _exit(127); 133 134 if (execl(passwd, passwd, 135 blobmsg_data(tb[RPC_P_USER]), NULL)) 136 _exit(127); 137 138 default: 139 close(fds[0]); 140 141 n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), 142 blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); 143 if (n < 0) 144 return rpc_errno_status(); 145 146 n = write(fds[1], "\n", 1); 147 if (n < 0) 148 return rpc_errno_status(); 149 150 nanosleep(&ts, NULL); 151 152 n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), 153 blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); 154 if (n < 0) 155 return rpc_errno_status(); 156 n = write(fds[1], "\n", 1); 157 if (n < 0) 158 return rpc_errno_status(); 159 160 close(fds[1]); 161 162 waitpid(pid, NULL, 0); 163 164 return 0; 165 } 166 } 167 168 static bool 169 is_all_or_world(const char *pkg, const char **world) 170 { 171 /* compares null-terminated pkg with non-null-terminated world[i] */ 172 /* e.g., "my_pkg\0" == "my_pkg==1.2.3\n" => true */ 173 174 if (!world) return true; /* handles 'all' case */ 175 176 const char *terminators = "\n@~<>="; /* man 5 apk-world */ 177 178 size_t i, c; 179 const char *item; 180 for (i = 0; *world[i]; i++) { 181 item = world[i]; 182 for (c = 0; pkg[c] == item[c]; c++); 183 if (pkg[c] == '\0' && strchr(terminators, item[c])) 184 return true; 185 } 186 187 return false; 188 } 189 190 static bool 191 is_blank(const char *line) 192 { 193 for (; *line; line++) 194 if (!isspace((unsigned char)*line)) 195 return false; 196 return true; 197 } 198 199 static int 200 rpc_sys_packagelist(struct ubus_context *ctx, struct ubus_object *obj, 201 struct ubus_request_data *req, const char *method, 202 struct blob_attr *msg) 203 { 204 struct blob_attr *tb[__RPC_PACKAGELIST_MAX]; 205 bool all = false; 206 struct blob_buf buf = { 0 }; 207 /* line len = prefix(2) + content(512) + newline + null = 516 */ 208 char line[516], abi[128], pkg[128], ver[128]; 209 void *tbl; 210 struct stat statbuf; 211 const char **world = NULL; 212 char *world_buf = NULL; 213 size_t world_size = 0; 214 char *token = NULL; 215 216 /* 217 * Status file fields, /usr/lib/opkg/status vs /lib/apk/db/installed 218 * opkg apk 219 * PACKAGE_ABIVERSION "ABIVersion" "g:openwrt:abiversion=" 220 * PACKAGE_AUTOINSTALLED "Auto-Installed" package listed in 'world', not a db field 221 * PACKAGE_NAME "Package" "P" 222 * PACKAGE_STATUS "Status" package listed in db, not a status value 223 * PACKAGE_VERSION "Version" "V" 224 */ 225 226 blobmsg_parse(rpc_packagelist_policy, __RPC_PACKAGELIST_MAX, tb, 227 blob_data(msg), blob_len(msg)); 228 229 if (tb[RPC_PACKAGELIST_ALL] && blobmsg_get_bool(tb[RPC_PACKAGELIST_ALL])) 230 all = true; 231 232 FILE *f = fopen("/lib/apk/db/installed", "r"); 233 if (!f) 234 return UBUS_STATUS_NOT_FOUND; 235 236 if (!all) { 237 /* We return only those items appearing in 'world' file. */ 238 int world_fd = open("/etc/apk/world", O_RDONLY); 239 if (world_fd == -1) { 240 fclose(f); 241 return rpc_errno_status(); 242 } 243 244 if (fstat(world_fd, &statbuf) == -1) { 245 close(world_fd); 246 fclose(f); 247 return rpc_errno_status(); 248 } 249 250 world_size = statbuf.st_size + 1; 251 if (world_size == 1) { 252 /* 'world' file is malformed: empty */ 253 close(world_fd); 254 fclose(f); 255 return UBUS_STATUS_UNKNOWN_ERROR; 256 } 257 258 /* 259 * Read the whole file into a NUL terminated heap buffer. The 260 * parser below relies on C string termination (a trailing NUL at 261 * world_buf[statbuf.st_size]); mmap() could not guarantee a 262 * readable byte at that offset when the file size is an exact 263 * multiple of the page size, which would fault with SIGBUS. 264 */ 265 world_buf = malloc(world_size); 266 if (!world_buf) { 267 close(world_fd); 268 fclose(f); 269 return UBUS_STATUS_UNKNOWN_ERROR; 270 } 271 272 size_t world_got = 0; 273 while (world_got < (size_t)statbuf.st_size) { 274 ssize_t rd = read(world_fd, world_buf + world_got, 275 statbuf.st_size - world_got); 276 if (rd < 0 && errno == EINTR) 277 continue; 278 if (rd <= 0) 279 break; 280 world_got += rd; 281 } 282 close(world_fd); 283 284 if (world_got != (size_t)statbuf.st_size) { 285 free(world_buf); 286 fclose(f); 287 return UBUS_STATUS_UNKNOWN_ERROR; 288 } 289 290 world_buf[statbuf.st_size] = '\0'; 291 292 if (world_buf[world_size-2] != '\n') { 293 /* 'world' file is malformed: missing final newline */ 294 free(world_buf); 295 fclose(f); 296 return UBUS_STATUS_UNKNOWN_ERROR; 297 } 298 299 /* resulting 'world' pointer map looks like this: 300 * nstrs = 2 == count of newlines in buffer 301 * buf = "pkg1\npkg2=1.2\n\0" 302 * | | | 303 * world[0]-+ | | 304 * world[1]-------+ | 305 * world[2]-----------------+ 306 */ 307 308 size_t istr, nstrs; 309 char *s; 310 for (nstrs = 0, s = world_buf; s[nstrs]; s[nstrs] == '\n' ? nstrs++ : *s++); 311 312 if (nstrs) { 313 /* extra one in world for NULL sentinel */ 314 world = (const char **)calloc(nstrs+1, sizeof(char *)); 315 if (!world) { 316 free(world_buf); 317 fclose(f); 318 return UBUS_STATUS_UNKNOWN_ERROR; 319 } 320 world[0] = world_buf; 321 for (istr = 1, s = world_buf; *s; s++) { 322 if (*s == '\n') { 323 world[istr] = s + 1; 324 istr++; 325 } 326 } 327 } 328 } 329 330 blob_buf_init(&buf, 0); 331 tbl = blobmsg_open_table(&buf, "packages"); 332 abi[0] = pkg[0] = ver[0] = '\0'; 333 334 while (fgets(line, sizeof(line), f)) { 335 switch (line[0]) { 336 case 'P': 337 if (sscanf(line, "P: %127s", pkg) != 1) 338 pkg[0] = '\0'; 339 break; 340 case 'V': 341 if (sscanf(line, "V: %127s", ver) != 1) 342 ver[0] = '\0'; 343 break; 344 case 'g': 345 /* this is a custom tag, defined in include/package-pack.mk */ 346 token = strtok(line+2, " =\n"); 347 while (token) { 348 if (strcmp(token, "openwrt:abiversion") == 0) { 349 token = strtok(NULL, " =\n"); 350 if (token) 351 strlcpy(abi, token, sizeof(abi)); 352 break; 353 } 354 token = strtok(NULL, " =\n"); 355 } 356 break; 357 default: 358 if (is_blank(line)) { 359 if (pkg[0] && ver[0]) { 360 /* Need to check both ABI-versioned and non-versioned pkg */ 361 bool keep = is_all_or_world(pkg, world); 362 size_t plen = strlen(pkg), alen = strlen(abi); 363 /* The ABI version is expected to be a suffix of the 364 * package name. Guard against a malformed status file 365 * where it is longer, which would underflow the 366 * unsigned subtraction and write out of bounds. */ 367 if (abi[0] && alen <= plen) { 368 pkg[plen - alen] = '\0'; 369 if (!keep) 370 keep = is_all_or_world(pkg, world); 371 } 372 if (keep) 373 blobmsg_add_string(&buf, pkg, ver); 374 } 375 abi[0] = pkg[0] = ver[0] = '\0'; 376 } 377 break; 378 } 379 } 380 381 if (world) 382 free(world); 383 if (world_buf) 384 free(world_buf); 385 386 blobmsg_close_table(&buf, tbl); 387 ubus_send_reply(ctx, req, buf.head); 388 blob_buf_free(&buf); 389 fclose(f); 390 391 return 0; 392 } 393 394 static int 395 rpc_sys_upgrade_test(struct ubus_context *ctx, struct ubus_object *obj, 396 struct ubus_request_data *req, const char *method, 397 struct blob_attr *msg) 398 { 399 const char *cmd[4] = { "sysupgrade", "--test", "/tmp/firmware.bin", NULL }; 400 return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); 401 } 402 403 static int 404 rpc_sys_upgrade_start(struct ubus_context *ctx, struct ubus_object *obj, 405 struct ubus_request_data *req, const char *method, 406 struct blob_attr *msg) 407 { 408 struct blob_attr *tb[__RPC_UPGRADE_MAX]; 409 char * const cmd[4] = { "/sbin/sysupgrade", "-n", "/tmp/firmware.bin", NULL }; 410 char * const cmd_keep[3] = { "/sbin/sysupgrade", "/tmp/firmware.bin", NULL }; 411 char * const * c = cmd; 412 413 blobmsg_parse(rpc_upgrade_policy, __RPC_UPGRADE_MAX, tb, 414 blob_data(msg), blob_len(msg)); 415 416 if (tb[RPC_UPGRADE_KEEP] && blobmsg_get_bool(tb[RPC_UPGRADE_KEEP])) 417 c = cmd_keep; 418 419 if (!fork()) { 420 /* wait for the RPC call to complete */ 421 sleep(2); 422 return execv(c[0], c); 423 } 424 425 return 0; 426 } 427 428 static int 429 rpc_sys_upgrade_clean(struct ubus_context *ctx, struct ubus_object *obj, 430 struct ubus_request_data *req, const char *method, 431 struct blob_attr *msg) 432 { 433 if (unlink("/tmp/firmware.bin")) 434 return rpc_errno_status(); 435 436 return 0; 437 } 438 439 static int 440 rpc_sys_factory(struct ubus_context *ctx, struct ubus_object *obj, 441 struct ubus_request_data *req, const char *method, 442 struct blob_attr *msg) 443 { 444 char * const cmd[4] = { "/sbin/jffs2reset", "-y", "-r", NULL }; 445 446 if (!fork()) { 447 /* wait for the RPC call to complete */ 448 sleep(2); 449 return execv(cmd[0], cmd); 450 } 451 452 return 0; 453 } 454 455 static int 456 rpc_sys_reboot(struct ubus_context *ctx, struct ubus_object *obj, 457 struct ubus_request_data *req, const char *method, 458 struct blob_attr *msg) 459 { 460 if (!fork()) { 461 sync(); 462 sleep(2); 463 reboot(RB_AUTOBOOT); 464 while (1) 465 ; 466 } 467 468 return 0; 469 } 470 471 static int 472 rpc_sys_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) 473 { 474 static const struct ubus_method sys_methods[] = { 475 UBUS_METHOD("packagelist", rpc_sys_packagelist, rpc_packagelist_policy), 476 UBUS_METHOD("password_set", rpc_cgi_password_set, rpc_password_policy), 477 UBUS_METHOD_NOARG("upgrade_test", rpc_sys_upgrade_test), 478 UBUS_METHOD("upgrade_start", rpc_sys_upgrade_start, 479 rpc_upgrade_policy), 480 UBUS_METHOD_NOARG("upgrade_clean", rpc_sys_upgrade_clean), 481 UBUS_METHOD_NOARG("factory", rpc_sys_factory), 482 UBUS_METHOD_NOARG("reboot", rpc_sys_reboot), 483 }; 484 485 static struct ubus_object_type sys_type = 486 UBUS_OBJECT_TYPE("rpcd-plugin-sys", sys_methods); 487 488 static struct ubus_object obj = { 489 .name = "rpc-sys", 490 .type = &sys_type, 491 .methods = sys_methods, 492 .n_methods = ARRAY_SIZE(sys_methods), 493 }; 494 495 ops = o; 496 497 return ubus_add_object(ctx, &obj); 498 } 499 500 struct rpc_plugin rpc_plugin = { 501 .init = rpc_sys_api_init 502 }; 503
This page was automatically generated by LXR 0.3.1. • OpenWrt