Index: server/common/oursrc/accountadm/Makefile.in
===================================================================
--- server/common/oursrc/accountadm/Makefile.in	(revision 528)
+++ server/common/oursrc/accountadm/Makefile.in	(revision 544)
@@ -1,3 +1,4 @@
 CC = @CC@
+CPPFLAGS = @CPPFLAGS@
 CFLAGS = @CFLAGS@
 prefix = @prefix@
@@ -7,5 +8,8 @@
 sysconfdir = @sysconfdir@
 
-all-local: signup-scripts-frontend
+all-local: admof signup-scripts-frontend
+
+admof: admof.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $< -L/usr/lib/afs -L/usr/lib64/afs -lprot -lauth -lrxkad -lubik -laudit -lsys -lrx -llwp -lsys -lafsutil -lcom_err -lresolv -lkrb5 -ldes -lkrb4 -o $@
 
 install:
@@ -18,7 +22,8 @@
 
 clean:
-	rm -f signup-scripts-frontend
+	rm -f admof signup-scripts-frontend
 
 distclean: clean
+	rm -f mbash signup-scripts-backend
 	rm -f configure config.* Makefile
 	rm -rf auto*.cache
Index: server/common/oursrc/accountadm/admof.c
===================================================================
--- server/common/oursrc/accountadm/admof.c	(revision 544)
+++ server/common/oursrc/accountadm/admof.c	(revision 544)
@@ -0,0 +1,186 @@
+/* admof
+ * Version 2.0, released 2007-12-30
+ * Anders Kaseorg <andersk@mit.edu>
+ * replacing Perl version by Jeff Arnold <jbarnold@mit.edu>
+ *
+ * Usage:
+ *   admof scripts andersk/root@ATHENA.MIT.EDU
+ * Outputs "yes" and exits with status 33 if the given principal is an
+ * administrator of the locker.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <netinet/in.h>
+#include <afs/vice.h>
+#include <afs/venus.h>
+#include <afs/ptclient.h>
+#include <afs/ptuser.h>
+#include <afs/prs_fs.h>
+#include <afs/ptint.h>
+#include <afs/cellconfig.h>
+#include <afs/afsutil.h>
+#include <krb5.h>
+#include <kerberosIV/krb.h>
+
+extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
+
+#define die(args...) do { fprintf(stderr, args); pr_End(); exit(1); } while(0)
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+#define OVERLORDS "system:scripts-root"
+
+static int
+ismember(const char *user, const char *group)
+{
+    int flag;
+    if (pr_IsAMemberOf((char *)user, (char *)group, &flag) == 0)
+	return flag;
+    else
+	return 0;
+}
+
+/* Parse an ACL of n entries, returning the rights for user. */
+static int
+parse_rights(int n, const char **p, const char *user)
+{
+    int rights = 0;
+
+    int i;
+    for (i = 0; i < n; ++i) {
+	char tname[PR_MAXNAMELEN];
+	int trights;
+
+	int off;
+	if (sscanf(*p, "%" STR(PR_MAXNAMELEN) "s %d\n%n", tname, &trights, &off) < 2)
+	    die("internal error: can't parse output from pioctl\n");
+	*p += off;
+
+	if (~rights & trights &&
+	    (strcasecmp(tname, user) == 0 ||
+	     (strchr(tname, ':') != 0 && ismember(user, tname))))
+	    rights |= trights;
+    }
+
+    return rights;
+}
+
+int
+main(int argc, const char *argv[])
+{
+    /* Get arguments. */
+    if (argc != 3)
+	die("Usage: %s LOCKER PRINCIPAL\n", argv[0]);
+    const char *locker = argv[1], *name = argv[2];
+
+    /* Convert the locker into a directory. */
+    char dir[PATH_MAX];
+    int n;
+    struct passwd *pwd = getpwnam(locker);
+    if (pwd != NULL)
+	n = snprintf(dir, sizeof dir, pwd->pw_dir);
+    else
+	n = snprintf(dir, sizeof dir, "/mit/%s", locker);
+    if (n < 0 || n >= sizeof dir)
+	die("internal error\n");
+
+    /* Get the locker's cell. */
+    char cell[MAXCELLCHARS];
+    struct ViceIoctl vi;
+    vi.in = NULL;
+    vi.in_size = 0;
+    vi.out = cell;
+    vi.out_size = sizeof cell;
+    if (pioctl(dir, VIOC_FILE_CELL_NAME, &vi, 1) != 0)
+	die("internal error: pioctl: %m\n");
+
+    if (pr_Initialize(0, (char *)AFSDIR_CLIENT_ETC_DIRPATH, cell) != 0)
+	die("internal error: pr_Initialize failed\n");
+
+    /* Get the cell configuration. */
+    struct afsconf_dir *configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
+    if (configdir == NULL)
+	die("internal error: afsconf_Open failed\n");
+    struct afsconf_cell cellconfig;
+    if (afsconf_GetCellInfo(configdir, cell, NULL, &cellconfig) != 0)
+	die("internal error: afsconf_GetCellInfo failed\n");
+    afsconf_Close(configdir);
+
+    /* Figure out the cell's realm. */
+    krb5_context context;
+    krb5_init_context(&context);
+
+    char **realm_list;
+    if (krb5_get_host_realm(context, cellconfig.hostName[0], &realm_list) != 0 ||
+	realm_list[0] == NULL)
+	die("internal error: krb5_get_host_realm failed");
+
+    /* Convert the Kerberos 5 principal into a (Kerberos IV-style) AFS
+       name, omitting the realm if it equals the cell's realm. */
+    krb5_principal principal;
+    if (krb5_parse_name(context, name, &principal) != 0)
+	die("internal error: krb5_parse_name failed");
+    char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
+    if (krb5_524_conv_principal(context, principal, pname, pinst, prealm) != 0)
+	die("internal error: krb5_524_conv_principal failed\n");
+    char user[MAX(PR_MAXNAMELEN, MAX_K_NAME_SZ)];
+    if (kname_unparse(user, pname, pinst,
+		      strcmp(prealm, realm_list[0]) == 0 ? NULL : prealm) != 0)
+	die("internal error: kname_unparse failed\n");
+
+    krb5_free_principal(context, principal);
+    krb5_free_host_realm(context, realm_list);
+    krb5_free_context(context);
+
+    /* Instead of canonicalizing the name as below, we just use
+       strcasecmp above. */
+#if 0
+    afs_int32 id;
+    if (pr_SNameToId((char *)user, &id) != 0)
+	die("bad principal\n");
+    if (id == ANONYMOUSID)
+	die("anonymous\n");
+    if (pr_SIdToName(id, user) != 0)
+	die("internal error: pr_SIdToName failed\n");
+#endif
+
+    /* Read the locker ACL. */
+    char acl[2048];
+    vi.in = NULL;
+    vi.in_size = 0;
+    vi.out = acl;
+    vi.out_size = sizeof acl;
+    if (pioctl(dir, VIOCGETAL, &vi, 1) != 0)
+	die("internal error: pioctl: %m\n");
+
+    /* Parse the locker ACL to compute the user's rights. */
+    const char *p = acl;
+
+    int nplus, nminus;
+    int off;
+    if (sscanf(p, "%d\n%d\n%n", &nplus, &nminus, &off) < 2)
+	die("internal error: can't parse output from pioctl\n");
+    p += off;
+
+    int rights = parse_rights(nplus, &p, user);
+    rights &= ~parse_rights(nminus, &p, user);
+#ifdef OVERLORDS
+    if (~rights & PRSFS_ADMINISTER && ismember(user, OVERLORDS))
+	rights |= PRSFS_ADMINISTER;
+#endif
+
+    pr_End();
+
+    /* Output whether the user is an administrator. */
+    if (rights & PRSFS_ADMINISTER) {
+	printf("yes\n");
+	exit(33);
+    } else {
+	printf("no\n");
+	exit(1);
+    }
+}
Index: server/common/oursrc/accountadm/admof.in
===================================================================
--- server/common/oursrc/accountadm/admof.in	(revision 528)
+++ 	(revision )
@@ -1,129 +1,0 @@
-#!/usr/bin/perl
-use strict;
-
-# admof
-# Copyright (C) 2006  Jeff Arnold <jbarnold@mit.edu>
-#
-# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-#
-# See /COPYRIGHT in this repository for more information.
-
-$ENV{PATH} = '';
-
-my $targetuser;
-unless(($targetuser) = ($ARGV[0] =~ /^([\w._-]+)$/)) {
-  error("Invalid locker name: <$ARGV[0]>.");
-}
-my $curuser;
-unless(($curuser) = ($ARGV[1] =~ /^([\w._\/-]+)\@ATHENA\.MIT\.EDU$/)) {
-  error("An internal error has occurred.\nContact scripts\@mit.edu for assistance.");
-}
-
-($curuser) =~ s|/|.|; # Replace first instance of a / only; pts membership prints foo/root as foo.root
-
-if (($curuser) =~ m|/|) { # There were two /'s in their name. What?
-  error("An internal error has occurred.\nContact scripts\@mit.edu for assistance.");
-}
-
-my (undef, undef, $uid, undef, undef, undef, undef, $home, undef, undef)
-  = getpwnam $targetuser;
-if(defined $uid) {
-  error() if ($uid <= 1000);
-} else {
-  $home = "/mit/$targetuser";
-}
-
-my $cell;
-unless(open WHICHCELL, '-|') {
-  close STDERR;
-  exec '@fs_path@', 'whichcell', '-path', $home;
-  die;
-}
-
-unless(($cell) = (<WHICHCELL> =~ /^File \Q$home\E lives in cell '(.*)'$/)) {
-  error("Cannot find locker <$targetuser>.");
-}
-close WHICHCELL;
-
-open LISTACL, '-|', '@fs_path@', 'listacl', '-path', $home;
-
-#Access list for . is
-#Normal rights:
-#  system:scripts-root rlidwka
-#  system:anyuser rl
-
-unless(<LISTACL> eq "Access list for $home is\n" &&
-       <LISTACL> eq "Normal rights:\n") {
-  error("Cannot find locker <$targetuser>.");
-}
-
-if($ARGV[2] && !defined $uid) {
-  error("Locker <$targetuser> does not have a scripts.mit.edu account.");
-}
-
-my @targetacl = <LISTACL>;
-unshift(@targetacl, "  system:scripts-root a");
-
-close LISTACL;
-
-foreach(@targetacl) {
-  last unless /^  /;
-  my ($name) = /^  ([\w:_.-]+) \w*a\w*$/ or next;
-  if($name eq $curuser) { success(); }
-  elsif($name =~ /:/) {
-    unless(open MEMBERSHIP, '-|') {
-      close STDERR;
-      exec '@pts_path@', 'membership', '-nameorid', $name, '-cell', $cell;
-      die;
-    }
-
-#Members of system:scripts-root (id: -56104) are:
-#  hartmans
-#  jbarnold
-#  presbrey
-#  tabbott
-#  hartmans.root
-
-    next unless(<MEMBERSHIP> =~ /^Members of \Q$name\E \(id: \S+\) are:$/);
-    while(<MEMBERSHIP>) {
-      success() if($_ eq "  $curuser\n");
-    }
-    close MEMBERSHIP;
-  }
-}
-
-print <<END;
-
-ERROR:
-It appears as though you are not an administrator of locker <$targetuser>.
-In order to be able to su to <$targetuser>, you must have full AFS access
-to the root directory of locker <$targetuser>.  Try running the command
-fs sa /mit/$targetuser $curuser all
-on Athena in order to explicitly grant yourself full AFS access.
-Contact scripts\@mit.edu if you are unable to solve the problem.
-
-END
-
-exit(1);
-
-sub error {
-  print "\nERROR:\n$_[0]\n\n";
-  exit(1);
-}
-
-sub success {
-  print "yes";
-  exit(33);
-}
Index: server/common/oursrc/accountadm/configure.in
===================================================================
--- server/common/oursrc/accountadm/configure.in	(revision 528)
+++ server/common/oursrc/accountadm/configure.in	(revision 544)
@@ -15,34 +15,11 @@
 ])
 
-dnl Needed by admof.in
-
-AC_ARG_WITH(fs,
-[  --with-fs[=PATH]          fs is located at PATH],[
-  if test "$withval" != "no" -a "$withval" != "yes"; then
-    fs_path="$withval"
-  fi
-])
-REQUIRE_PATH(fs)
-
-AC_ARG_WITH(pts,
-[  --with-pts[=PATH]         pts is located at PATH],[
-  if test "$withval" != "no" -a "$withval" != "yes"; then
-    pts_path="$withval"
-  fi
-])
-REQUIRE_PATH(pts)
-
 dnl Needed by signup-scripts-backend.in
 
-LOCATE(ls)
-LOCATE(grep)
-LOCATE(sudo)
-LOCATE(useradd)
-LOCATE(groupadd)
-LOCATE(setquota)
 LOCATE(hesinfo)
 LOCATE(sort)
 LOCATE(head)
 LOCATE(ldapadd)
+LOCATE(sudo)
 
 dnl Needed by mbash.in
@@ -51,5 +28,4 @@
 
 AC_OUTPUT(Makefile)
-AC_OUTPUT(admof)
 AC_OUTPUT(signup-scripts-backend)
 AC_OUTPUT(mbash)
Index: server/common/oursrc/accountadm/mrproper
===================================================================
--- server/common/oursrc/accountadm/mrproper	(revision 528)
+++ server/common/oursrc/accountadm/mrproper	(revision 544)
@@ -1,5 +1,5 @@
 #!/bin/sh
 
-rm -f signup-scripts-frontend admof signup-scripts-backend
+rm -f signup-scripts-frontend admof signup-scripts-backend mbash
 rm -f configure config.* Makefile
 rm -rf auto*.cache
