RCS_ID_C="$Id: passwd.c,v 1.5 1994/05/02 19:52:55 jraja Exp $";
/*
 * passwd.c -- change password
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * Copyright © 1994 AmiTCP/IP Group, <AmiTCP-group@hut.fi>
 *                  Helsinki University of Technology, Finland.
 *
 * Created      : Tue Jan 11 14:06:41 1994 ppessi
 * Last modified: Sun Feb 27 03:45:36 1994 ppessi
 *
 */

#include "passwd_rev.h"

static const char version[] = VERSTAG " "
  "Copyright © 1994 AmiTCP/IP Group, <amitcp-group@hut.fi>\n"
  "Helsinki University of Technology, Finland.\n";

/****** utilities/passwd *****************************************************

    NAME
        passwd - modify a user's password

    VERSION
        $Id: passwd.c,v 1.5 1994/05/02 19:52:55 jraja Exp $

    SYNOPSIS
        passwd [user]

    FUNCTION

        Passwd changes the user's password.  First, the user is prompted for
        their current password.  If the current password is correctly typed, a
        new password is requested.  The new password must be entered twice to
        avoid typing errors.

        The new password should be at least six characters long and not purely
        alphabetic.  Its total length must be less than _PASSWORD_LEN
        (currently 128 characters).  Numbers, upper case letters and meta
        characters are en­ couraged.

        Once the password has been verified, passwd communicates the new
        password information to the netinfo.device.

        The super-user is not required to provide a user's current password.

    FILES

    SEE ALSO
        login

        Robert Morris, and Ken Thompson, UNIX password security.

    HISTORY
        A passwd command appeared in Version 6 AT&T UNIX.

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

#if defined(__SASC)
extern struct ExecBase *SysBase;
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/exec_protos.h>
#include <proto/usergroup.h>
#include <proto/dos.h>
/* Disable ^C signaling */
void __regargs __chkabort(void) {}
#else
#include <clib/exec_protos.h>
#include <clib/usergroup_protos.h>
#include <clib/dos_protos.h>
#endif

#include <dos/rdargs.h>

#include <sys/time.h>
#include <sys/param.h>

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

#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>

#include "config.h"

#include <bsdsocket.h>

BPTR Stdin, Stdout, Stderr;
APTR WinPtr = NULL;
struct RDArgs *rdargs = NULL;
struct MsgPort *port = NULL;
struct NetInfoReq *req = NULL;
short device_is_open = 0;

static void resume_amiga_stdio(void)
{
  struct Process* me = (struct Process *)FindTask(NULL);
  me->pr_WindowPtr = WinPtr;

  if (rdargs)
    FreeArgs(rdargs);
  rdargs = NULL;

  if (device_is_open) 
    CloseDevice((struct IORequest *)req);
  if (req)
    DeleteIORequest((struct IORequest *)req);
  if (port)
    DeleteMsgPort(port);
}

static void startup_amiga_stdio(void)
{
  struct Process* me = (struct Process *)FindTask(NULL);
  Stdin = me -> pr_CIS;
  Stdout = me -> pr_COS;
  Stderr = (me -> pr_CES ? me -> pr_CES : Stdout);

  /* Remove requesters */
  WinPtr = me->pr_WindowPtr;
  me->pr_WindowPtr = (void *)-1L;
  atexit(resume_amiga_stdio);
}

int local_passwd(char *uname);
char *getnewpasswd(struct passwd *pw);
void pw_update(struct passwd *pw);

void main(void)
{
  const char *template = 
    "USERNAME";
  struct {
    STRPTR a_name;
  } args[1] = { 0 };

  startup_amiga_stdio();

  rdargs = ReadArgs((STRPTR)template, (LONG *)args, NULL);
  if (!rdargs) {
    PrintFault(IoErr(), "passwd");
    exit(RETURN_ERROR);
  } 

  if (args->a_name) {
    exit(local_passwd(args->a_name));
  } else {
    char *uname = getlogin();
    if (uname)
      exit(local_passwd(uname));
    
    FPrintf(Stderr, "passwd: no user");
    exit(RETURN_ERROR);
  }
}

static uid_t uid;

int local_passwd(char *uname)
{
  struct passwd *pw;

  if (!(pw = getpwnam(uname))) {
    (void)FPrintf(Stderr, "passwd: unknown user %s.\n", uname);
    exit(RETURN_ERROR);
  }

  uid = getuid();
  if (uid && uid != pw->pw_uid) {
    (void)FPrintf(Stderr, "passwd: %s\n", ug_StrError(EACCES));
    exit(RETURN_ERROR);
  }

  /*
   * Get the new password.
   */
  pw->pw_passwd = getnewpasswd(pw);
  
  pw_update(pw);
  return 0;
}

char *getnewpasswd(struct passwd *pw)
{
  register char *p, *t;
  int tries;
  char buf[_PASSWORD_LEN+1], salt[16];

  (void)Printf("Changing password for %s.\n", pw->pw_name);

  if (uid && pw->pw_passwd && pw->pw_passwd[0] &&
      strcmp(crypt(getpass("Old password:"), pw->pw_passwd),
	     pw->pw_passwd)) {
    (void)FPrintf(Stderr, "passwd: %s\n", ug_StrError(EACCES));
    exit(RETURN_ERROR);
  }

  for (buf[0] = '\0', tries = 0;;) {
    p = getpass("New password:");
    if (!*p) {
      (void)PutStr("Password unchanged.\n");
      exit(RETURN_ERROR);
    }
    if (strlen(p) <= 5 && (uid != 0 || ++tries < 2)) {
      (void)PutStr("Please enter a longer password.\n");
      continue;
    }
    for (t = p; *t && islower(*t); ++t);
    if (!*t && (uid != 0 || ++tries < 2)) {
      (void)PutStr("Please don't use an all-lower case password.\n"
		   "Unusual capitalization, control characters or "
		   "digits are suggested.\n");
      continue;
    }
    (void)strncpy(buf, p, _PASSWORD_LEN);
    if (!strcmp(buf, getpass("Retype new password:")))
      break;
    (void)Printf("Mismatch; try again, EOF to quit.\n");
  }

  strncpy(salt, pw->pw_passwd, sizeof(salt));
  salt[8] = '\0';
  ug_GetSalt(pw, salt, sizeof(salt));

  return(crypt(buf, salt));
}

#include <devices/netinfo.h>

void pw_update(struct passwd *pw)
{
  if (port = CreateMsgPort()) 
    req = CreateIORequest(port, sizeof(*req));
  if (!port || !req) {
    PrintFault(ERROR_NO_FREE_STORE, "CreateIORequest");
    exit(RETURN_FAIL);
  }

  if (OpenDevice(NETINFONAME, NETINFO_PASSWD_UNIT, 
		 (struct IORequest *)req, 0L)) {
    Printf("%s: %s\n", NETINFONAME, ug_StrError(req->io_Error));
    exit(RETURN_FAIL);
  }

  device_is_open = 1;

  req->io_Data = pw;
  req->io_Command = CMD_WRITE;
  DoIO(req);
  if (req->io_Error) {
    Printf("%s (%s): %s\n", NETINFONAME, "CMD_WRITE", 
	   ug_StrError(req->io_Error));
    exit(RETURN_FAIL);
  }

  req->io_Command = CMD_UPDATE;
  DoIO(req);
  if (req->io_Error) {
    Printf("%s (%s): %s\n", NETINFONAME, "CMD_UPDATE", 
	   ug_StrError(req->io_Error));
    exit(RETURN_FAIL);
  }
}
