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