/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   Copyright (C) Andrew Tridgell 1994-1997

   This file is part of the port to OpenVMS
   Routines to set user environment
   Copyright (C) Eckart Meyer 1996-1997
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 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 General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*	setuid.c
 *	V1.1			3-Feb-1997		IfN/Mey
 *
 *	Copyright E. Meyer <meyer@ifn.ing.tu-bs.de>
 *+
 * Setting the UID in VMS involves:
 *
 *	- setting process and job UIC to that value
 *	- setting process username to the corresponding user
 *	- setting user's privileges
 *	- setting user's rights identifiers
 *-
 */
#define EXTERN extern
#include "priv.h"

static struct uai *current_uai = NULL;	/* changed only by setuid() */

static void grant_identifiers(struct id *id_list)
{
	unsigned long sts;

	if (id_list == NULL) return;
	while(id_list->uic != -1) {
		DEBUG(6,("  grant identifier %08X\n",id_list->uic));
		sts = sys$grantid(0,0,id_list,0,0);
		id_list++;
	}
}

static void revoke_identifiers(struct id *id_list)
{
	unsigned long sts;

	if (id_list == NULL) return;
	while(id_list->uic != -1) {
		DEBUG(6,("  revoke identifier %08X\n",id_list->uic));
		sts = sys$revokid(0,0,id_list,0,0);
		id_list++;
	}
}



int vms_setuid(uid_t uid)
{
  int st;
  struct {
     int size;
     char *ptr;
  } userdesc;
  long sys_set_username(void *);
  long sys_set_uic(unsigned int *);
  struct passwd *pw;
  struct uai *puai;
  unsigned long tmpprv[2];
  uid_t suid;
/*
 * Check if current use is already known (from a previous setuid).
 * If not, obtain current username and setup things.
 */
  if (current_uai == NULL) {
	vms_getlogin();
	if (setup_pw() != SS$_NORMAL) return(-1);
	current_uai = login_uai;
  }
/*
 * Find UID in table. If not found, we become a new user.
 * UICs [0,0], [1,0] and [1,4] are considered to be SYSTEM.
 */
  if (uid == 0x00000000L || uid == 0x00010000L || uid == 0x00010004L) {
	strcpy(login_name,"SYSTEM");
  	if (setup_pw() != SS$_NORMAL) return(-1);
	password_struct.pw_uid = uid;
	password_struct.pw_gid = uid >> 16;
  }
  else {
	puai = find_uai_by_uic(uid);
	if (puai == NULL) return(-1);
	strcpy(login_name,puai->name);
  	if (setup_pw() != SS$_NORMAL) return(-1);
  }

DEBUG (2,("vms_setuid: uid = %08X, username = \"%s\", uic = %08X\n",
uid,login_name,password_struct.pw_uid));
/*
 * Set new use user name, UIC and privileges.
 *
 *	- clear all privs and set CMKRNL for setting username/uic.
 */
  tmpprv[0] = 0xFFFFFFFF;
  tmpprv[1] = 0xFFFFFFFF;
#if 0 /* since we enable *all* privileges, we don't needd to clear them before */
  st = sys$setprv(0,tmpprv,0,0);	/* clear all privileges */
  if (st != SS$_NORMAL) return(-1);

  tmpprv[0] = PRV$M_CMKRNL;
  tmpprv[1] = 0;
#endif
  st = sys$setprv(1,tmpprv,0,0);	/* enable to set USERNAME/UIC */
  if (st != SS$_NORMAL) return(-1);
/*
 *	- revoke current user's identifiers and setup new
 */
  revoke_identifiers(current_uai->id_list);
  grant_identifiers(login_uai->id_list);
  current_uai = login_uai;
/*
 *	- set username/uic - this is kernel mode hacking!
 */
  userdesc.size = strlen(login_name);
  userdesc.ptr  = login_name;
  st = sys_set_username(&userdesc);
  if (st != SS$_NORMAL) return(-1);
  suid = password_struct.pw_uid;
  if (suid == 0x00000000L || suid == 0x00010000L || suid == 0x00010004L) {
  	suid = 0x00010004L;
  }
  st = sys_set_uic(&suid); /* do this after $GRANTID */
  if (st != SS$_NORMAL) return(-1);
/*
 *	- clear privileges required for hacking...
 */
  st = sys$setprv(0,tmpprv,0,0);	/* reset those privs */
  if (st != SS$_NORMAL) return(-1);
/*
 *	- finally set new users privs
 */
DEBUG (3,("vms_setuid: privs = %08X %08X\n",login_priv[0], login_priv[1]));
  st = sys$setprv(1,login_priv,0,0);	/* set new user's privs */
  if (st != SS$_NORMAL) return(-1);

sys$setprv(0,0,0,tmpprv);
DEBUG (8,("            privs = %08X %08X\n",tmpprv[0], tmpprv[1]));
  return(0);
}
