/*
   SSSD

   Client Interface for NSS and PAM.

   Authors:
        Simo Sorce <ssorce@redhat.com>

   Copyright (C) Red Hat, Inc 2007

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _SSSCLI_H
#define _SSSCLI_H

#include <nss.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>

#ifndef HAVE_ERRNO_T
#define HAVE_ERRNO_T
typedef int errno_t;
#endif


#ifndef EOK
#define EOK 0
#endif

#define SSS_NSS_PROTOCOL_VERSION 1
#define SSS_PAM_PROTOCOL_VERSION 3
#define SSS_SUDO_PROTOCOL_VERSION 1
#define SSS_AUTOFS_PROTOCOL_VERSION 1
#define SSS_SSH_PROTOCOL_VERSION 0
#define SSS_PAC_PROTOCOL_VERSION 1

#ifdef LOGIN_NAME_MAX
#define SSS_NAME_MAX LOGIN_NAME_MAX
#else
#define SSS_NAME_MAX 256
#endif

/**
 * @defgroup sss_cli_command SSS client commands
 * @{
 */

/** The allowed commands a SSS client can send to the SSSD */

enum sss_cli_command {
/* null */
    SSS_CLI_NULL           = 0x0000,

/* version */
    SSS_GET_VERSION    = 0x0001,

/* passwd */

    SSS_NSS_GETPWNAM       = 0x0011,
    SSS_NSS_GETPWUID       = 0x0012,
    SSS_NSS_SETPWENT       = 0x0013,
    SSS_NSS_GETPWENT       = 0x0014,
    SSS_NSS_ENDPWENT       = 0x0015,

/* group */

    SSS_NSS_GETGRNAM       = 0x0021,
    SSS_NSS_GETGRGID       = 0x0022,
    SSS_NSS_SETGRENT       = 0x0023,
    SSS_NSS_GETGRENT       = 0x0024,
    SSS_NSS_ENDGRENT       = 0x0025,
    SSS_NSS_INITGR         = 0x0026,

#if 0
/* aliases */

    SSS_NSS_GETALIASBYNAME = 0x0031,
    SSS_NSS_GETALIASBYPORT = 0x0032,
    SSS_NSS_SETALIASENT    = 0x0033,
    SSS_NSS_GETALIASENT    = 0x0034,
    SSS_NSS_ENDALIASENT    = 0x0035,

/* ethers */

    SSS_NSS_GETHOSTTON     = 0x0041,
    SSS_NSS_GETNTOHOST     = 0x0042,
    SSS_NSS_SETETHERENT    = 0x0043,
    SSS_NSS_GETETHERENT    = 0x0044,
    SSS_NSS_ENDETHERENT    = 0x0045,

/* hosts */

    SSS_NSS_GETHOSTBYNAME  = 0x0051,
    SSS_NSS_GETHOSTBYNAME2 = 0x0052,
    SSS_NSS_GETHOSTBYADDR  = 0x0053,
    SSS_NSS_SETHOSTENT     = 0x0054,
    SSS_NSS_GETHOSTENT     = 0x0055,
    SSS_NSS_ENDHOSTENT     = 0x0056,
#endif
/* netgroup */

    SSS_NSS_SETNETGRENT    = 0x0061,
    SSS_NSS_GETNETGRENT    = 0x0062,
    SSS_NSS_ENDNETGRENT    = 0x0063,
    /* SSS_NSS_INNETGR     = 0x0064, */
#if 0
/* networks */

    SSS_NSS_GETNETBYNAME   = 0x0071,
    SSS_NSS_GETNETBYADDR   = 0x0072,
    SSS_NSS_SETNETENT      = 0x0073,
    SSS_NSS_GETNETENT      = 0x0074,
    SSS_NSS_ENDNETENT      = 0x0075,

/* protocols */

    SSS_NSS_GETPROTOBYNAME = 0x0081,
    SSS_NSS_GETPROTOBYNUM  = 0x0082,
    SSS_NSS_SETPROTOENT    = 0x0083,
    SSS_NSS_GETPROTOENT    = 0x0084,
    SSS_NSS_ENDPROTOENT    = 0x0085,

/* rpc */

    SSS_NSS_GETRPCBYNAME   = 0x0091,
    SSS_NSS_GETRPCBYNUM    = 0x0092,
    SSS_NSS_SETRPCENT      = 0x0093,
    SSS_NSS_GETRPCENT      = 0x0094,
    SSS_NSS_ENDRPCENT      = 0x0095,
#endif

/* services */

    SSS_NSS_GETSERVBYNAME  = 0x00A1,
    SSS_NSS_GETSERVBYPORT  = 0x00A2,
    SSS_NSS_SETSERVENT     = 0x00A3,
    SSS_NSS_GETSERVENT     = 0x00A4,
    SSS_NSS_ENDSERVENT     = 0x00A5,

#if 0
/* shadow */

    SSS_NSS_GETSPNAM       = 0x00B1,
    SSS_NSS_GETSPUID       = 0x00B2,
    SSS_NSS_SETSPENT       = 0x00B3,
    SSS_NSS_GETSPENT       = 0x00B4,
    SSS_NSS_ENDSPENT       = 0x00B5,
#endif

/* SUDO */
    SSS_SUDO_GET_SUDORULES = 0x00C1,
    SSS_SUDO_GET_DEFAULTS  = 0x00C2,

/* autofs */
    SSS_AUTOFS_SETAUTOMNTENT    = 0x00D1,
    SSS_AUTOFS_GETAUTOMNTENT    = 0x00D2,
    SSS_AUTOFS_GETAUTOMNTBYNAME  = 0x00D3,
    SSS_AUTOFS_ENDAUTOMNTENT    = 0x00D4,

/* SSH */
    SSS_SSH_GET_USER_PUBKEYS = 0x00E1,
    SSS_SSH_GET_HOST_PUBKEYS = 0x00E2,

/* PAM related calls */
    SSS_PAM_AUTHENTICATE     = 0x00F1, /**< see pam_sm_authenticate(3) for
                                        * details.
                                        *
                                        * Additionally we allow sssd to send
                                        * the return code PAM_NEW_AUTHTOK_REQD
                                        * during authentication if the
                                        * authentication was successful but
                                        * the authentication token is expired.
                                        * To meet the standards of libpam we
                                        * return PAM_SUCCESS for
                                        * authentication and set a flag so
                                        * that the account management module
                                        * can return PAM_NEW_AUTHTOK_REQD if
                                        * sssd return success for account
                                        * management. We do this to reduce the
                                        * communication with external servers,
                                        * because there are cases, e.g.
                                        * Kerberos authentication, where the
                                        * information that the password is
                                        * expired is already available during
                                        * authentication. */
    SSS_PAM_SETCRED          = 0x00F2, /**< see pam_sm_setcred(3) for
                                        * details */
    SSS_PAM_ACCT_MGMT        = 0x00F3, /**< see pam_sm_acct_mgmt(3) for
                                        * details */
    SSS_PAM_OPEN_SESSION     = 0x00F4, /**< see pam_sm_open_session(3) for
                                        * details */
    SSS_PAM_CLOSE_SESSION    = 0x00F5, /**< see pam_sm_close_session(3) for
                                        *details */
    SSS_PAM_CHAUTHTOK        = 0x00F6, /**< second run of the password change
                                        * operation where the PAM_UPDATE_AUTHTOK
                                        * flag is set and the real change may
                                        * happen, see pam_sm_chauthtok(3) for
                                        * details */
    SSS_PAM_CHAUTHTOK_PRELIM = 0x00F7, /**< first run of the password change
                                        * operation where the PAM_PRELIM_CHECK
                                        * flag is set, see pam_sm_chauthtok(3)
                                        * for details */
    SSS_CMD_RENEW            = 0x00F8, /**< Renew a credential with a limited
                                        * lifetime, e.g. a Kerberos Ticket
                                        * Granting Ticket (TGT) */

/* PAC responder calls */
    SSS_PAC_ADD_PAC_USER     = 0x0101,

};

/**
 * @}
 */ /* end of group sss_cli_command */


/**
 * @defgroup sss_pam SSSD and PAM
 *
 * SSSD offers authentication and authorization via PAM
 *
 * The SSSD provides a PAM client modules pam_sss which can be called from the
 * PAM stack of the operation system. pam_sss will collect all the data about
 * the user from the PAM stack and sends them via a socket to the PAM
 * responder of the SSSD. The PAM responder selects the appropriate backend
 * and forwards the data via DBUS to the backend. The backend preforms the
 * requested operation and sends the result expressed by a PAM return value
 * and optional additional information back to the PAM responder. Finally the
 * PAM responder forwards the response back to the client.
 *
 * @{
 */

/**
 * @}
 */ /* end of group sss_pam */

/**
 * @defgroup sss_authtok_type Authentication Tokens
 * @ingroup sss_pam
 *
 * To indicate to the components of the SSSD how to handle the authentication
 * token the client sends the type of the authentication token to the SSSD.
 *
 * @{
 */

/** The different types of authentication tokens */

enum sss_authtok_type {
    SSS_AUTHTOK_TYPE_EMPTY    =  0x0000, /**< No authentication token
                                          * available */
    SSS_AUTHTOK_TYPE_PASSWORD =  0x0001, /**< Authentication token is a
                                          * password, it may or may no contain
                                          * a trailing \\0 */
    SSS_AUTHTOK_TYPE_CCFILE =    0x0002, /**< Authentication token is a path to
                                          * a Kerberos credential cache file,
                                          * it may or may no contain
                                          * a trailing \\0 */
};

/**
 * @}
 */ /* end of group sss_authtok_type */

#define SSS_START_OF_PAM_REQUEST 0x4d415049
#define SSS_END_OF_PAM_REQUEST 0x4950414d

enum pam_item_type {
    SSS_PAM_ITEM_EMPTY = 0x0000,
    SSS_PAM_ITEM_USER,
    SSS_PAM_ITEM_SERVICE,
    SSS_PAM_ITEM_TTY,
    SSS_PAM_ITEM_RUSER,
    SSS_PAM_ITEM_RHOST,
    SSS_PAM_ITEM_AUTHTOK,
    SSS_PAM_ITEM_NEWAUTHTOK,
    SSS_PAM_ITEM_CLI_LOCALE,
    SSS_PAM_ITEM_CLI_PID,
};

#define SSS_NSS_MAX_ENTRIES 256
#define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4)
struct sss_cli_req_data {
    size_t len;
    const void *data;
};

/* this is in milliseconds, wait up to 300 seconds */
#define SSS_CLI_SOCKET_TIMEOUT 300000

enum sss_status {
    SSS_STATUS_TRYAGAIN,
    SSS_STATUS_UNAVAIL,
    SSS_STATUS_SUCCESS
};

/**
 * @defgroup sss_pam_cli Responses to the PAM client
 * @ingroup sss_pam
 * @{
 */

/**
 * @defgroup response_type Messages from the server
 * @ingroup sss_pam_cli
 *
 * SSSD can send different kind of information back to the client.
 * A response from the SSSD can contain 0 or more messages. Each message
 * contains a type tag and the size of the message data, both are unsigned
 * 32-bit integer values, followed be the message specific data.
 *
 * If the message is generated by a backend it is send back to the PAM
 * responder via a D-BUS message in an array of D-BUS structs. The struct
 * consists of a DBUS_TYPE_UINT32 for the tag and a DBUS_TYPE_ARRAY to hold
 * the message.
 *
 * Examples:
 *  - #SSS_PAM_ENV_ITEM,
 *    <pre>
 *    ------------------------------------
 *    | uint32_t | uint32_t | uint8_t[4] |
 *    | 0x03     | 0x04     | a=b\\0      |
 *    ------------------------------------
 *    </pre>
 * @{
 */

/** Types of different messages */

enum response_type {
    SSS_PAM_SYSTEM_INFO = 0x01, /**< Message for the system log.
                                 * @param String, zero terminated. */
    SSS_PAM_DOMAIN_NAME, /**< Name of the domain the user belongs too.
                          * This messages is generated by the PAM responder.
                          * @param String, zero terminated, with the domain
                          * name. */
    SSS_PAM_ENV_ITEM,    /**< Set and environment variable with pam_putenv(3).
                          * @param String, zero terminated, of the form
                          * name=value. See pam_putenv(3) for details. */
    SSS_ENV_ITEM,        /**< Set and environment variable with putenv(3).
                          * @param String, zero terminated, of the form
                          * name=value. See putenv(3) for details. */
    SSS_ALL_ENV_ITEM,    /**< Set and environment variable with putenv(3) and
                          * pam_putenv(3).
                          * @param String, zero terminated, of the form
                          * name=value. See putenv(3) and pam_putenv(3) for
                          * details. */
    SSS_PAM_USER_INFO,   /**< A message which should be displayed to the user.
                          * @param User info message, see #user_info_type
                          * for details. */
    SSS_PAM_TEXT_MSG,    /**< A plain text message which should be displayed to
                          * the user.This should only be used in the case where
                          * it is not possile to use SSS_PAM_USER_INFO.
                          * @param A zero terminated string. */
};

/**
 * @defgroup user_info_type User info messages
 * @ingroup response_type
 *
 * To achieve a consistent user experience and to facilitate
 * internationalization all messages show to the user are generate by the PAM
 * client and not by the SSSD server components. To indicate what message the
 * client should display to the user SSSD can send a #SSS_PAM_USER_INFO message
 * where the data part contains one of the following tags as an unsigned
 * 32-bit integer value and optional data.
 *
 * Examples:
 *  - #SSS_PAM_USER_INFO_OFFLINE_CHPASS
 *    <pre>
 *    ----------------------------------
 *    | uint32_t | uint32_t | uint32_t |
 *    | 0x06     | 0x01     | 0x03     |
 *    ----------------------------------
 *    </pre>
 *  - #SSS_PAM_USER_INFO_CHPASS_ERROR
 *    <pre>
 *    ----------------------------------------------------------
 *    | uint32_t | uint32_t | uint32_t | uint32_t | uint8_t[3] |
 *    | 0x06     | 0x05     | 0x04     | 0x03     | abc        |
 *    ----------------------------------------------------------
 *    </pre>
 * @{
 */

/** Different types of user messages */

enum user_info_type {
    SSS_PAM_USER_INFO_OFFLINE_AUTH = 0x01, /**< Inform the user that the
                                            * authentication happened offline.
                                            * This message is generated by the
                                            * PAM responder.
                                            * @param Time when the cached
                                            * password will expire in seconds
                                            * since the UNIX Epoch as returned
                                            * by time(2) as int64_t. A value
                                            * of zero indicates that the
                                            * cached password will never
                                            * expire. */
    SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED, /**< Tell the user how low a new
                                             * authentication is delayed. This
                                             * message is generated by the PAM
                                             * responder.
                                             * @param Time when an
                                             * authentication is allowed again
                                             * in seconds since the UNIX Epoch
                                             * as returned by time(2) as
                                             * int64_t. */
    SSS_PAM_USER_INFO_OFFLINE_CHPASS, /**< * Tell the user that it is not
                                       * possible to change the password while
                                       * the system is offline. This message
                                       * is generated by the PAM responder. */
    SSS_PAM_USER_INFO_CHPASS_ERROR, /**< Tell the user that a password change
                                     * failed and optionally give a reason.
                                     * @param Size of the message as unsigned
                                     * 32-bit integer value. A value of 0
                                     * indicates that no message is following.
                                     * @param String with the specified
                                     * length. */
    SSS_PAM_USER_INFO_GRACE_LOGIN, /**< Warn the user that the password is
                                    * expired and inform about the remaining
                                    * number of grace logins.
                                    * @param The number of remaining grace
                                    * logins as uint32_t */
    SSS_PAM_USER_INFO_EXPIRE_WARN /**< Warn the user that the password will
                                   * expire soon.
                                   * @param Number of seconds before the user's
                                   * password will expire. */
};
/**
 * @}
 */ /* end of group user_info_type */

/**
 * @}
 */ /* end of group response_type */

/**
 * @}
 */ /* end of group sss_pam_cli */

enum sss_netgr_rep_type {
    SSS_NETGR_REP_TRIPLE = 1,
    SSS_NETGR_REP_GROUP
};

enum sss_cli_error_codes {
    ESSS_SSS_CLI_ERROR_START = 0x1000,
    ESSS_BAD_PRIV_SOCKET,
    ESSS_BAD_PUB_SOCKET,
    ESSS_BAD_CRED_MSG,
    ESSS_SERVER_NOT_TRUSTED,

    ESS_SSS_CLI_ERROR_MAX
};

const char *ssscli_err2string(int err);

enum nss_status sss_nss_make_request(enum sss_cli_command cmd,
                                     struct sss_cli_req_data *rd,
                                     uint8_t **repbuf, size_t *replen,
                                     int *errnop);

int sss_pam_make_request(enum sss_cli_command cmd,
                                     struct sss_cli_req_data *rd,
                                     uint8_t **repbuf, size_t *replen,
                                     int *errnop);
int sss_pac_make_request(enum sss_cli_command cmd,
                         struct sss_cli_req_data *rd,
                         uint8_t **repbuf, size_t *replen,
                         int *errnop);

int sss_sudo_make_request(enum sss_cli_command cmd,
                          struct sss_cli_req_data *rd,
                          uint8_t **repbuf, size_t *replen,
                          int *errnop);

int sss_autofs_make_request(enum sss_cli_command cmd,
                            struct sss_cli_req_data *rd,
                            uint8_t **repbuf, size_t *replen,
                            int *errnop);

int sss_ssh_make_request(enum sss_cli_command cmd,
                         struct sss_cli_req_data *rd,
                         uint8_t **repbuf, size_t *replen,
                         int *errnop);

#ifndef SAFEALIGN_COPY_UINT32
static inline void
safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter)
{
    memcpy(dest, src, n);
    if (counter) {
        *counter += n;
    }
}

#define SAFEALIGN_SET_VALUE(dest, value, type, pctr) do { \
    type CV_MACRO_val = (type)(value); \
    safealign_memcpy(dest, &CV_MACRO_val, sizeof(type), pctr); \
} while(0)

#ifndef SAFEALIGN_SET_UINT32
#define SAFEALIGN_SET_UINT32(dest, value, pctr) \
    SAFEALIGN_SET_VALUE(dest, value, uint32_t, pctr)
#endif

#define SAFEALIGN_COPY_UINT32(dest, src, pctr) \
    safealign_memcpy(dest, src, sizeof(uint32_t), pctr)
#endif

#ifndef SAFEALIGN_SET_UINT16
#define SAFEALIGN_SET_UINT16(dest, value, pctr) \
    SAFEALIGN_SET_VALUE(dest, value, uint16_t, pctr)
#endif

#ifndef SAFEALIGN_COPY_UINT16
#define SAFEALIGN_COPY_UINT16(dest, src, pctr) \
    safealign_memcpy(dest, src, sizeof(uint16_t), pctr)
#endif

#if 0

/* GETSPNAM Request:
 *
 * 0-X: string with name
 *
 * Replies:
 *
 * 0-3: 32bit unsigned number of results
 * 4-7: 32bit unsigned (reserved/padding)
 * For each result:
 *  0-7: 64bit unsigned with Date of last change
 *  8-15: 64bit unsigned with Min #days between changes
 *  16-23: 64bit unsigned with Max #days between changes
 *  24-31: 64bit unsigned with #days before pwd expires
 *  32-39: 64bit unsigned with #days after pwd expires until account is disabled
 *  40-47: 64bit unsigned with expiration date in days since 1970-01-01
 *  48-55: 64bit unsigned (flags/reserved)
 *  56-X: sequence of 2, 0 terminated, strings (name, pwd) 64bit padded
 */
#endif

/* Return strlen(str) or maxlen, whichever is shorter
 * Returns EINVAL if str is NULL, EFBIG if str is longer than maxlen
 * _len will return the result
 */
errno_t sss_strnlen(const char *str, size_t maxlen, size_t *len);

void sss_nss_lock(void);
void sss_nss_unlock(void);
void sss_pam_lock(void);
void sss_pam_unlock(void);

errno_t sss_readrep_copy_string(const char *in,
                                size_t *offset,
                                size_t *slen,
                                size_t *dlen,
                                char **out,
                                size_t *size);

#endif /* _SSSCLI_H */