/* su.c */

/*-------------------------------------------------------------
** Program to do the equivilant of the *IX su command (Substitute User).
** If 1 argument is given (the userid), an attempt will be made to create
** a new shell under the new userid's uid. If the argument is the userid
** "root" (the superuser), or if there are no arguments given (in which case
** it defaults to "root"), the su program will check to see if the current
** user is a member of the group root (either primary or secondary).
**		If the user is not in the group root, the program will return
** unsuccessfully.
**    Next the password of the new user is requested, if the password matches
** the password of the supplied userid, then a new shell is spawned with the
** new user's uid, and the su program returns, and so does not wait for the
** other shell to return. Failed/Successfull attempts at su may/may not be
** logged in the multiuser.log file. This is handled my the multiuser.server,
** so it depends on your config file.
**	© 1995 Chris Mc Carthy.
**
**	email: 	cmccarth@icl.rtc-cork.ie		(Try this first, though sometimes
**														 our /var partition fills up with
**														 coredumps from the mailer :-(  )
**				cmccarth@dullahan.rtc-cork.ie (mailer not configured yet, new system)
**				atoz@nether.net					(Safest, but may take a while to reply)
**				atoz@mayhem.inferno.net
**
**	smail:	Chris Mc Carthy
**				Castlepark
**				Kinsale
**				Co. Cork
**				Ireland.
**
**	IRC:		AtoZ on #hack, #amiga, #wasteland (Mainly on the Undernet).
**
**
**--------------------------------------------------------------
*/



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libraries/multiuser.h>
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/multiuser.h>
#include <utility/tagitem.h>

/* Minimum library versions */
#define DOSVER			36
#define MULTIUSERVER	39

#define NEWSHELL		"NewShell"
#define ROOTUSERID	"root"		/* UserID of the super-user */

void cleanUp(char *str, int rc);			/* Shutdown libs etc, and exit */
int main(int argc, char *argv[]);

struct muBase *muBase;
struct DosBase *DosBase;

struct muExtOwner *extOwner;		/* Info about user attempting to su */

main(int argc, char *argv[])
{
	STRPTR suUserID;					/* Try to su to this user-id */
	BOOL inRootGroup = FALSE;		/* primary or secondary group = root group */
	int i;

	/* Check command line arguments */

	if (argc > 2)				/* Invalid arg count */
		cleanUp("Usage: su <user-id>", ERROR_TOO_MANY_ARGS);
	else if (argc == 2)		/* su to a non-superuser */
		suUserID = argv[1];
	else							/* su to super-user (root) */
		suUserID = ROOTUSERID;



	/* Open libraries */

	if (!(muBase = (struct muBase *) OpenLibrary (MULTIUSERNAME, MULTIUSERVER)))
		cleanUp("Cant open multiuser.library", ERROR_INVALID_RESIDENT_LIBRARY);

	if (!(DosBase = (struct DosBase *) OpenLibrary (DOSNAME, DOSVER)))
		cleanUp("Cant open dos.library", ERROR_INVALID_RESIDENT_LIBRARY);



	/* Get gid and sgid's of current user */

	extOwner = muGetTaskExtOwner(NULL);
	if (!extOwner)
		cleanUp("Out of memory.", ERROR_NO_FREE_STORE);


	/* If attempting to su to root, so check to see if the user is in the
		group root, (either primary or secondary member). */

	if (!strcmp(suUserID, ROOTUSERID)) {

		/* Check primary group */
		if (extOwner->gid != muROOT_GID) {

			/* Check secondary groups */
			for (i = 0; i < extOwner->NumSecGroups; i++)
				if (muSecGroups(extOwner)[i] == muROOT_GID) {
					inRootGroup = TRUE;	/* User is in root group */
					break;
				}

				/* User not in root gruop */
			if (!inRootGroup)
				cleanUp("Not in correct group to su to root", RETURN_WARN);
		}

	}


	/* ok, now if correct password supplied, su successfully. */

	if (!muLogin(muT_UserID, (ULONG) suUserID, TAG_DONE))
		/* Incorrect password. */
		cleanUp("Sorry.", RETURN_WARN);


	/* Now start new shell with the new uid */
	/* This will spawn off a new process, and this program will not wait for
	** the shell to return */

	if (!Execute(NEWSHELL,0,0))
		cleanUp("Could not open new shell", RETURN_FAIL);


	/* logout and return to the original shell */

	muLogout(muT_Quiet, TRUE, TAG_DONE);

	cleanUp("", RETURN_OK);

}

/*-----------------------------------
** Closedown, shut libs, and free allocated memory.
** Exit with the supplied return code
**----------------------------------*/

void cleanUp(char *str, int rc)
{

	if (extOwner)
		muFreeExtOwner(extOwner);

	/* Since using >= v36, no need to check for NULL library pointers */
	CloseLibrary((struct Library *)muBase);
	CloseLibrary((struct Library *)DosBase);

	if (strcmp(str, ""))
		puts(str);

	exit(rc);

}
