1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 /*
  25  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  26  * Copyright (c) 2018, Joyent, Inc.
  27  */
  28 
  29 #include <fcntl.h>
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <strings.h>
  33 #include <unistd.h>
  34 #include <locale.h>
  35 #include <libgen.h>
  36 #include <sys/types.h>
  37 #include <sys/varargs.h>
  38 #include <zone.h>
  39 #include <sys/crypto/ioctladmin.h>
  40 #include "cryptoadm.h"
  41 
  42 #define DEFAULT_DEV_NUM 5
  43 #define DEFAULT_SOFT_NUM 10
  44 
  45 static crypto_get_soft_info_t *setup_get_soft_info(char *, int);
  46 
  47 /*
  48  * Prepare the argument for the LOAD_SOFT_CONFIG ioctl call for the
  49  * provider pointed by pent.  Return NULL if out of memory.
  50  */
  51 crypto_load_soft_config_t *
  52 setup_soft_conf(entry_t *pent)
  53 {
  54         crypto_load_soft_config_t       *pload_soft_conf;
  55         mechlist_t      *plist;
  56         uint_t          sup_count;
  57         size_t          extra_mech_size = 0;
  58         int             i;
  59 
  60         if (pent == NULL) {
  61                 return (NULL);
  62         }
  63 
  64         sup_count = pent->sup_count;
  65         if (sup_count > 1) {
  66                 extra_mech_size = sizeof (crypto_mech_name_t) *
  67                     (sup_count - 1);
  68         }
  69 
  70         pload_soft_conf = malloc(sizeof (crypto_load_soft_config_t) +
  71             extra_mech_size);
  72         if (pload_soft_conf == NULL) {
  73                 cryptodebug("out of memory.");
  74                 return (NULL);
  75         }
  76 
  77         (void) strlcpy(pload_soft_conf->sc_name, pent->name, MAXNAMELEN);
  78         pload_soft_conf->sc_count = sup_count;
  79 
  80         i = 0;
  81         plist =  pent->suplist;
  82         while (i < sup_count) {
  83                 (void) strlcpy(pload_soft_conf->sc_list[i++],
  84                     plist->name, CRYPTO_MAX_MECH_NAME);
  85                 plist = plist->next;
  86         }
  87 
  88         return (pload_soft_conf);
  89 }
  90 
  91 
  92 /*
  93  * Prepare the argument for the LOAD_SOFT_DISABLED ioctl call for the
  94  * provider pointed by pent.  Return NULL if out of memory.
  95  */
  96 crypto_load_soft_disabled_t *
  97 setup_soft_dis(entry_t *pent)
  98 {
  99         crypto_load_soft_disabled_t     *pload_soft_dis = NULL;
 100         mechlist_t      *plist = NULL;
 101         size_t          extra_mech_size = 0;
 102         uint_t          dis_count;
 103         int             i;
 104 
 105         if (pent == NULL) {
 106                 return (NULL);
 107         }
 108 
 109         dis_count = pent->dis_count;
 110         if (dis_count > 1) {
 111                 extra_mech_size = sizeof (crypto_mech_name_t) *
 112                     (dis_count - 1);
 113         }
 114 
 115         pload_soft_dis = malloc(sizeof (crypto_load_soft_disabled_t) +
 116             extra_mech_size);
 117         if (pload_soft_dis == NULL) {
 118                 cryptodebug("out of memory.");
 119                 return (NULL);
 120         }
 121 
 122         (void) strlcpy(pload_soft_dis->sd_name, pent->name, MAXNAMELEN);
 123         pload_soft_dis->sd_count = dis_count;
 124 
 125         i = 0;
 126         plist =  pent->dislist;
 127         while (i < dis_count) {
 128                 (void) strlcpy(pload_soft_dis->sd_list[i++],
 129                     plist->name, CRYPTO_MAX_MECH_NAME);
 130                 plist = plist->next;
 131         }
 132 
 133         return (pload_soft_dis);
 134 }
 135 
 136 
 137 /*
 138  * Prepare the argument for the LOAD_DEV_DISABLED ioctl call for the
 139  * provider pointed by pent.  Return NULL if out of memory.
 140  */
 141 crypto_load_dev_disabled_t *
 142 setup_dev_dis(entry_t *pent)
 143 {
 144         crypto_load_dev_disabled_t      *pload_dev_dis = NULL;
 145         mechlist_t      *plist = NULL;
 146         size_t          extra_mech_size = 0;
 147         uint_t          dis_count;
 148         int             i;
 149         char            pname[MAXNAMELEN];
 150         int             inst_num;
 151 
 152         if (pent == NULL) {
 153                 return (NULL);
 154         }
 155 
 156         /* get the device name and the instance number */
 157         if (split_hw_provname(pent->name, pname, &inst_num) == FAILURE) {
 158                 return (NULL);
 159         }
 160 
 161         /* allocate space for pload_dev_des */
 162         dis_count = pent->dis_count;
 163         if (dis_count > 1) {
 164                 extra_mech_size = sizeof (crypto_mech_name_t) *
 165                     (dis_count - 1);
 166         }
 167 
 168         pload_dev_dis = malloc(sizeof (crypto_load_dev_disabled_t) +
 169             extra_mech_size);
 170         if (pload_dev_dis == NULL) {
 171                 cryptodebug("out of memory.");
 172                 return (NULL);
 173         }
 174 
 175         /* set the values for pload_dev_dis */
 176         (void) strlcpy(pload_dev_dis->dd_dev_name, pname, MAXNAMELEN);
 177         pload_dev_dis->dd_dev_instance = inst_num;
 178         pload_dev_dis->dd_count = dis_count;
 179 
 180         i = 0;
 181         plist =  pent->dislist;
 182         while (i < dis_count) {
 183                 (void) strlcpy(pload_dev_dis->dd_list[i++],
 184                     plist->name, CRYPTO_MAX_MECH_NAME);
 185                 plist = plist->next;
 186         }
 187 
 188         return (pload_dev_dis);
 189 }
 190 
 191 
 192 /*
 193  * Prepare the calling argument of the UNLOAD_SOFT_MODULE ioctl call for the
 194  * provider pointed by pent.  Return NULL if out of memory.
 195  */
 196 crypto_unload_soft_module_t *
 197 setup_unload_soft(entry_t *pent)
 198 {
 199         crypto_unload_soft_module_t *punload_soft;
 200 
 201         if (pent == NULL) {
 202                 return (NULL);
 203         }
 204 
 205         punload_soft = malloc(sizeof (crypto_unload_soft_module_t));
 206         if (punload_soft == NULL) {
 207                 cryptodebug("out of memory.");
 208                 return (NULL);
 209         }
 210 
 211         (void) strlcpy(punload_soft->sm_name, pent->name, MAXNAMELEN);
 212 
 213         return (punload_soft);
 214 }
 215 
 216 
 217 /*
 218  * Prepare the calling argument for the GET_SOFT_INFO call for the provider
 219  * with the number of mechanisms specified in the second argument.
 220  *
 221  * Called by get_soft_info().
 222  */
 223 static crypto_get_soft_info_t *
 224 setup_get_soft_info(char *provname, int count)
 225 {
 226         crypto_get_soft_info_t  *psoft_info;
 227         size_t                  extra_mech_size = 0;
 228 
 229         if (provname == NULL) {
 230                 return (NULL);
 231         }
 232 
 233         if (count > 1) {
 234                 extra_mech_size = sizeof (crypto_mech_name_t) * (count - 1);
 235         }
 236 
 237         psoft_info = malloc(sizeof (crypto_get_soft_info_t) + extra_mech_size);
 238         if (psoft_info == NULL) {
 239                 cryptodebug("out of memory.");
 240                 return (NULL);
 241         }
 242 
 243         (void) strlcpy(psoft_info->si_name, provname, MAXNAMELEN);
 244         psoft_info->si_count = count;
 245 
 246         return (psoft_info);
 247 }
 248 
 249 
 250 /*
 251  * Get the device list from kernel.
 252  */
 253 int
 254 get_dev_list(crypto_get_dev_list_t **ppdevlist)
 255 {
 256         crypto_get_dev_list_t   *pdevlist;
 257         int                     fd = -1;
 258         int                     count = DEFAULT_DEV_NUM;
 259 
 260         pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
 261             sizeof (crypto_dev_list_entry_t) * (count - 1));
 262         if (pdevlist == NULL) {
 263                 cryptodebug("out of memory.");
 264                 return (FAILURE);
 265         }
 266 
 267         if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
 268                 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
 269                     ADMIN_IOCTL_DEVICE, strerror(errno));
 270                 free(pdevlist);
 271                 return (FAILURE);
 272         }
 273 
 274         pdevlist->dl_dev_count = count;
 275         if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
 276                 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
 277                     strerror(errno));
 278                 free(pdevlist);
 279                 (void) close(fd);
 280                 return (FAILURE);
 281         }
 282 
 283         /* BUFFER is too small, get the number of devices and retry it. */
 284         if (pdevlist->dl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
 285                 count = pdevlist->dl_dev_count;
 286                 free(pdevlist);
 287                 pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
 288                     sizeof (crypto_dev_list_entry_t) * (count - 1));
 289                 if (pdevlist == NULL) {
 290                         cryptodebug("out of memory.");
 291                         (void) close(fd);
 292                         return (FAILURE);
 293                 }
 294 
 295                 if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
 296                         cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
 297                             strerror(errno));
 298                         free(pdevlist);
 299                         (void) close(fd);
 300                         return (FAILURE);
 301                 }
 302         }
 303 
 304         if (pdevlist->dl_return_value != CRYPTO_SUCCESS) {
 305                 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed, "
 306                     "return_value = %d", pdevlist->dl_return_value);
 307                 free(pdevlist);
 308                 (void) close(fd);
 309                 return (FAILURE);
 310         }
 311 
 312         *ppdevlist = pdevlist;
 313         (void) close(fd);
 314         return (SUCCESS);
 315 }
 316 
 317 
 318 /*
 319  * Get all the mechanisms supported by the hardware provider.
 320  * The result will be stored in the second argument.
 321  */
 322 int
 323 get_dev_info(char *devname, int inst_num, int count, mechlist_t **ppmechlist)
 324 {
 325         crypto_get_dev_info_t   *dev_info;
 326         mechlist_t      *phead;
 327         mechlist_t      *pcur;
 328         mechlist_t      *pmech;
 329         int             fd = -1;
 330         int             i;
 331         int             rc;
 332 
 333         if (devname == NULL || count < 1) {
 334                 cryptodebug("get_dev_info(): devname is NULL or bogus count");
 335                 return (FAILURE);
 336         }
 337 
 338         /* Set up the argument for the CRYPTO_GET_DEV_INFO ioctl call */
 339         dev_info = malloc(sizeof (crypto_get_dev_info_t) +
 340             sizeof (crypto_mech_name_t) * (count - 1));
 341         if (dev_info == NULL) {
 342                 cryptodebug("out of memory.");
 343                 return (FAILURE);
 344         }
 345         (void) strlcpy(dev_info->di_dev_name, devname, MAXNAMELEN);
 346         dev_info->di_dev_instance = inst_num;
 347         dev_info->di_count = count;
 348 
 349         /* Open the ioctl device */
 350         if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
 351                 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
 352                     ADMIN_IOCTL_DEVICE, strerror(errno));
 353                 free(dev_info);
 354                 return (FAILURE);
 355         }
 356 
 357         if (ioctl(fd, CRYPTO_GET_DEV_INFO, dev_info) == -1) {
 358                 cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed: %s",
 359                     strerror(errno));
 360                 free(dev_info);
 361                 (void) close(fd);
 362                 return (FAILURE);
 363         }
 364 
 365         if (dev_info->di_return_value != CRYPTO_SUCCESS) {
 366                 cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed, "
 367                     "return_value = %d", dev_info->di_return_value);
 368                 free(dev_info);
 369                 (void) close(fd);
 370                 return (FAILURE);
 371         }
 372 
 373         phead = pcur = NULL;
 374         rc = SUCCESS;
 375         for (i = 0; i < dev_info->di_count; i++) {
 376                 pmech = create_mech(&dev_info->di_list[i][0]);
 377                 if (pmech == NULL) {
 378                         rc = FAILURE;
 379                         break;
 380                 } else {
 381                         if (phead == NULL) {
 382                                 phead = pcur = pmech;
 383                         } else {
 384                                 pcur->next = pmech;
 385                                 pcur = pmech;
 386                         }
 387                 }
 388         }
 389 
 390         if (rc == SUCCESS) {
 391                 *ppmechlist = phead;
 392         } else {
 393                 free_mechlist(phead);
 394         }
 395 
 396         free(dev_info);
 397         (void) close(fd);
 398         return (rc);
 399 }
 400 
 401 
 402 /*
 403  * Get the supported mechanism list of the software provider from kernel.
 404  *
 405  * Parameters phardlist and psoftlist are supplied by get_kcfconf_info().
 406  * If NULL, this function calls get_kcfconf_info() internally.
 407  */
 408 int
 409 get_soft_info(char *provname, mechlist_t **ppmechlist,
 410         entrylist_t *phardlist, entrylist_t *psoftlist)
 411 {
 412         boolean_t               in_kernel = B_FALSE;
 413         crypto_get_soft_info_t  *psoft_info;
 414         mechlist_t              *phead;
 415         mechlist_t              *pmech;
 416         mechlist_t              *pcur;
 417         entry_t                 *pent = NULL;
 418         int                     count;
 419         int                     fd = -1;
 420         int                     rc;
 421         int                     i;
 422 
 423         if (provname == NULL) {
 424                 return (FAILURE);
 425         }
 426 
 427         if (getzoneid() == GLOBAL_ZONEID) {
 428                 /* use kcf.conf for kernel software providers in global zone */
 429                 if ((pent = getent_kef(provname, phardlist, psoftlist)) ==
 430                     NULL) {
 431 
 432                         /* No kcf.conf entry for this provider */
 433                         if (check_kernel_for_soft(provname, NULL, &in_kernel)
 434                             == FAILURE) {
 435                                 return (FAILURE);
 436                         } else if (in_kernel == B_FALSE) {
 437                                 cryptoerror(LOG_STDERR,
 438                                     gettext("%s does not exist."), provname);
 439                                 return (FAILURE);
 440                         }
 441 
 442                         /*
 443                          * Set mech count to 1.  It will be reset to the
 444                          * correct value later if the setup buffer is too small.
 445                          */
 446                         count = 1;
 447                 } else {
 448                         count = pent->sup_count;
 449                         free_entry(pent);
 450                 }
 451         } else {
 452                 /*
 453                  * kcf.conf not there in non-global zone: set mech count to 1.
 454                  * It will be reset to the correct value later if the setup
 455                  * buffer is too small.
 456                  */
 457                 count = 1;
 458         }
 459 
 460         if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) {
 461                 return (FAILURE);
 462         }
 463 
 464         if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
 465                 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
 466                     ADMIN_IOCTL_DEVICE, strerror(errno));
 467                 free(psoft_info);
 468                 return (FAILURE);
 469         }
 470 
 471         /* make GET_SOFT_INFO ioctl call */
 472         if ((rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info)) == -1) {
 473                 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed: %s",
 474                     strerror(errno));
 475                 (void) close(fd);
 476                 free(psoft_info);
 477                 return (FAILURE);
 478         }
 479 
 480         /* BUFFER is too small, get the number of mechanisms and retry it. */
 481         if (psoft_info->si_return_value == CRYPTO_BUFFER_TOO_SMALL) {
 482                 count = psoft_info->si_count;
 483                 free(psoft_info);
 484                 if ((psoft_info = setup_get_soft_info(provname, count))
 485                     == NULL) {
 486                         (void) close(fd);
 487                         return (FAILURE);
 488                 } else {
 489                         rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info);
 490                         if (rc == -1) {
 491                                 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl "
 492                                     "failed: %s", strerror(errno));
 493                                 (void) close(fd);
 494                                 free(psoft_info);
 495                                 return (FAILURE);
 496                         }
 497                 }
 498         }
 499 
 500         (void) close(fd);
 501         if (psoft_info->si_return_value != CRYPTO_SUCCESS) {
 502                 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed, "
 503                     "return_value = %d", psoft_info->si_return_value);
 504                 free(psoft_info);
 505                 return (FAILURE);
 506         }
 507 
 508 
 509         /* Build the mechanism linked list and return it */
 510         rc = SUCCESS;
 511         phead = pcur = NULL;
 512         for (i = 0; i < psoft_info->si_count; i++) {
 513                 pmech = create_mech(&psoft_info->si_list[i][0]);
 514                 if (pmech == NULL) {
 515                         rc = FAILURE;
 516                         break;
 517                 } else {
 518                         if (phead == NULL) {
 519                                 phead = pcur = pmech;
 520                         } else {
 521                                 pcur->next = pmech;
 522                                 pcur = pmech;
 523                         }
 524                 }
 525         }
 526 
 527         if (rc == FAILURE) {
 528                 free_mechlist(phead);
 529         } else {
 530                 *ppmechlist = phead;
 531         }
 532 
 533         free(psoft_info);
 534         return (rc);
 535 }
 536 
 537 
 538 /*
 539  * Get the kernel software provider list from kernel.
 540  */
 541 int
 542 get_soft_list(crypto_get_soft_list_t **ppsoftlist)
 543 {
 544         crypto_get_soft_list_t *psoftlist = NULL;
 545         int     count = DEFAULT_SOFT_NUM;
 546         int     len;
 547         int     fd = -1;
 548 
 549         if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
 550                 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
 551                     ADMIN_IOCTL_DEVICE, strerror(errno));
 552                 return (FAILURE);
 553         }
 554 
 555         len = MAXNAMELEN * count;
 556         psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
 557         if (psoftlist == NULL) {
 558                 cryptodebug("out of memory.");
 559                 (void) close(fd);
 560                 return (FAILURE);
 561         }
 562         psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
 563         psoftlist->sl_soft_count = count;
 564         psoftlist->sl_soft_len = len;
 565 
 566         if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
 567                 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed: %s",
 568                     strerror(errno));
 569                 free(psoftlist);
 570                 (void) close(fd);
 571                 return (FAILURE);
 572         }
 573 
 574         /*
 575          * if BUFFER is too small, get the number of software providers and
 576          * the minimum length needed for names and length and retry it.
 577          */
 578         if (psoftlist->sl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
 579                 count = psoftlist->sl_soft_count;
 580                 len = psoftlist->sl_soft_len;
 581                 free(psoftlist);
 582                 psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
 583                 if (psoftlist == NULL) {
 584                         cryptodebug("out of memory.");
 585                         (void) close(fd);
 586                         return (FAILURE);
 587                 }
 588                 psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
 589                 psoftlist->sl_soft_count = count;
 590                 psoftlist->sl_soft_len = len;
 591 
 592                 if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
 593                         cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed:"
 594                             "%s", strerror(errno));
 595                         free(psoftlist);
 596                         (void) close(fd);
 597                         return (FAILURE);
 598                 }
 599         }
 600 
 601         if (psoftlist->sl_return_value != CRYPTO_SUCCESS) {
 602                 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed, "
 603                     "return_value = %d", psoftlist->sl_return_value);
 604                 free(psoftlist);
 605                 (void) close(fd);
 606                 return (FAILURE);
 607         }
 608 
 609         *ppsoftlist = psoftlist;
 610         (void) close(fd);
 611         return (SUCCESS);
 612 }