/* * Read a value from kernel memory, given its offset, and store it in the dest * buffer. That buffer has space for dest_len octets. */ static void kernel_read(off_t where, void *dest, int dest_len) { ssize_t status; if (lseek(kernel_fd, where, SEEK_SET) < 0) sysdie("cannot lseek in %s", C_KMEM); status = read(kernel_fd, dest, dest_len); if (status < 0) sysdie("kernel read of %s failed", C_KMEM); }
/* * Sets up for later queries. Open the kernel memory file and find the * address of the necessary variable. Exits on error. */ static int kernel_open(void) { kernel_fd = open(C_KMEM, O_RDONLY); if (kernel_fd < 0) sysdie("cannot open %s", C_KMEM); if (nlist(C_VMUNIX, nl) < 0) sysdie("no namelist for %s", C_VMUNIX); kernel_init = 1; return 0; }
/* * Sets up for later queries. Open the kernel memory file and find the * address of the necessary variable. Exits on error. */ static int kernel_open(void) { int ldav_off; kmem_fd = open("/dev/kmem", O_RDONLY); if (kmem_fd < 0) sysdie("cannot open /dev/kmem"); ldav_off = sysmp(MP_KERNADDR, MPKA_AVENRUN); if (ldav_off < 0) sysdie("sysmp failed"); kernel_offset = (long) ldav_off & 0x7fffffff; kernel_init = 1; }
/* * Get the current load average from the kernel and return the one minute, * five minute, and fifteen minute averages in the given parameters. Returns * 0 on success and -1 on failure. */ int kernel_getload(double *load1, double *load5, double *load15) { int status, load[3]; static long offset = -1; if (!kernel_init) kernel_open(); if (lseek(kmem_fd, offset, 0) < 0) sysdie("cannot lseek in /dev/kmem"); status = read(kmem_fd, (void *) load, sizeof(load)); if (status != sizeof(load)) sysdie("kernel read failed"); *load1 = (double) load[0] / 1000.0; *load5 = (double) load[1] / 1000.0; *load15 = (double) load[2] / 1000.0; return 0; }
/* * Given a remctl object, the Kerberos context, the type for the wallet * interface, and a file name of a keytab, iterate through every existing * principal in the keytab in the local realm, get fresh keys for those * principals, and save the old and new keys to that file. Returns true on * success and false on partial failure to retrieve all the keys. */ bool rekey_keytab(struct remctl *r, krb5_context ctx, const char *type, const char *file) { char *realm = NULL; char *data = NULL; char *tempfile; size_t length = 0; int status; bool error = false, rekeyed = false; struct principal_name *names, *current; xasprintf(&tempfile, "%s.new", file); krb5_get_default_realm(ctx, &realm); names = keytab_principals(ctx, file, realm); for (current = names; current != NULL; current = current->next) { status = download_keytab(r, type, current->princ, &data, &length); if (status != 0) { warn("error rekeying for principal %s", current->princ); error = true; continue; } write_file(tempfile, data, length); rekeyed = true; /* * Now merge the original keytab file with the one containing the new * keys from the rekeying of this principal. */ if (access(file, F_OK) != 0) { if (link(tempfile, file) < 0) sysdie("rename of temporary keytab %s to %s failed", tempfile, file); } else { merge_keytab(ctx, tempfile, file); if (unlink(tempfile) < 0) syswarn("unlink of temporary keytab file %s failed", tempfile); } } /* If no new keytab data, then leave the keytab as-is. */ if (!rekeyed) die("no rekeyable principals found"); free(tempfile); return !error; }
/* * Given a remctl object, the Kerberos context, the name of a keytab object, * and a file name, call the correct wallet commands to download a keytab and * write it to that file. Returns the status or 255 on an internal error. */ int get_keytab(struct remctl *r, krb5_context ctx, const char *type, const char *name, const char *file, const char *srvtab) { const char *command[5]; char *tempfile; char *data = NULL; size_t length = 0; int status; command[0] = type; command[1] = "get"; command[2] = "keytab"; command[3] = name; command[4] = NULL; status = run_command(r, command, &data, &length); if (status != 0) return status; if (data == NULL) { warn("no data returned by wallet server"); return 255; } if (access(file, F_OK) == 0) { xasprintf(&tempfile, "%s.new", file); overwrite_file(tempfile, data, length); if (srvtab != NULL) write_srvtab(ctx, srvtab, name, tempfile); merge_keytab(ctx, tempfile, file); if (unlink(tempfile) < 0) sysdie("unlink of temporary keytab file %s failed", tempfile); free(tempfile); } else { write_file(file, data, length); if (srvtab != NULL) write_srvtab(ctx, srvtab, name, file); } return 0; }
/* * Take the amount of memory to allocate in bytes as a command-line argument * and call test_malloc with that amount of memory. */ int main(int argc, char *argv[]) { size_t size, max; size_t limit = 0; int willfail = 0; unsigned char code; if (argc < 3) die("Usage error. Type, size, and limit must be given."); errno = 0; size = strtol(argv[2], 0, 10); if (size == 0 && errno != 0) sysdie("Invalid size"); errno = 0; limit = strtol(argv[3], 0, 10); if (limit == 0 && errno != 0) sysdie("Invalid limit"); /* If the code is capitalized, install our customized error handler. */ code = argv[1][0]; if (isupper(code)) { xmalloc_error_handler = test_handler; code = tolower(code); } /* * Decide if the allocation should fail. If it should, set willfail to 2, * so that if it unexpectedly succeeds, we exit with a status indicating * that the test should be skipped. */ max = size; if (code == 's' || code == 'n' || code == 'a' || code == 'v') { max += size; if (limit > 0) limit += size; } if (limit > 0 && max > limit) willfail = 2; /* * If a memory limit was given and we can set memory limits, set it. * Otherwise, exit 2, signalling to the driver that the test should be * skipped. We do this here rather than in the driver due to some * pathological problems with Linux (setting ulimit in the shell caused * the shell to die). */ if (limit > 0) { #if HAVE_SETRLIMIT && defined(RLIMIT_AS) struct rlimit rl; void *tmp; size_t test_size; rl.rlim_cur = limit; rl.rlim_max = limit; if (setrlimit(RLIMIT_AS, &rl) < 0) { syswarn("Can't set data limit to %lu", (unsigned long) limit); exit(2); } if (size < limit || code == 'r') { test_size = code == 'r' ? 10 : size; if (test_size == 0) test_size = 1; tmp = malloc(test_size); if (tmp == NULL) { syswarn("Can't allocate initial memory of %lu (limit %lu)", (unsigned long) test_size, (unsigned long) limit); exit(2); } free(tmp); } #else warn("Data limits aren't supported."); exit(2); #endif } switch (code) { case 'c': exit(test_calloc(size) ? willfail : 1); case 'm': exit(test_malloc(size) ? willfail : 1); case 'r': exit(test_realloc(size) ? willfail : 1); case 's': exit(test_strdup(size) ? willfail : 1); case 'n': exit(test_strndup(size) ? willfail : 1); case 'a': exit(test_asprintf(size) ? willfail : 1); case 'v': exit(test_vasprintf(size) ? willfail : 1); default: die("Unknown mode %c", argv[1][0]); break; } exit(1); }
/* * Main routine. Parse the arguments, open the remctl connection, send the * command, and then call process_response. */ int main(int argc, char *argv[]) { int option, status; char *server_host; struct addrinfo hints, *ai; const char *source = NULL; const char *service_name = NULL; unsigned short port = 0; struct remctl *r; int errorcode = 0; /* Set up logging and identity. */ message_program_name = "remctl"; if (!socket_init()) die("failed to initialize socket library"); /* * Parse options. The + tells GNU getopt to stop option parsing at the * first non-argument rather than proceeding on to find options anywhere. * Without this, it's hard to call remote programs that take options. * Non-GNU getopt will treat the + as a supported option, which is handled * below. */ while ((option = getopt(argc, argv, "+b:dhp:s:v")) != EOF) { switch (option) { case 'b': source = optarg; break; case 'd': message_handlers_debug(1, message_log_stderr); break; case 'h': usage(0); break; case 'p': port = atoi(optarg); break; case 's': service_name = optarg; break; case 'v': printf("%s\n", PACKAGE_STRING); exit(0); break; case '+': fprintf(stderr, "%s: invalid option -- +\n", argv[0]); default: usage(1); break; } } argc -= optind; argv += optind; if (argc < 2) usage(1); server_host = *argv++; argc--; /* * If service_name isn't set, the remctl library uses host/<server> * (host@<server> in GSS-API parlance). However, if the server to which * we're connecting is a DNS-load-balanced name, we have to be careful * what principal name we use. * * Ideally, we would let the GSS-API library handle this and choose * whether to canonicalize the <server> in the principal name based on the * krb5.conf rdns setting and similar configuration. However, with DNS * load balancing, this still may fail. At the time of network * connection, we will connect to whatever the name resolves to then. * After we connect, we authenticate, and the GSS-API library will then * separately canonicalize the hostname. It could get a different answer * than we got for our network connection, leading to an authentication * failure. * * Therefore, if the principal isn't specified, we canonicalize the * hostname to which we're connecting before we connect. Then, the * additional canonicalization possibly done by the GSS-API library should * return the same results and be consistent. * * Note that this opens the possibility of a subtle attack through DNS * spoofing, since both the principal used and the host to which we're * connecting can be changed by varying the DNS response. * * If the principal is specified explicitly, assume the user knows what * they're doing and don't do any of this. */ if (service_name == NULL) { memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; status = getaddrinfo(server_host, NULL, &hints, &ai); if (status != 0) die("cannot resolve host %s: %s", server_host, gai_strerror(status)); server_host = xstrdup(ai->ai_canonname); freeaddrinfo(ai); } /* Open connection. */ r = remctl_new(); if (r == NULL) sysdie("cannot initialize remctl connection"); if (source != NULL) if (!remctl_set_source_ip(r, source)) die("%s", remctl_error(r)); if (!remctl_open(r, server_host, port, service_name)) die("%s", remctl_error(r)); /* Do the work. */ if (!remctl_command(r, (const char **) argv)) die("%s", remctl_error(r)); if (!process_response(r, &errorcode)) die("%s", remctl_error(r)); /* Shut down cleanly. */ remctl_close(r); socket_shutdown(); return errorcode; }