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