2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <strings.h>
2N/A#include <errno.h>
2N/A#include <smbsrv/libsmb.h>
2N/A#include "smbfs_lib.h"
2N/A#include <netsmb/libsmbfs.h>
2N/A
2N/A#define SMBFS_PASSWD SMBIOD_PWDFILE
2N/A#define SMBFS_OPASSWD "/var/smb/osmbfspasswd"
2N/A#define SMBFS_PASSTEMP "/var/smb/smbfsptmp"
2N/A#define SMBFS_PASSLCK "/var/smb/.smbfspwd.lock"
2N/A
2N/A#define SMBFS_PWD_BUFSIZE 1024
2N/A
2N/Atypedef enum {
2N/A SMBFS_PWD_UID = 0,
2N/A SMBFS_PWD_DOMAIN,
2N/A SMBFS_PWD_USER,
2N/A SMBFS_PWD_LMHASH,
2N/A SMBFS_PWD_NTHASH,
2N/A SMBFS_PWD_NARG
2N/A} smbfs_pwdarg_t;
2N/A
2N/Astatic smb_lockinfo_t lockinfo = {{0, 0, 0, 0, 0, 0}, 0, 0, -1, DEFAULTMUTEX};
2N/A
2N/Astatic int smbfs_pwd_lock(void);
2N/Astatic int smbfs_pwd_unlock(void);
2N/A
2N/A/*
2N/A * buffer structure used by smbfs_pwd_fgetent/smbfs_pwd_fputent
2N/A */
2N/Atypedef struct smbfs_pwbuf {
2N/A char pw_buf[SMBFS_PWD_BUFSIZE];
2N/A smbfs_passwd_t *pw_pwd;
2N/A} smbfs_pwbuf_t;
2N/A
2N/Astatic smbfs_pwbuf_t *smbfs_pwd_fgetent(FILE *, smbfs_pwbuf_t *);
2N/Astatic int smbfs_pwd_fputent(FILE *, const smbfs_pwbuf_t *);
2N/A
2N/A/*
2N/A * Loads the smbfspasswd entries into the password keychain.
2N/A */
2N/Aint
2N/Asmbfs_pwd_loadkeychain(void)
2N/A{
2N/A smbfs_pwbuf_t pwbuf;
2N/A smbfs_passwd_t smbpw;
2N/A FILE *fp;
2N/A int err;
2N/A
2N/A err = smbfs_pwd_lock();
2N/A if (err != SMB_PWE_SUCCESS)
2N/A return (err);
2N/A
2N/A if ((fp = fopen(SMBFS_PASSWD, "rF")) == NULL) {
2N/A (void) smbfs_pwd_unlock();
2N/A return (err);
2N/A }
2N/A
2N/A pwbuf.pw_pwd = &smbpw;
2N/A
2N/A while (smbfs_pwd_fgetent(fp, &pwbuf) != NULL) {
2N/A err = smbfs_keychain_addhash(smbpw.pw_uid, smbpw.pw_dom,
2N/A smbpw.pw_usr, smbpw.pw_lmhash, smbpw.pw_nthash);
2N/A
2N/A if (err != 0) {
2N/A (void) fclose(fp);
2N/A (void) smbfs_pwd_unlock();
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A (void) smbfs_pwd_unlock();
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Updates the password hashes of the given password info if the entry already
2N/A * exists, otherwise it'll add an entry with given password information.
2N/A *
2N/A * The entries are added to the file based on the uid in ascending order.
2N/A */
2N/Aint
2N/Asmbfs_pwd_add(smbfs_passwd_t *newpw)
2N/A{
2N/A struct stat64 stbuf;
2N/A FILE *src, *dst;
2N/A int tempfd;
2N/A int err = SMB_PWE_SUCCESS;
2N/A smbfs_pwbuf_t pwbuf, newpwbuf;
2N/A smbfs_passwd_t smbpw;
2N/A boolean_t doinsert = B_FALSE;
2N/A
2N/A err = smbfs_pwd_lock();
2N/A if (err != SMB_PWE_SUCCESS)
2N/A return (err);
2N/A
2N/A if (stat64(SMBFS_PASSWD, &stbuf) < 0) {
2N/A err = SMB_PWE_STAT_FAILED;
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if ((tempfd =
2N/A open(SMBFS_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
2N/A err = SMB_PWE_OPEN_FAILED;
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if ((dst = fdopen(tempfd, "wF")) == NULL) {
2N/A err = SMB_PWE_OPEN_FAILED;
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if ((src = fopen(SMBFS_PASSWD, "rF")) == NULL) {
2N/A err = SMB_PWE_OPEN_FAILED;
2N/A (void) fclose(dst);
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A pwbuf.pw_pwd = &smbpw;
2N/A newpwbuf.pw_pwd = newpw;
2N/A
2N/A /*
2N/A * copy old password entries to temporary file while replacing
2N/A * the entry that matches uid, domain, and user
2N/A */
2N/A while (smbfs_pwd_fgetent(src, &pwbuf) != NULL) {
2N/A if (newpw->pw_uid < smbpw.pw_uid) {
2N/A doinsert = B_TRUE;
2N/A break;
2N/A } else if (newpw->pw_uid == smbpw.pw_uid) {
2N/A if ((smb_strcasecmp(
2N/A smbpw.pw_dom, newpw->pw_dom, 0) == 0) &&
2N/A (smb_strcasecmp(
2N/A smbpw.pw_usr, newpw->pw_usr, 0) == 0))
2N/A break;
2N/A }
2N/A
2N/A err = smbfs_pwd_fputent(dst, &pwbuf);
2N/A
2N/A if (err != SMB_PWE_SUCCESS) {
2N/A (void) fclose(src);
2N/A (void) fclose(dst);
2N/A goto passwd_exit;
2N/A }
2N/A }
2N/A
2N/A err = smbfs_pwd_fputent(dst, &newpwbuf);
2N/A
2N/A if (err != SMB_PWE_SUCCESS) {
2N/A (void) fclose(src);
2N/A (void) fclose(dst);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if (doinsert) {
2N/A /*
2N/A * a new entry was inserted before the old entry
2N/A * so now copy the old entry to the temporary
2N/A * file
2N/A */
2N/A err = smbfs_pwd_fputent(dst, &pwbuf);
2N/A
2N/A if (err != SMB_PWE_SUCCESS) {
2N/A (void) fclose(src);
2N/A (void) fclose(dst);
2N/A goto passwd_exit;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * copy any remaining old password entries to temporary file
2N/A */
2N/A while (smbfs_pwd_fgetent(src, &pwbuf) != NULL) {
2N/A err = smbfs_pwd_fputent(dst, &pwbuf);
2N/A
2N/A if (err != SMB_PWE_SUCCESS) {
2N/A (void) fclose(src);
2N/A (void) fclose(dst);
2N/A goto passwd_exit;
2N/A }
2N/A }
2N/A
2N/A (void) fclose(src);
2N/A if (fclose(dst) != 0) {
2N/A err = SMB_PWE_CLOSE_FAILED;
2N/A goto passwd_exit; /* Don't trust the temporary file */
2N/A }
2N/A
2N/A /* Rename temp to passwd */
2N/A if (unlink(SMBFS_OPASSWD) && access(SMBFS_OPASSWD, 0) == 0) {
2N/A err = SMB_PWE_UPDATE_FAILED;
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if (link(SMBFS_PASSWD, SMBFS_OPASSWD) == -1) {
2N/A err = SMB_PWE_UPDATE_FAILED;
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if (rename(SMBFS_PASSTEMP, SMBFS_PASSWD) == -1) {
2N/A err = SMB_PWE_UPDATE_FAILED;
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A (void) chmod(SMBFS_PASSWD, 0400);
2N/A
2N/Apasswd_exit:
2N/A (void) smbfs_pwd_unlock();
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_pwd_del(smbfs_passwd_t *newpw, boolean_t delete_all)
2N/A{
2N/A struct stat64 stbuf;
2N/A FILE *src, *dst;
2N/A int tempfd;
2N/A int err = SMB_PWE_SUCCESS;
2N/A smbfs_pwbuf_t pwbuf;
2N/A smbfs_passwd_t smbpw;
2N/A
2N/A err = smbfs_pwd_lock();
2N/A if (err != SMB_PWE_SUCCESS)
2N/A return (err);
2N/A
2N/A if (stat64(SMBFS_PASSWD, &stbuf) < 0) {
2N/A err = SMB_PWE_STAT_FAILED;
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if ((tempfd =
2N/A open(SMBFS_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
2N/A err = SMB_PWE_OPEN_FAILED;
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if ((dst = fdopen(tempfd, "wF")) == NULL) {
2N/A err = SMB_PWE_OPEN_FAILED;
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if ((src = fopen(SMBFS_PASSWD, "rF")) == NULL) {
2N/A err = SMB_PWE_OPEN_FAILED;
2N/A (void) fclose(dst);
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A pwbuf.pw_pwd = &smbpw;
2N/A
2N/A /*
2N/A * copy password entries to temporary file while skipping
2N/A * the entry that matches uid, domain, and user
2N/A */
2N/A while (smbfs_pwd_fgetent(src, &pwbuf) != NULL) {
2N/A if (newpw->pw_uid == smbpw.pw_uid) {
2N/A
2N/A if (delete_all)
2N/A continue;
2N/A
2N/A if ((smb_strcasecmp(
2N/A smbpw.pw_dom, newpw->pw_dom, 0) == 0) &&
2N/A (smb_strcasecmp(
2N/A smbpw.pw_usr, newpw->pw_usr, 0) == 0))
2N/A continue;
2N/A }
2N/A
2N/A err = smbfs_pwd_fputent(dst, &pwbuf);
2N/A
2N/A if (err != SMB_PWE_SUCCESS) {
2N/A (void) fclose(src);
2N/A (void) fclose(dst);
2N/A goto passwd_exit;
2N/A }
2N/A }
2N/A
2N/A
2N/A (void) fclose(src);
2N/A if (fclose(dst) != 0) {
2N/A err = SMB_PWE_CLOSE_FAILED;
2N/A goto passwd_exit; /* Don't trust the temporary file */
2N/A }
2N/A
2N/A /* Rename temp to passwd */
2N/A if (unlink(SMBFS_OPASSWD) && access(SMBFS_OPASSWD, 0) == 0) {
2N/A err = SMB_PWE_UPDATE_FAILED;
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if (link(SMBFS_PASSWD, SMBFS_OPASSWD) == -1) {
2N/A err = SMB_PWE_UPDATE_FAILED;
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A if (rename(SMBFS_PASSTEMP, SMBFS_PASSWD) == -1) {
2N/A err = SMB_PWE_UPDATE_FAILED;
2N/A (void) unlink(SMBFS_PASSTEMP);
2N/A goto passwd_exit;
2N/A }
2N/A
2N/A (void) chmod(SMBFS_PASSWD, 0400);
2N/A
2N/Apasswd_exit:
2N/A (void) smbfs_pwd_unlock();
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Parse the buffer in the passed pwbuf and fill in the
2N/A * smbfs password structure to point to the parsed information.
2N/A * The entry format is:
2N/A *
2N/A * <user-id>:<domain>:<user-name>:<LM hash>:<NTLM hash>
2N/A *
2N/A * Returns a pointer to the passed pwbuf structure on success,
2N/A * otherwise returns NULL.
2N/A */
2N/Astatic smbfs_pwbuf_t *
2N/Asmbfs_pwd_fgetent(FILE *fp, smbfs_pwbuf_t *pwbuf)
2N/A{
2N/A char *argv[SMBFS_PWD_NARG];
2N/A char *pwentry;
2N/A smbfs_passwd_t *pw;
2N/A smbfs_pwdarg_t i;
2N/A int lm_len, nt_len;
2N/A
2N/A pwentry = pwbuf->pw_buf;
2N/A if (fgets(pwentry, SMBFS_PWD_BUFSIZE, fp) == NULL)
2N/A return (NULL);
2N/A (void) trim_whitespace(pwentry);
2N/A
2N/A for (i = 0; i < SMBFS_PWD_NARG; ++i) {
2N/A if ((argv[i] = strsep((char **)&pwentry, ":")) == NULL)
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((*argv[SMBFS_PWD_UID] == '\0') ||
2N/A (*argv[SMBFS_PWD_DOMAIN] == '\0') ||
2N/A (*argv[SMBFS_PWD_USER] == '\0'))
2N/A return (NULL);
2N/A
2N/A pw = pwbuf->pw_pwd;
2N/A bzero(pw, sizeof (smbfs_passwd_t));
2N/A pw->pw_uid = strtoul(argv[SMBFS_PWD_UID], 0, 10);
2N/A (void) strlcpy(pw->pw_dom, argv[SMBFS_PWD_DOMAIN],
2N/A sizeof (pw->pw_dom));
2N/A (void) strlcpy(pw->pw_usr, argv[SMBFS_PWD_USER], sizeof (pw->pw_usr));
2N/A
2N/A lm_len = strlen(argv[SMBFS_PWD_LMHASH]);
2N/A if (lm_len == SMBAUTH_HEXHASH_SZ) {
2N/A (void) hextobin(argv[SMBFS_PWD_LMHASH], SMBAUTH_HEXHASH_SZ,
2N/A (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ);
2N/A } else if (lm_len != 0) {
2N/A return (NULL);
2N/A }
2N/A
2N/A nt_len = strlen(argv[SMBFS_PWD_NTHASH]);
2N/A if (nt_len == SMBAUTH_HEXHASH_SZ) {
2N/A (void) hextobin(argv[SMBFS_PWD_NTHASH], SMBAUTH_HEXHASH_SZ,
2N/A (char *)pw->pw_nthash, SMBAUTH_HASH_SZ);
2N/A } else if (nt_len != 0) {
2N/A return (NULL);
2N/A }
2N/A
2N/A return (pwbuf);
2N/A}
2N/A
2N/A/*
2N/A * Converts LM/NTLM hash to hex string and write them along with user's name,
2N/A * domain, and Id to the smbfspasswd file.
2N/A */
2N/Astatic int
2N/Asmbfs_pwd_fputent(FILE *fp, const smbfs_pwbuf_t *pwbuf)
2N/A{
2N/A smbfs_passwd_t *pw = pwbuf->pw_pwd;
2N/A char hex_nthash[SMBAUTH_HEXHASH_SZ+1];
2N/A char hex_lmhash[SMBAUTH_HEXHASH_SZ+1];
2N/A int rc;
2N/A
2N/A (void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ,
2N/A hex_lmhash, SMBAUTH_HEXHASH_SZ);
2N/A hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0';
2N/A
2N/A (void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ,
2N/A hex_nthash, SMBAUTH_HEXHASH_SZ);
2N/A hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0';
2N/A
2N/A rc = fprintf(fp, "%u:%s:%s:%s:%s\n", pw->pw_uid, pw->pw_dom, pw->pw_usr,
2N/A hex_lmhash, hex_nthash);
2N/A
2N/A if (rc <= 0)
2N/A return (SMB_PWE_WRITE_FAILED);
2N/A
2N/A return (SMB_PWE_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * A wrapper around smb_file_lock() which locks smbfs password
2N/A * file so that only one thread at a time is operational.
2N/A */
2N/Astatic int
2N/Asmbfs_pwd_lock(void)
2N/A{
2N/A int res;
2N/A
2N/A if (smb_file_lock(SMBFS_PASSLCK, &lockinfo)) {
2N/A switch (errno) {
2N/A case EINTR:
2N/A res = SMB_PWE_BUSY;
2N/A break;
2N/A case EACCES:
2N/A res = SMB_PWE_DENIED;
2N/A break;
2N/A case 0:
2N/A res = SMB_PWE_SUCCESS;
2N/A break;
2N/A }
2N/A } else
2N/A res = SMB_PWE_SUCCESS;
2N/A
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * A wrapper around smb_file_unlock() which unlocks
2N/A * smbfs password file.
2N/A */
2N/Astatic int
2N/Asmbfs_pwd_unlock(void)
2N/A{
2N/A if (smb_file_unlock(&lockinfo))
2N/A return (SMB_PWE_SYSTEM_ERROR);
2N/A
2N/A return (SMB_PWE_SUCCESS);
2N/A}