RCS_ID_C="$Id: credential.c,v 4.2 1994/05/18 15:43:02 ppessi Exp $";
/*
 * credential.c --- handle credentials
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * This file is part of the AmiTCP/IP User Library.
 *
 * Copyright © 1993 AmiTCP/IP Group, <AmiTCP-Group@hut.fi>
 *                  Helsinki University of Technology, Finland.
 *
 * Created      : Wed Sep 15 01:25:26 1993 ppessi
 * Last modified: Wed May 18 18:42:43 1994 ppessi
 */

#include "base.h"
#include "credential.h"
#include "libfunc.h"

#include <exec/memory.h>
#include <exec/alerts.h>
#include <string.h>
#include <assert.h>

struct CredentialResource *CredentialBase;

#ifdef notyet
typedef struct CredentialResource *(*resident_init_fp)(void);

/*
 * Create credential resource
 */
struct CredentialResource *CredentialInit(const char *name)
{
  BPTR resseg = LoadSeg(_PATH_CREDENTIAL);

  if (resseg) {
    resident_init_fp initf = (resident_init_fp)((LONG *)(resseg << 2) + 1);
    struct CredentialResource *res = initf();

    if (res)
      return res;

    UnLoadSeg(resseg);
  }

  return NULL;
}
#endif

/****** usergroup.library/getspgrp ********************************************

    NAME
        getpgrp - get process group identification

    SYNOPSIS
        #include <libraries/usergroup.h>

        sessionid = getpgrp()
            D0
        pid_t getpgrp(void);

    FUNCTION
        The getpgrp() function returns the process group id for the current
        process.  Currently, the process group ID is the same as the session
        ID. The 0 is valid process group ID for console session.

    RESULTS
        Upon successful completion, the value of the process group ID is
        returned.  Otherwise, a value of -1 is returned and an error code is
        stored to global errno location.

    ERRORS
        [ESRCH]   The calling process don't belong to any process group.

    SEE ALSO
         setsid(),  exec.library/FindTask()

******************************************************************************
*/

SAVEDS ASM pid_t R_getpgrp(void)
{
  struct proc *p = procfind(NULL);

  return p->p_session->s_leader;
}

/****** usergroup.library/setsid *********************************************

    NAME
        setsid - create a new session

    SYNOPSIS
        #include <unistd.h>

        sessionid = setsid()
            D0
        pid_t setsid(void);

    FUNCTION
        The setsid() function creates a new session when the calling
        process is not a process group leader.  The calling process
        then becomes the session leader of this session and the only
        process in the new session.

    RESULTS
        Upon successful completion, the value of the new session ID is
        returned.  Otherwise, a value of -1 is returned and an error
    	code is  stored to global errno location.

    ERRORS
        [EPERM]   The calling process is already a session leader.

    SEE ALSO
         getpgrp()

******************************************************************************
*/

SAVEDS ASM pid_t R_setsid(void)
{
#ifdef notyet
  pid_t pid = (pid_t)FindTask(NULL);
  struct proc *p = procfind(pid);
  int error;

  if (p->p_session->s_leader == pid) {
    error = EPERM;
  } else {
    struct session *session;

    while (!(session = AllocMem(sizeof(*session), MEMF_CLEAR|MEMF_PUBLIC))) {
      Alert(AG_NoMemory | UG_CredRes);
      Wait(0L);
    }
    InitList((struct List *)session->s_pgrpl);
    Forbid();
    p = proccopy(p, pid);
    p->p_ucred->cr_ref++;
    p->p_ucred = crcopy(p->p_ucred);

    /* Remove from old process group */
    rempgrp(p);
    p->p_session = session;
    MinAddHead(session->s_pgrpl, p->p_pgrp);
    session->s_consoletask = GetConsoleTask();
    session->s_leader = pid;
    Permit();
    return pid;
  }

  SetErrno(error);
  return -1;
#endif
  return 0;
}

#ifdef notyet
/*
 * Remove a process from session's process group and
 * free session if last process is removed
 */
void
rempgrp(struct proc *p)
{
  Forbid();
  /* Free the session structure if the last task in process group dies */
  MinRemove((struct Node *)p->p_pgrp);
  if (IsMinListEmpty(p->p_session->s_pgrpl)) {
    FreeMem(p->p_session, sizeof(*p->p_session));
  } else if (p->p_session->s_leader == (pid_t)p->p_task) {
  }
  Permit();
}
#endif

/*
 * Check super-user
 */
int
suser(struct ucred *cred)
{
  if (cred->cr_uid == 0)
    return 0;
  return EPERM;
}

/*
 * Search credentials
 */
static struct pcred *crfind(pid_t pid)
{
  return procfind(pid)->p_cred;
}

/****** usergroup.library/getuid *********************************************

    NAME
        getuid, geteuid - get user process identification
        getgid, getegid - get group process identification

    SYNOPSIS
        ruid = getuid()
        D0

        uid_t getuid(void);

        euid = geteuid()
        D0

        uid_t geteuid(void);

        rgid = getgid()
        D0

        gid_t getgid(void);

        egid = getegid()
        D0

        gid_t getegid(void);

    FUNCTION
        The getuid() function returns the real user ID of the calling
        process, geteuid() returns the effective user ID of the calling
        process.

        The getgid() function returns the real group ID of the calling
        process, getegid() returns the effective group ID of the calling
        process.

        The real user ID and real group ID is specified at login time.

        The real ID is the ID of the user who invoked the program.  As the
        effective user and gourp ID gives the process additional permissions
        during the execution of `set-user-ID' or `set-group-ID' mode
        programs, functions getgid() and getuid () are used to determine the
        real-ids of the calling process.

    RESULT
        The getuid(), geteuid(), getgid(), and getegid() functions are
        always successful, and no return value is reserved to indicate an
        error.

    NOTES
        Any task can call these functions

    SEE ALSO
        getgroups(), setuid(), setreuid(), setgid(), setregid(), setgroups()

******************************************************************************
*/

SAVEDS ASM uid_t R_getuid(void)
{
  return crfind(NULL)->p_ruid;
}

SAVEDS ASM uid_t R_geteuid(void)
{
  return crfind(NULL)->p_euid;
}

SAVEDS ASM gid_t R_getgid(void)
{
  return crfind(NULL)->p_rgid;
}

SAVEDS ASM gid_t R_getegid(void)
{
  return crfind(NULL)->p_egid;
}

/****** usergroup.library/getgroups *******************************************

    NAME
        getgroups - get group access list

    SYNOPSIS
        len = getgroups(gidsetlen, gidset[])
        D0              D0         A0

        int getgroups(int, gid_t *);

    FUNCTION
        Getgroups() gets the current group access list of the user process
        and stores it in the array gidset. The parameter gidsetlen indicates
        the number of entries that may be placed in gidset. The function
        getgroups() returns the actual number of groups returned in gidset.
        No more than NGROUPS, as defined in <libraries/usergroup.h>, will
        ever be returned.

     RESULT
        A successful call returns the number of groups in the group set.  A
        value of -1 indicates that the argument gidsetlen is smaller than
        the number of groups in the group set.

    ERRORS
        [EINVAL] The argument gidsetlen is smaller than the number of
                 groups in the group set.
        [EFAULT] The argument gidset specifies an invalid address.

    SEE ALSO
        setgroups(), initgroups(), getgid(), getegid()

    HISTORY
        The getgroups function call appeared in 4.2BSD.

****************************************************************************
*/

SAVEDS ASM int R_getgroups(REG(d0) int ngroups, REG(a1) gid_t *groups)
{
  struct pcred *pc = crfind(NULL);
  gid_t *getgroups;
  short error;

  if (ngroups == 0) {
    return pc->p_ngroups;
  } else if (ngroups < pc->p_ngroups) {
    error = EINVAL;
  } else if (groups == NULL || ((LONG)groups & 1) != 0) {
    error = EFAULT;
  } else {
    /* This does also UWORD -> gid_t conversion */
    getgroups = pc->p_groups;
    ngroups = pc->p_ngroups;
    while (ngroups-- > 0) {
      *groups++ = *getgroups++;
    }
    return pc->p_ngroups;
  }

  SetErrno(error);
  return -1;
}

/****** usergroup.library/setuid ***********************************************

    NAME
        setuid, setreuid - set real and effective user ID's
        setgid, setregid - set real and effective group ID's

    SYNOPSIS
        success = setuid(uid)
        D0               D0

        int setuid(uid_t);

        success = setreuid(ruid, euid);
        D0                 D0    D1

        int setreuid(uid_t, uid_t);

        success = setgid(gid)
        D0               D0

        int setgid(gid_t);

        success = setregid(ruid, euid)
        D0                 D0    D1

        int setregid(gid_t ruid, gid_t euid);

    FUNCTION
        The real and effective ID's of the current process are set according
        to the arguments.  If ruid or euid is -1, the current uid is filled
        in by the system.  Unprivileged users may change the real ID to the
        effective ID and vice-versa; only the super-user may make other
        changes.

    RETURN VALUES
        Upon successful completion, a value of 0 is returned. Otherwise, a
        value of -1 is returned and errno is set to indicate the error.

    ERRORS
        [EPERM]  The current process is not the super-user and a change
                 other than changing the effective id to the real id was
                 specified.

    SEE ALSO
        getuid(), getgid(), geteuid(), getegid()

    NOTES
        Any task can call these functions.

    HISTORY
        A setuid() and setgid() function calls appeared in Version 6 AT&T
        UNIX.  The setreuid() and setregid() function calls appeared in
        4.2BSD.

****************************************************************************
*/

SAVEDS ASM int R_setuid(REG(d0) uid_t uid)
{
  return R_setreuid(uid, uid);
}

SAVEDS ASM int R_setreuid(REG(d0) uid_t ruid, REG(d1) uid_t euid)
{
  struct pcred *pc = crfind(NULL);
  short error;

  if (ruid == NOID)
    ruid = pc->p_ruid;
  if (euid == NOID)
    euid = pc->pc_ucred->cr_uid;

  /*
   * Allow setting real uid to previous effective, for swapping real and
   * effective.  This should be:
   *
   * if (ruid != pc->p_ruid &&
   *     (error = suser(pc->pc_ucred, &p->p_acflag)))
   */
#if 1
  /* allow setreuid(-1, 0) to emulate SUID */
  if ((ruid != pc->p_ruid && ruid != pc->pc_ucred->cr_uid /* XXX */ &&
       (error = suser(pc->pc_ucred))) ||
      (/* euid != pc->pc_ucred->cr_uid */ 0 && euid != pc->p_ruid &&
       (error = suser(pc->pc_ucred))))
#else
  if ((ruid != pc->p_ruid && ruid != pc->pc_ucred->cr_uid /* XXX */ &&
       (error = suser(pc->pc_ucred))) ||
      (euid != pc->pc_ucred->cr_uid && euid != pc->p_ruid &&
       (error = suser(pc->pc_ucred))))
#endif
    {
      unlock(&credential_list);
      SetErrno(error);
      return -1;
    } else {
    /*
     * Everything's okay, do it.  Copy credentials so other references do
     * not see our changes.
     */
    pc->pc_ucred = crcopy(pc->pc_ucred);
    pc->pc_ucred->cr_uid = euid;
    pc->p_ruid = ruid;

    return 0;
  }
}

SAVEDS ASM int R_setgid(REG(d0) gid_t gid)
{
  return R_setregid(gid, gid);
}

SAVEDS ASM int R_setregid(REG(d0) gid_t rgid, REG(d1) gid_t egid)
{
  struct pcred *pc = crfind(NULL);
  short error;

  if (rgid == NOID)
    rgid = pc->p_rgid;
  if (egid == NOID)
    egid = pc->pc_ucred->cr_gid;

  /*
   * Allow setting real gid to previous effective, for swapping real and
   * effective.  This should be:
   *
   * if (rgid != pc->p_rgid &&
   *     (error = suser(pc->pc_ucred, &p->p_acflag)))
   */
  if ((rgid != pc->p_rgid && rgid != pc->pc_ucred->cr_gid /* XXX */ &&
       (error = suser(pc->pc_ucred))) ||
      (egid != pc->pc_ucred->cr_gid && egid != pc->p_rgid &&
       (error = suser(pc->pc_ucred)))) {
    SetErrno(error);
    return -1;
  } else {
    /*
     * Everything's okay, do it.  Copy credentials so other references do
     * not see our changes.
     */
    pc->pc_ucred = crcopy(pc->pc_ucred);
    pc->pc_ucred->cr_gid = egid;
    pc->p_rgid = rgid;

    return 0;
  }
}

/****** usergroup.library/setgroups ******************************************

    NAME
        setgroups - set group access list

    SYNOPSIS
        success = setgroups(ngroups, gidset)
        D0                  D0       A0

        int setgroups(int, const gid_t *);

    FUNCTION
        Setgroups() sets the group access list of the current user process
        according to the array gidset. The parameter ngroups indicates the
        number of entries in the array and must be no more than NGROUPS, as
        defined in <libraries/usergroup.h>.

        Only the super-user may set new groups. The super-user can not
        set illegal groups (-1).

    RESULT
        A 0 value is returned on success, -1 on error, with an error
        code stored in errno and available with ug_GetErr() function.

    ERRORS
        [EINVAL]    An illegal group id was specified.
        [EPERM]     The caller has got no necessary privileges.
        [EFAULT]    The address specified for gidset is illegal.

    NOTES
        Any task can call this function.

    SEE ALSO
        getgroups(), initgroups()

****************************************************************************
*/

SAVEDS ASM int R_setgroups(REG(d0) int ngrp,
			   REG(a1) const gid_t *groups)
{
  register struct pcred *pc = crfind(NULL);
  register gid_t *gp;
  register const gid_t *lp;
  short error;

  if (error = suser(pc->pc_ucred /*, &p->p_acflag */)) {

  } else if (ngrp > NGROUPS) {
    error = EINVAL;
  } else if (groups == NULL || ((LONG)groups & 1) != 0) {
    error = EFAULT;
  } else {
    pc->pc_ucred = crcopy(pc->pc_ucred);
    pc->pc_ucred->cr_ngroups = ngrp;
    for (gp = pc->pc_ucred->cr_groups, lp = groups; ngrp--; )
      *gp++ = *lp++;
    return 0;
  }

  SetErrno(error);
  return -1;
}


/****** usergroup.library/MU2UG **********************************************

    NAME
        MU2ID - macro converting MultiUser id to usergroup id
        ID2MU - macro converting usergroup id to MultiUser id

    SYNOPSIS
        ug_id = MU2ID(mu_id)

        mu_id = ID2MU(ug_id)

    FUNCTION
        These macros are used to convert between different user ID
        formats.  The mu_id is in the format used by MultiUser filesystem
        and multiuser.library.  The ug_id is the format used by Unix and
        usergroup.library.

        Most of the id values are identical in usergroup.library and
        multiuser.library. However, these two exceptions have values as
        follows:

                    usergroup.library  multiuser.library
        super-user          0              65535
        nobody             -2                  0

    INPUTS AND RESULTS
        mu_id    - user ID in MultiUser format.
        ug_id    - user ID in usergroup format.

    BUGS
        The usergroup id values that won't fit into UWORD are truncated.

    SEE ALSO
******************************************************************************
*/

#if 0
ASM uid_t R_ug_mu2id(REG(d0) UWORD id)
{
  if (id == 0) {
    return -2;
  } else if (id == 65535) {
    return 0;
  } else {
    return id;
  }
}

ASM UWORD R_ug_id2mu(REG(d0) uid_t id)
{
  if (id == 0) {
    return 65535;
  } else if (id == -2) {
    return 0;
  } else {
    return id;
  }
}
#endif

/****** usergroup.library/umask *********************************************

    NAME
        umask - set file creation mode mask

    SYNOPSIS
        #include <sys/stat.h>

        oldmask = umask(newmask)
	D0              D0

        mode_t umask(mode_t);

    FUNCTION
        The umask() routine sets the process's file mode creation mask to
        numask and returns the previous value of the mask.  The 9 low-order
        access permission bits of numask are used by Unix-compatible
        filesystems, for examble by NFS, to turn off corresponding bits
        requested in file mode.  This clearing allows each user to restrict
        the default access to his files.

        The default mask value is 022 (write access for owner only).  Child
        processes should inherit the mask of the calling process.

    RESULT
        The previous value of the file mode mask is returned by the call.

    ERRORS
        The umask() function is always successful.

    SEE ALSO
        getumask()
******************************************************************************
*/

SAVEDS ASM mode_t R_umask(REG(d0) mode_t newmask)
{
  struct proc *p = procfind(NULL);

  mode_t oldmask = p->p_umask;

  p->p_umask = newmask;

  return oldmask ;
}

/****** usergroup.library/getumask *********************************************

    NAME
        getumask - get file creation mode mask

    SYNOPSIS
        #include <sys/stat.h>

        oldmask = getumask()
	D0

        mode_t getumask(void);

    FUNCTION
        The getumask() routine sets the process's file mode creation mask to
        numask and returns the previous value of the mask.  The 9 low-order
        access permission bits of numask are used by Unix-compatible
        filesystems, for examble by NFS, to turn off corresponding bits
        requested in file mode.

    RESULT
        The value of the file mode mask is returned by the call.

    ERRORS
        The getumask() function is always successful.

    SEE ALSO
        umask()
******************************************************************************
*/

SAVEDS ASM mode_t R_getumask(void)
{
  struct proc *p = procfind(NULL);

  return p->p_umask;
}


/****** usergroup.library/getcredentials *************************************

    NAME
        getcredentials -- get credentials of given task. (V4)

    SYNOPSIS
        #include <libraries/usergroup.h>

        credentials = getcredentials(task)
	D0                              A0

        struct UserGroupCredentials *getcredentials(struct Task *);

    FUNCTION
	The function getcredentials() returns all credentials of the given
        task.  The credentials include real and effective user and group IDs,
        umask, login name and session ID.  If the task pointer is NULL, the
        credentials of current task are returned.

    RESULT
        A getcredentials() function returns a valid pointer to structure
        UserGroupCredentials on success and a null pointer if an error
        occurs.

    ERRORS
        [EINVAL]    An illegal task pointer was specified.

    BUGS
        This function leave its result in an internal static object and
        return a pointer to that object. Subsequent calls to this function
        will modify the same object.

    SEE ALSO

******************************************************************************
*/

static struct UserGroupCredentials creds[1];

SAVEDS ASM struct UserGroupCredentials
  *R_getcredentials(REG(a0) struct Task *task)
{
  register struct proc *p = procfind(task);

  if (p != NULL) {
    register struct UserGroupCredentials *c = creds;

    c->cr_ruid  = p->p_cred->p_ruid;
    c->cr_rgid  = p->p_cred->p_rgid;
    c->cr_umask = p->p_umask;
    c->cr_euid  = p->p_cred->p_euid;
    c->cr_ngroups = p->p_cred->p_ngroups;
    c->cr_session = p->p_session->s_leader;
    memcpy(c->cr_groups, p->p_cred->p_groups, sizeof(c->cr_groups));
    memcpy(c->cr_login, p->p_session->s_login, sizeof(c->cr_login));

    return c;
  }

  return NULL;
}
