1 /*- 2 * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/lock.h> 35 #include <sys/mutex.h> 36 #include <sys/bio.h> 37 #include <sys/sysctl.h> 38 #include <sys/malloc.h> 39 #include <sys/kthread.h> 40 #include <sys/proc.h> 41 #include <sys/sched.h> 42 #include <sys/uio.h> 43 44 #include <vm/uma.h> 45 46 #include <geom/geom.h> 47 #include <geom/eli/g_eli.h> 48 49 50 MALLOC_DECLARE(M_ELI); 51 52 53 static void 54 g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp) 55 { 56 struct g_eli_metadata md; 57 struct g_provider *pp; 58 const char *name; 59 u_char *key, mkey[G_ELI_DATAIVKEYLEN]; 60 int *nargs, *detach; 61 int keysize, error; 62 u_int nkey; 63 64 g_topology_assert(); 65 66 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 67 if (nargs == NULL) { 68 gctl_error(req, "No '%s' argument.", "nargs"); 69 return; 70 } 71 if (*nargs != 1) { 72 gctl_error(req, "Invalid number of arguments."); 73 return; 74 } 75 76 detach = gctl_get_paraml(req, "detach", sizeof(*detach)); 77 if (detach == NULL) { 78 gctl_error(req, "No '%s' argument.", "detach"); 79 return; 80 } 81 82 name = gctl_get_asciiparam(req, "arg0"); 83 if (name == NULL) { 84 gctl_error(req, "No 'arg%u' argument.", 0); 85 return; 86 } 87 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 88 name += strlen("/dev/"); 89 pp = g_provider_by_name(name); 90 if (pp == NULL) { 91 gctl_error(req, "Provider %s is invalid.", name); 92 return; 93 } 94 error = g_eli_read_metadata(mp, pp, &md); 95 if (error != 0) { 96 gctl_error(req, "Cannot read metadata from %s (error=%d).", 97 name, error); 98 return; 99 } 100 if (md.md_keys == 0x00) { 101 bzero(&md, sizeof(md)); 102 gctl_error(req, "No valid keys on %s.", pp->name); 103 return; 104 } 105 106 key = gctl_get_param(req, "key", &keysize); 107 if (key == NULL || keysize != G_ELI_USERKEYLEN) { 108 bzero(&md, sizeof(md)); 109 gctl_error(req, "No '%s' argument.", "key"); 110 return; 111 } 112 113 error = g_eli_mkey_decrypt(&md, key, mkey, &nkey); 114 bzero(key, keysize); 115 if (error == -1) { 116 bzero(&md, sizeof(md)); 117 gctl_error(req, "Wrong key for %s.", pp->name); 118 return; 119 } else if (error > 0) { 120 bzero(&md, sizeof(md)); 121 gctl_error(req, "Cannot decrypt Master Key for %s (error=%d).", 122 pp->name, error); 123 return; 124 } 125 G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name); 126 127 if (*detach) 128 md.md_flags |= G_ELI_FLAG_WO_DETACH; 129 g_eli_create(req, mp, pp, &md, mkey, nkey); 130 bzero(mkey, sizeof(mkey)); 131 bzero(&md, sizeof(md)); 132 } 133 134 static struct g_eli_softc * 135 g_eli_find_device(struct g_class *mp, const char *prov) 136 { 137 struct g_eli_softc *sc; 138 struct g_geom *gp; 139 struct g_provider *pp; 140 struct g_consumer *cp; 141 142 if (strncmp(prov, "/dev/", strlen("/dev/")) == 0) 143 prov += strlen("/dev/"); 144 LIST_FOREACH(gp, &mp->geom, geom) { 145 sc = gp->softc; 146 if (sc == NULL) 147 continue; 148 pp = LIST_FIRST(&gp->provider); 149 if (pp != NULL && strcmp(pp->name, prov) == 0) 150 return (sc); 151 cp = LIST_FIRST(&gp->consumer); 152 if (cp != NULL && cp->provider != NULL && 153 strcmp(cp->provider->name, prov) == 0) { 154 return (sc); 155 } 156 } 157 return (NULL); 158 } 159 160 static void 161 g_eli_ctl_detach(struct gctl_req *req, struct g_class *mp) 162 { 163 struct g_eli_softc *sc; 164 int *force, *last, *nargs, error; 165 const char *prov; 166 char param[16]; 167 int i; 168 169 g_topology_assert(); 170 171 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 172 if (nargs == NULL) { 173 gctl_error(req, "No '%s' argument.", "nargs"); 174 return; 175 } 176 if (*nargs <= 0) { 177 gctl_error(req, "Missing device(s)."); 178 return; 179 } 180 force = gctl_get_paraml(req, "force", sizeof(*force)); 181 if (force == NULL) { 182 gctl_error(req, "No '%s' argument.", "force"); 183 return; 184 } 185 last = gctl_get_paraml(req, "last", sizeof(*last)); 186 if (last == NULL) { 187 gctl_error(req, "No '%s' argument.", "last"); 188 return; 189 } 190 191 for (i = 0; i < *nargs; i++) { 192 snprintf(param, sizeof(param), "arg%d", i); 193 prov = gctl_get_asciiparam(req, param); 194 if (prov == NULL) { 195 gctl_error(req, "No 'arg%d' argument.", i); 196 return; 197 } 198 sc = g_eli_find_device(mp, prov); 199 if (sc == NULL) { 200 gctl_error(req, "No such device: %s.", prov); 201 return; 202 } 203 if (*last) { 204 sc->sc_flags |= G_ELI_FLAG_RW_DETACH; 205 sc->sc_geom->access = g_eli_access; 206 } else { 207 error = g_eli_destroy(sc, *force); 208 if (error != 0) { 209 gctl_error(req, 210 "Cannot destroy device %s (error=%d).", 211 sc->sc_name, error); 212 return; 213 } 214 } 215 } 216 } 217 218 static void 219 g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp) 220 { 221 struct g_eli_metadata md; 222 struct g_provider *pp; 223 const char *name; 224 intmax_t *keylen, *sectorsize; 225 u_char mkey[G_ELI_DATAIVKEYLEN]; 226 int *nargs, *detach; 227 228 g_topology_assert(); 229 bzero(&md, sizeof(md)); 230 231 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 232 if (nargs == NULL) { 233 gctl_error(req, "No '%s' argument.", "nargs"); 234 return; 235 } 236 if (*nargs != 1) { 237 gctl_error(req, "Invalid number of arguments."); 238 return; 239 } 240 241 detach = gctl_get_paraml(req, "detach", sizeof(*detach)); 242 if (detach == NULL) { 243 gctl_error(req, "No '%s' argument.", "detach"); 244 return; 245 } 246 247 strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 248 md.md_version = G_ELI_VERSION; 249 md.md_flags |= G_ELI_FLAG_ONETIME; 250 if (*detach) 251 md.md_flags |= G_ELI_FLAG_WO_DETACH; 252 253 md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 254 name = gctl_get_asciiparam(req, "aalgo"); 255 if (name == NULL) { 256 gctl_error(req, "No '%s' argument.", "aalgo"); 257 return; 258 } 259 if (strcmp(name, "none") != 0) { 260 md.md_aalgo = g_eli_str2aalgo(name); 261 if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 262 md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 263 md.md_flags |= G_ELI_FLAG_AUTH; 264 } else { 265 /* 266 * For backward compatibility, check if the -a option 267 * was used to provide encryption algorithm. 268 */ 269 md.md_ealgo = g_eli_str2ealgo(name); 270 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 271 md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 272 gctl_error(req, 273 "Invalid authentication algorithm."); 274 return; 275 } else { 276 gctl_error(req, "warning: The -e option, not " 277 "the -a option is now used to specify " 278 "encryption algorithm to use."); 279 } 280 } 281 } 282 283 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 284 md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 285 name = gctl_get_asciiparam(req, "ealgo"); 286 if (name == NULL) { 287 gctl_error(req, "No '%s' argument.", "ealgo"); 288 return; 289 } 290 md.md_ealgo = g_eli_str2ealgo(name); 291 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 292 md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 293 gctl_error(req, "Invalid encryption algorithm."); 294 return; 295 } 296 } 297 298 keylen = gctl_get_paraml(req, "keylen", sizeof(*keylen)); 299 if (keylen == NULL) { 300 gctl_error(req, "No '%s' argument.", "keylen"); 301 return; 302 } 303 md.md_keylen = g_eli_keylen(md.md_ealgo, *keylen); 304 if (md.md_keylen == 0) { 305 gctl_error(req, "Invalid '%s' argument.", "keylen"); 306 return; 307 } 308 309 /* Not important here. */ 310 md.md_provsize = 0; 311 /* Not important here. */ 312 bzero(md.md_salt, sizeof(md.md_salt)); 313 314 md.md_keys = 0x01; 315 arc4rand(mkey, sizeof(mkey), 0); 316 317 /* Not important here. */ 318 bzero(md.md_hash, sizeof(md.md_hash)); 319 320 name = gctl_get_asciiparam(req, "arg0"); 321 if (name == NULL) { 322 gctl_error(req, "No 'arg%u' argument.", 0); 323 return; 324 } 325 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 326 name += strlen("/dev/"); 327 pp = g_provider_by_name(name); 328 if (pp == NULL) { 329 gctl_error(req, "Provider %s is invalid.", name); 330 return; 331 } 332 333 sectorsize = gctl_get_paraml(req, "sectorsize", sizeof(*sectorsize)); 334 if (sectorsize == NULL) { 335 gctl_error(req, "No '%s' argument.", "sectorsize"); 336 return; 337 } 338 if (*sectorsize == 0) 339 md.md_sectorsize = pp->sectorsize; 340 else { 341 if (*sectorsize < 0 || (*sectorsize % pp->sectorsize) != 0) { 342 gctl_error(req, "Invalid sector size."); 343 return; 344 } 345 md.md_sectorsize = *sectorsize; 346 } 347 348 g_eli_create(req, mp, pp, &md, mkey, -1); 349 bzero(mkey, sizeof(mkey)); 350 bzero(&md, sizeof(md)); 351 } 352 353 static void 354 g_eli_ctl_setkey(struct gctl_req *req, struct g_class *mp) 355 { 356 struct g_eli_softc *sc; 357 struct g_eli_metadata md; 358 struct g_provider *pp; 359 struct g_consumer *cp; 360 const char *name; 361 u_char *key, *mkeydst, *sector; 362 intmax_t *valp; 363 int keysize, nkey, error; 364 365 g_topology_assert(); 366 367 name = gctl_get_asciiparam(req, "arg0"); 368 if (name == NULL) { 369 gctl_error(req, "No 'arg%u' argument.", 0); 370 return; 371 } 372 sc = g_eli_find_device(mp, name); 373 if (sc == NULL) { 374 gctl_error(req, "Provider %s is invalid.", name); 375 return; 376 } 377 cp = LIST_FIRST(&sc->sc_geom->consumer); 378 pp = cp->provider; 379 380 error = g_eli_read_metadata(mp, pp, &md); 381 if (error != 0) { 382 gctl_error(req, "Cannot read metadata from %s (error=%d).", 383 name, error); 384 return; 385 } 386 387 valp = gctl_get_paraml(req, "keyno", sizeof(*valp)); 388 if (valp == NULL) { 389 gctl_error(req, "No '%s' argument.", "keyno"); 390 return; 391 } 392 if (*valp != -1) 393 nkey = *valp; 394 else 395 nkey = sc->sc_nkey; 396 if (nkey < 0 || nkey >= G_ELI_MAXMKEYS) { 397 gctl_error(req, "Invalid '%s' argument.", "keyno"); 398 return; 399 } 400 401 valp = gctl_get_paraml(req, "iterations", sizeof(*valp)); 402 if (valp == NULL) { 403 gctl_error(req, "No '%s' argument.", "iterations"); 404 return; 405 } 406 /* Check if iterations number should and can be changed. */ 407 if (*valp != -1) { 408 if (bitcount32(md.md_keys) != 1) { 409 gctl_error(req, "To be able to use '-i' option, only " 410 "one key can be defined."); 411 return; 412 } 413 if (md.md_keys != (1 << nkey)) { 414 gctl_error(req, "Only already defined key can be " 415 "changed when '-i' option is used."); 416 return; 417 } 418 md.md_iterations = *valp; 419 } 420 421 key = gctl_get_param(req, "key", &keysize); 422 if (key == NULL || keysize != G_ELI_USERKEYLEN) { 423 bzero(&md, sizeof(md)); 424 gctl_error(req, "No '%s' argument.", "key"); 425 return; 426 } 427 428 mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 429 md.md_keys |= (1 << nkey); 430 431 bcopy(sc->sc_mkey, mkeydst, sizeof(sc->sc_mkey)); 432 433 /* Encrypt Master Key with the new key. */ 434 error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, mkeydst); 435 bzero(key, sizeof(key)); 436 if (error != 0) { 437 bzero(&md, sizeof(md)); 438 gctl_error(req, "Cannot encrypt Master Key (error=%d).", error); 439 return; 440 } 441 442 sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO); 443 /* Store metadata with fresh key. */ 444 eli_metadata_encode(&md, sector); 445 bzero(&md, sizeof(md)); 446 error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, 447 pp->sectorsize); 448 bzero(sector, sizeof(sector)); 449 free(sector, M_ELI); 450 if (error != 0) { 451 gctl_error(req, "Cannot store metadata on %s (error=%d).", 452 pp->name, error); 453 return; 454 } 455 G_ELI_DEBUG(1, "Key %u changed on %s.", nkey, pp->name); 456 } 457 458 static void 459 g_eli_ctl_delkey(struct gctl_req *req, struct g_class *mp) 460 { 461 struct g_eli_softc *sc; 462 struct g_eli_metadata md; 463 struct g_provider *pp; 464 struct g_consumer *cp; 465 const char *name; 466 u_char *mkeydst, *sector; 467 intmax_t *valp; 468 size_t keysize; 469 int error, nkey, *all, *force; 470 u_int i; 471 472 g_topology_assert(); 473 474 nkey = 0; /* fixes causeless gcc warning */ 475 476 name = gctl_get_asciiparam(req, "arg0"); 477 if (name == NULL) { 478 gctl_error(req, "No 'arg%u' argument.", 0); 479 return; 480 } 481 sc = g_eli_find_device(mp, name); 482 if (sc == NULL) { 483 gctl_error(req, "Provider %s is invalid.", name); 484 return; 485 } 486 cp = LIST_FIRST(&sc->sc_geom->consumer); 487 pp = cp->provider; 488 489 error = g_eli_read_metadata(mp, pp, &md); 490 if (error != 0) { 491 gctl_error(req, "Cannot read metadata from %s (error=%d).", 492 name, error); 493 return; 494 } 495 496 all = gctl_get_paraml(req, "all", sizeof(*all)); 497 if (all == NULL) { 498 gctl_error(req, "No '%s' argument.", "all"); 499 return; 500 } 501 502 if (*all) { 503 mkeydst = md.md_mkeys; 504 keysize = sizeof(md.md_mkeys); 505 } else { 506 force = gctl_get_paraml(req, "force", sizeof(*force)); 507 if (force == NULL) { 508 gctl_error(req, "No '%s' argument.", "force"); 509 return; 510 } 511 512 valp = gctl_get_paraml(req, "keyno", sizeof(*valp)); 513 if (valp == NULL) { 514 gctl_error(req, "No '%s' argument.", "keyno"); 515 return; 516 } 517 if (*valp != -1) 518 nkey = *valp; 519 else 520 nkey = sc->sc_nkey; 521 if (nkey < 0 || nkey >= G_ELI_MAXMKEYS) { 522 gctl_error(req, "Invalid '%s' argument.", "keyno"); 523 return; 524 } 525 if (!(md.md_keys & (1 << nkey)) && !*force) { 526 gctl_error(req, "Master Key %u is not set.", nkey); 527 return; 528 } 529 md.md_keys &= ~(1 << nkey); 530 if (md.md_keys == 0 && !*force) { 531 gctl_error(req, "This is the last Master Key. Use '-f' " 532 "flag if you really want to remove it."); 533 return; 534 } 535 mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 536 keysize = G_ELI_MKEYLEN; 537 } 538 539 sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO); 540 for (i = 0; i <= g_eli_overwrites; i++) { 541 if (i == g_eli_overwrites) 542 bzero(mkeydst, keysize); 543 else 544 arc4rand(mkeydst, keysize, 0); 545 /* Store metadata with destroyed key. */ 546 eli_metadata_encode(&md, sector); 547 error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, 548 pp->sectorsize); 549 if (error != 0) { 550 G_ELI_DEBUG(0, "Cannot store metadata on %s " 551 "(error=%d).", pp->name, error); 552 } 553 } 554 bzero(&md, sizeof(md)); 555 bzero(sector, sizeof(sector)); 556 free(sector, M_ELI); 557 if (*all) 558 G_ELI_DEBUG(1, "All keys removed from %s.", pp->name); 559 else 560 G_ELI_DEBUG(1, "Key %d removed from %s.", nkey, pp->name); 561 } 562 563 static int 564 g_eli_kill_one(struct g_eli_softc *sc) 565 { 566 struct g_provider *pp; 567 struct g_consumer *cp; 568 u_char *sector; 569 int err, error = 0; 570 u_int i; 571 572 g_topology_assert(); 573 574 if (sc == NULL) 575 return (ENOENT); 576 577 pp = LIST_FIRST(&sc->sc_geom->provider); 578 g_error_provider(pp, ENXIO); 579 580 cp = LIST_FIRST(&sc->sc_geom->consumer); 581 pp = cp->provider; 582 583 sector = malloc(pp->sectorsize, M_ELI, M_WAITOK); 584 for (i = 0; i <= g_eli_overwrites; i++) { 585 if (i == g_eli_overwrites) 586 bzero(sector, pp->sectorsize); 587 else 588 arc4rand(sector, pp->sectorsize, 0); 589 err = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, 590 pp->sectorsize); 591 if (err != 0) { 592 G_ELI_DEBUG(0, "Cannot erase metadata on %s " 593 "(error=%d).", pp->name, err); 594 if (error == 0) 595 error = err; 596 } 597 } 598 free(sector, M_ELI); 599 if (error == 0) 600 G_ELI_DEBUG(0, "%s has been killed.", pp->name); 601 g_eli_destroy(sc, 1); 602 return (error); 603 } 604 605 static void 606 g_eli_ctl_kill(struct gctl_req *req, struct g_class *mp) 607 { 608 int *all, *nargs; 609 int error; 610 611 g_topology_assert(); 612 613 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 614 if (nargs == NULL) { 615 gctl_error(req, "No '%s' argument.", "nargs"); 616 return; 617 } 618 all = gctl_get_paraml(req, "all", sizeof(*all)); 619 if (all == NULL) { 620 gctl_error(req, "No '%s' argument.", "all"); 621 return; 622 } 623 if (!*all && *nargs == 0) { 624 gctl_error(req, "Too few arguments."); 625 return; 626 } 627 628 if (*all) { 629 struct g_geom *gp, *gp2; 630 631 LIST_FOREACH_SAFE(gp, &mp->geom, geom, gp2) { 632 error = g_eli_kill_one(gp->softc); 633 if (error != 0) 634 gctl_error(req, "Not fully done."); 635 } 636 } else { 637 struct g_eli_softc *sc; 638 const char *prov; 639 char param[16]; 640 int i; 641 642 for (i = 0; i < *nargs; i++) { 643 snprintf(param, sizeof(param), "arg%d", i); 644 prov = gctl_get_asciiparam(req, param); 645 if (prov == NULL) { 646 G_ELI_DEBUG(0, "No 'arg%d' argument.", i); 647 continue; 648 } 649 650 sc = g_eli_find_device(mp, prov); 651 if (sc == NULL) { 652 G_ELI_DEBUG(0, "No such provider: %s.", prov); 653 continue; 654 } 655 error = g_eli_kill_one(sc); 656 if (error != 0) 657 gctl_error(req, "Not fully done."); 658 } 659 } 660 } 661 662 void 663 g_eli_config(struct gctl_req *req, struct g_class *mp, const char *verb) 664 { 665 uint32_t *version; 666 667 g_topology_assert(); 668 669 version = gctl_get_paraml(req, "version", sizeof(*version)); 670 if (version == NULL) { 671 gctl_error(req, "No '%s' argument.", "version"); 672 return; 673 } 674 if (*version != G_ELI_VERSION) { 675 gctl_error(req, "Userland and kernel parts are out of sync."); 676 return; 677 } 678 679 if (strcmp(verb, "attach") == 0) 680 g_eli_ctl_attach(req, mp); 681 else if (strcmp(verb, "detach") == 0 || strcmp(verb, "stop") == 0) 682 g_eli_ctl_detach(req, mp); 683 else if (strcmp(verb, "onetime") == 0) 684 g_eli_ctl_onetime(req, mp); 685 else if (strcmp(verb, "setkey") == 0) 686 g_eli_ctl_setkey(req, mp); 687 else if (strcmp(verb, "delkey") == 0) 688 g_eli_ctl_delkey(req, mp); 689 else if (strcmp(verb, "kill") == 0) 690 g_eli_ctl_kill(req, mp); 691 else 692 gctl_error(req, "Unknown verb."); 693 } 694