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 /*
  23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*
  29  * External interface to the libsmbfs/netsmb keychain
  30  * storage mechanism.  This interface is consumed by
  31  * the "smbutil" commands: login, logout, ...
  32  * and by the SMBFS PAM module.
  33  */
  34 
  35 #include <sys/types.h>
  36 
  37 #include <errno.h>
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <string.h>
  41 #include <unistd.h>
  42 #include <libintl.h>
  43 
  44 #include <cflib.h>
  45 #include <netsmb/smb_dev.h>
  46 #include <netsmb/smb_lib.h>
  47 #include <netsmb/smb_keychain.h>
  48 
  49 #include "charsets.h"
  50 #include "private.h"
  51 #include "ntlm.h"
  52 
  53 /* common func. for add/del/chk */
  54 static int
  55 smbfs_keychain_cmn(
  56         int cmd,
  57         uid_t uid,
  58         const char *dom,
  59         const char *usr,
  60         uchar_t *lmhash,
  61         uchar_t *nthash)
  62 {
  63         smbioc_pk_t pk;
  64         int err, fd, sz;
  65 
  66         memset(&pk, 0, sizeof (pk));
  67         pk.pk_uid = uid;
  68         err = 0;
  69         fd = -1;
  70 
  71         switch (cmd) {
  72 
  73         case SMBIOC_PK_ADD:
  74                 /*
  75                  * Add password hashes to the keychain.
  76                  */
  77                 if (lmhash == NULL || nthash == NULL) {
  78                         err = SMB_KEYCHAIN_BADPASSWD;
  79                         goto out;
  80                 }
  81                 memcpy(pk.pk_lmhash, lmhash, SMBIOC_HASH_SZ);
  82                 memcpy(pk.pk_nthash, nthash, SMBIOC_HASH_SZ);
  83                 /* FALLTHROUGH */
  84 
  85         case SMBIOC_PK_CHK:
  86         case SMBIOC_PK_DEL:
  87                 /*
  88                  * Copy domain and user.
  89                  */
  90                 if (dom == NULL) {
  91                         err = SMB_KEYCHAIN_BADDOMAIN;
  92                         goto out;
  93                 }
  94                 sz = sizeof (pk.pk_dom);
  95                 if (strlcpy(pk.pk_dom, dom, sz) >= sz) {
  96                         err = SMB_KEYCHAIN_BADDOMAIN;
  97                         goto out;
  98                 }
  99                 if (usr == NULL) {
 100                         err = SMB_KEYCHAIN_BADUSER;
 101                         goto out;
 102                 }
 103                 sz = sizeof (pk.pk_usr);
 104                 if (strlcpy(pk.pk_usr, usr, sz) >= sz) {
 105                         err = SMB_KEYCHAIN_BADUSER;
 106                         goto out;
 107                 }
 108                 break;
 109 
 110         case SMBIOC_PK_DEL_OWNER:       /* all owned by the caller */
 111         case SMBIOC_PK_DEL_EVERYONE:    /* all owned by everyone */
 112                 /*
 113                  * These two do not copyin any args, but we'll
 114                  * pass pk here anyway just so we can use the
 115                  * common code path below.
 116                  */
 117                 break;
 118 
 119         default:
 120                 err = SMB_KEYCHAIN_UNKNOWN;
 121                 goto out;
 122         }
 123 
 124         fd = smb_open_driver();
 125         if (fd < 0) {
 126                 err = SMB_KEYCHAIN_NODRIVER;
 127                 goto out;
 128         }
 129 
 130         err = 0;
 131         if (nsmb_ioctl(fd, cmd, &pk) < 0) {
 132                 err = errno;
 133                 goto out;
 134         }
 135 
 136         if (cmd == SMBIOC_PK_CHK) {
 137                 if (lmhash != NULL)
 138                         memcpy(lmhash, pk.pk_lmhash, SMBIOC_HASH_SZ);
 139                 if (nthash != NULL)
 140                         memcpy(nthash, pk.pk_nthash, SMBIOC_HASH_SZ);
 141         }
 142 
 143 out:
 144         if (fd != -1)
 145                 nsmb_close(fd);
 146 
 147         return (err);
 148 }
 149 
 150 /*
 151  * Add a password to the keychain.
 152  *
 153  * Note: pass is a cleartext password.
 154  * We use it here to compute the LM hash and NT hash,
 155  * and then store ONLY the hashes.
 156  */
 157 int
 158 smbfs_keychain_add(uid_t uid, const char *dom, const char *usr,
 159         const char *pass)
 160 {
 161         uchar_t lmhash[SMBIOC_HASH_SZ];
 162         uchar_t nthash[SMBIOC_HASH_SZ];
 163         int err, cmd = SMBIOC_PK_ADD;
 164 
 165         if (pass == NULL)
 166                 return (SMB_KEYCHAIN_BADPASSWD);
 167 
 168         if ((err = ntlm_compute_lm_hash(lmhash, pass)) != 0)
 169                 return (err);
 170         if ((err = ntlm_compute_nt_hash(nthash, pass)) != 0)
 171                 return (err);
 172 
 173         err = smbfs_keychain_cmn(cmd, uid, dom, usr, lmhash, nthash);
 174         return (err);
 175 }
 176 
 177 /* Variant of the above that takes an NT hash. */
 178 int
 179 smbfs_keychain_addhash(uid_t uid, const char *dom, const char *usr,
 180         const uchar_t *nthash)
 181 {
 182         static const uchar_t lmhash[SMBIOC_HASH_SZ] = { 0 };
 183         int err, cmd = SMBIOC_PK_ADD;
 184         err = smbfs_keychain_cmn(cmd, uid, dom, usr,
 185             (uchar_t *)lmhash, (uchar_t *)nthash);
 186         return (err);
 187 }
 188 
 189 /* Delete a password from the keychain. */
 190 int
 191 smbfs_keychain_del(uid_t uid, const char *dom, const char *usr)
 192 {
 193         return (smbfs_keychain_cmn(SMBIOC_PK_DEL, uid, dom, usr, NULL, NULL));
 194 }
 195 
 196 /*
 197  * Check for existence of a keychain entry.
 198  * Returns 0 if it exists, else ENOENT.
 199  */
 200 int
 201 smbfs_keychain_chk(const char *dom, const char *usr)
 202 {
 203         uid_t uid = (uid_t)-1;
 204         return (smbfs_keychain_cmn(SMBIOC_PK_CHK, uid, dom, usr, NULL, NULL));
 205 }
 206 
 207 /*
 208  * Get the stored hashes
 209  */
 210 int
 211 smbfs_keychain_get(const char *dom, const char *usr,
 212                 uchar_t *lmhash, uchar_t *nthash)
 213 {
 214         uid_t uid = (uid_t)-1;
 215         int err, cmd = SMBIOC_PK_CHK;
 216 
 217         err = smbfs_keychain_cmn(cmd, uid, dom, usr, lmhash, nthash);
 218         return (err);
 219 }
 220 
 221 /*
 222  * Delete all keychain entries owned by the caller.
 223  */
 224 int
 225 smbfs_keychain_del_owner()
 226 {
 227         int cmd = SMBIOC_PK_DEL_OWNER;
 228         uid_t uid = getuid();
 229         return (smbfs_keychain_cmn(cmd, uid, NULL, NULL, NULL, NULL));
 230 }
 231 
 232 /*
 233  * Delete all keychain entries (regardless of onwer).
 234  * Requires super-user privliege.
 235  */
 236 int
 237 smbfs_keychain_del_everyone()
 238 {
 239         int cmd = SMBIOC_PK_DEL_EVERYONE;
 240         uid_t uid = getuid();
 241         return (smbfs_keychain_cmn(cmd, uid, NULL, NULL, NULL, NULL));
 242 }
 243 
 244 /*
 245  * Private function to get keychain p/w hashes.
 246  */
 247 int
 248 smb_get_keychain(struct smb_ctx *ctx)
 249 {
 250         int err;
 251 
 252         if (ctx->ct_fullserver == NULL) {
 253                 DPRINT("ct_fullserver == NULL");
 254                 return (EINVAL);
 255         }
 256 
 257         /*
 258          * 1st: try lookup using system name
 259          */
 260         err = smbfs_keychain_get(ctx->ct_fullserver, ctx->ct_user,
 261             ctx->ct_lmhash, ctx->ct_nthash);
 262         if (!err) {
 263                 ctx->ct_flags |= SMBCF_KCFOUND;
 264                 DPRINT("found keychain entry for"
 265                     " server/user: %s/%s\n",
 266                     ctx->ct_fullserver, ctx->ct_user);
 267                 return (0);
 268         }
 269 
 270         /*
 271          * 2nd: try lookup using domain name
 272          */
 273         err = smbfs_keychain_get(ctx->ct_domain, ctx->ct_user,
 274             ctx->ct_lmhash, ctx->ct_nthash);
 275         if (!err) {
 276                 ctx->ct_flags |= (SMBCF_KCFOUND | SMBCF_KCDOMAIN);
 277                 DPRINT("found keychain entry for"
 278                     " domain/user: %s/%s\n",
 279                     ctx->ct_domain, ctx->ct_user);
 280                 return (0);
 281         }
 282 
 283         return (err);
 284 }
 285 
 286 
 287 /*
 288  * This is not really part of the keychain library,
 289  * but is typically needed in code that wants to
 290  * provide (editable) defaults for domain/user
 291  *
 292  * Get default domain and user names
 293  * Server name is optional.
 294  */
 295 int
 296 smbfs_default_dom_usr(const char *home, const char *server,
 297         char *dom, int maxdom, char *usr, int maxusr)
 298 {
 299         struct smb_ctx  *ctx;
 300         int err;
 301 
 302         err = smb_ctx_alloc(&ctx);
 303         if (err)
 304                 return (err);
 305 
 306         if (server) {
 307                 err = smb_ctx_setfullserver(ctx, server);
 308                 if (err != 0)
 309                         goto out;
 310         }
 311 
 312         if (home && *home) {
 313                 if (ctx->ct_home)
 314                         free(ctx->ct_home);
 315                 ctx->ct_home = strdup(home);
 316         }
 317 
 318         err = smb_ctx_readrc(ctx);
 319         if (err)
 320                 goto out;
 321 
 322         if (dom)
 323                 strlcpy(dom, ctx->ct_domain, maxdom);
 324 
 325         if (usr)
 326                 strlcpy(usr, ctx->ct_user, maxusr);
 327 
 328 out:
 329         smb_ctx_free(ctx);
 330         return (err);
 331 }