/*
 * droproot.cpp
 *
 * Copyright (c) 2009 Vadtec (vadtec@vadtec.net)
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * Copyright (C) 2004-2008  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "znc.h"
#include "User.h"
#include <pwd.h>
#include <grp.h>

class CDroproot : public CGlobalModule {

public:
	GLOBALMODCONSTRUCTOR(CDroproot) {
	}

	virtual ~CDroproot() {
	}

	uid_t GetUser(const CString& sUser, CString& sMessage) {
		uid_t ret = sUser.ToUInt();

		if (ret != 0)
			return ret;

		struct passwd *pUser = getpwnam(sUser.c_str());

		if (!pUser) {
			sMessage = "User [" + sUser + "] not found!";
			return 0;
		}

		return pUser->pw_uid;
	}

	gid_t GetGroup(const CString& sGroup, CString& sMessage) {
		gid_t ret = sGroup.ToUInt();

		if (ret != 0)
			return ret;

		struct group *pGroup = getgrnam(sGroup.c_str());

		if (!pGroup) {
			sMessage = "Group [" + sGroup + "] not found!";
			return 0;
		}

		return pGroup->gr_gid;
	}

	virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
		CString sUser = sArgs.Token(0);
		CString sGroup = sArgs.Token(1, true);

		if (sUser.empty() || sGroup.empty()) {
			sMessage = "Usage: LoadModule = Droproot <uid> <gid>";
			return false;
		}

		m_user = GetUser(sUser, sMessage);

		if (m_user == 0) {
			sMessage
					= "Error: Cannot run as root, check your config file | Useage: LoadModule = Droproot <uid> <gid>";
			return false;
		}

		m_group = GetGroup(sGroup, sMessage);

		if (m_group == 0) {
			sMessage
					= "Error: Cannot run as root, check your config file | Useage: LoadModule = Droproot <uid> <gid>";
			return false;
		}

		return true;
	}

	virtual bool OnBoot() {
		int u, eu, g, eg, sg;

		if ((geteuid() == 0) || (getuid() == 0) || (getegid() == 0) || (getgid()
				== 0)) {

			CUtils::PrintAction("Dropping root permissions");

			// Clear all the supplementary groups
			sg = setgroups(0, NULL);

			if (sg < 0) {
				CUtils::PrintStatus(false,
						"Could not remove supplementary groups! ["
								+ CString(strerror(errno)) + "]");

				return false;
			}

			// Set the group (if we are root, this sets all three group IDs)
			g = setgid(m_group);
			eg = setegid(m_group);

			if ((g < 0) || (eg < 0)) {
				CUtils::PrintStatus(false, "Could not switch group id! ["
						+ CString(strerror(errno)) + "]");

				return false;
			}

			// and set the user (if we are root, this sets all three user IDs)
			u = setuid(m_user);
			eu = seteuid(m_user);

			if ((u < 0) || (eu < 0)) {
				CUtils::PrintStatus(false, "Could not switch user id! ["
						+ CString(strerror(errno)) + "]");

				return false;
			}

			CUtils::PrintStatus(true);

			return true;
		}

		return true;
	}

protected:
	uid_t m_user;
	gid_t m_group;
};

GLOBALMODULEDEFS(CDroproot, "Allows ZNC to drop root privileges and run as an un-privileged user.")
