This patch will upgrade Sudo version 1.8.14 patchlevel 1 to Sudo
version 1.8.14 patchlevel 2.  To apply:

    $ cd sudo-1.8.14p1
    $ patch -p1 < sudo-1.8.14p2.patch

diff -urNa sudo-1.8.14p1/ChangeLog sudo-1.8.14p2/ChangeLog
--- sudo-1.8.14p1/ChangeLog	Fri Jul 17 20:09:00 2015
+++ sudo-1.8.14p2/ChangeLog	Mon Jul 20 20:28:44 2015
@@ -1,3 +1,20 @@
+2015-07-20  Todd C. Miller  <Todd.Miller@courtesan.com>
+
+	* NEWS, configure, configure.ac:
+	Sudo 1.8.14p2
+	[55fe56b28c7b]
+
+	* plugins/sudoers/timestamp.c:
+	Fix creation of the timestamp file; bug #704
+	[1ff77fd5cc8f]
+
+2015-07-19  Todd C. Miller  <Todd.Miller@courtesan.com>
+
+	* src/regress/ttyname/check_ttyname.c, src/sudo.c, src/sudo.h,
+	src/ttyname.c:
+	Avoid needless memory allocation when resolving the tty name.
+	[c58cce92d5e0]
+
 2015-07-17  Todd C. Miller  <Todd.Miller@courtesan.com>
 
 	* NEWS, configure, configure.ac:
diff -urNa sudo-1.8.14p1/NEWS sudo-1.8.14p2/NEWS
--- sudo-1.8.14p1/NEWS	Fri Jul 17 15:29:01 2015
+++ sudo-1.8.14p2/NEWS	Mon Jul 20 20:27:54 2015
@@ -1,3 +1,8 @@
+What's new in Sudo 1.8.14p2
+
+ * Fixed a bug introduced in sudo 1.8.14 that prevented the lecture
+   file from being created.  Bug #704.
+
 What's new in Sudo 1.8.14p1
 
  * Fixed a bug introduced in sudo 1.8.14 that prevented the sssd
diff -urNa sudo-1.8.14p1/configure sudo-1.8.14p2/configure
--- sudo-1.8.14p1/configure	Fri Jul 17 15:29:01 2015
+++ sudo-1.8.14p2/configure	Mon Jul 20 20:27:54 2015
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for sudo 1.8.14p1.
+# Generated by GNU Autoconf 2.69 for sudo 1.8.14p2.
 #
 # Report bugs to <http://www.sudo.ws/bugs/>.
 #
@@ -590,8 +590,8 @@
 # Identity of this package.
 PACKAGE_NAME='sudo'
 PACKAGE_TARNAME='sudo'
-PACKAGE_VERSION='1.8.14p1'
-PACKAGE_STRING='sudo 1.8.14p1'
+PACKAGE_VERSION='1.8.14p2'
+PACKAGE_STRING='sudo 1.8.14p2'
 PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/'
 PACKAGE_URL=''
 
@@ -1524,7 +1524,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures sudo 1.8.14p1 to adapt to many kinds of systems.
+\`configure' configures sudo 1.8.14p2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1589,7 +1589,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of sudo 1.8.14p1:";;
+     short | recursive ) echo "Configuration of sudo 1.8.14p2:";;
    esac
   cat <<\_ACEOF
 
@@ -1835,7 +1835,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-sudo configure 1.8.14p1
+sudo configure 1.8.14p2
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2361,7 +2361,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by sudo $as_me 1.8.14p1, which was
+It was created by sudo $as_me 1.8.14p2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -25089,7 +25089,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by sudo $as_me 1.8.14p1, which was
+This file was extended by sudo $as_me 1.8.14p2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -25155,7 +25155,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-sudo config.status 1.8.14p1
+sudo config.status 1.8.14p2
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -urNa sudo-1.8.14p1/configure.ac sudo-1.8.14p2/configure.ac
--- sudo-1.8.14p1/configure.ac	Fri Jul 17 15:29:01 2015
+++ sudo-1.8.14p2/configure.ac	Mon Jul 20 20:27:54 2015
@@ -4,7 +4,7 @@
 dnl Copyright (c) 1994-1996,1998-2015 Todd C. Miller <Todd.Miller@courtesan.com>
 dnl
 AC_PREREQ([2.59])
-AC_INIT([sudo], [1.8.14p1], [http://www.sudo.ws/bugs/], [sudo])
+AC_INIT([sudo], [1.8.14p2], [http://www.sudo.ws/bugs/], [sudo])
 AC_CONFIG_HEADER([config.h pathnames.h])
 AC_CONFIG_SRCDIR([src/sudo.c])
 dnl
diff -urNa sudo-1.8.14p1/plugins/sudoers/timestamp.c sudo-1.8.14p2/plugins/sudoers/timestamp.c
--- sudo-1.8.14p1/plugins/sudoers/timestamp.c	Wed Jul 15 12:44:07 2015
+++ sudo-1.8.14p2/plugins/sudoers/timestamp.c	Mon Jul 20 20:27:54 2015
@@ -308,8 +308,9 @@
 }
 
 /*
- * Open and lock the specified timestamp file.
- * Returns 0 on success or -1 on failure.
+ * Open and lock the specified timestamp or lecture file.
+ * Returns open and locked file descriptor on success.
+ * Returns TIMESTAMP_OPEN_ERROR or TIMESTAMP_PERM_ERROR on error.
  */
 static int
 open_timestamp(const char *path, int flags)
@@ -320,7 +321,7 @@
 
     if (timestamp_uid != 0)
 	uid_changed = set_perms(PERM_TIMESTAMP);
-    fd = open(timestamp_file, flags, 0600);
+    fd = open(path, flags, 0600);
     if (uid_changed && !restore_perms()) {
 	/* Unable to restore permissions, should not happen. */
 	if (fd != -1) {
diff -urNa sudo-1.8.14p1/src/regress/ttyname/check_ttyname.c sudo-1.8.14p2/src/regress/ttyname/check_ttyname.c
--- sudo-1.8.14p1/src/regress/ttyname/check_ttyname.c	Wed Jul 15 12:44:07 2015
+++ sudo-1.8.14p2/src/regress/ttyname/check_ttyname.c	Mon Jul 20 20:27:54 2015
@@ -26,6 +26,7 @@
 # include <strings.h>
 #endif /* HAVE_STRINGS_H */
 #include <unistd.h>
+#include <limits.h>
 #include <errno.h>
 
 #include "sudo_compat.h"
@@ -36,18 +37,20 @@
 __dso_public int main(int argc, char *argv[]);
 
 int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
-extern char *get_process_ttyname(void);
+extern char *get_process_ttyname(char *name, size_t namelen);
 
 int
 main(int argc, char *argv[])
 {
-    char *tty_libc = NULL, *tty_sudo;
+    char *tty_libc = NULL, *tty_sudo = NULL;
+    char pathbuf[PATH_MAX];
     int rval = 1;
 
     initprogname(argc > 0 ? argv[0] : "check_ttyname");
 
     /* Lookup tty name using kernel info if possible. */
-    tty_sudo = get_process_ttyname();
+    if (get_process_ttyname(pathbuf, sizeof(pathbuf)) != NULL)
+	tty_sudo = pathbuf;
 
 #if defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV) || \
     defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || \
@@ -78,6 +81,5 @@
 	    tty_sudo ? tty_sudo : "none", tty_libc ? tty_libc : "none");
     }
 
-    free(tty_sudo);
     exit(rval);
 }
diff -urNa sudo-1.8.14p1/src/sudo.c sudo-1.8.14p2/src/sudo.c
--- sudo-1.8.14p1/src/sudo.c	Wed Jul 15 12:44:07 2015
+++ sudo-1.8.14p2/src/sudo.c	Mon Jul 20 20:27:54 2015
@@ -449,7 +449,7 @@
 static char **
 get_user_info(struct user_details *ud)
 {
-    char *cp, **user_info, cwd[PATH_MAX];
+    char *cp, **user_info, path[PATH_MAX];
     struct passwd *pw;
     int fd, i = 0;
     debug_decl(get_user_info, SUDO_DEBUG_UTIL)
@@ -518,23 +518,22 @@
     if ((cp = get_user_groups(ud)) != NULL)
 	user_info[++i] = cp;
 
-    if (getcwd(cwd, sizeof(cwd)) != NULL) {
-	user_info[++i] = sudo_new_key_val("cwd", cwd);
+    if (getcwd(path, sizeof(path)) != NULL) {
+	user_info[++i] = sudo_new_key_val("cwd", path);
 	if (user_info[i] == NULL)
 	    goto bad;
 	ud->cwd = user_info[i] + sizeof("cwd=") - 1;
     }
 
-    if ((cp = get_process_ttyname()) == NULL) {
-	/* tty may not always be present */
-	if (errno == ENOMEM)
-	    goto bad;
-    } else {
-	user_info[++i] = sudo_new_key_val("tty", cp);
+    if (get_process_ttyname(path, sizeof(path)) != NULL) {
+	user_info[++i] = sudo_new_key_val("tty", path);
 	if (user_info[i] == NULL)
 	    goto bad;
 	ud->tty = user_info[i] + sizeof("tty=") - 1;
-	free(cp);
+    } else {
+	/* tty may not always be present */
+	if (errno != ENOENT)
+	    goto bad;
     }
 
     cp = sudo_gethostname();
diff -urNa sudo-1.8.14p1/src/sudo.h sudo-1.8.14p2/src/sudo.h
--- sudo-1.8.14p1/src/sudo.h	Wed Jul 15 12:44:07 2015
+++ sudo-1.8.14p2/src/sudo.h	Mon Jul 20 20:27:54 2015
@@ -242,7 +242,7 @@
 int get_net_ifs(char **addrinfo);
 
 /* ttyname.c */
-char *get_process_ttyname(void);
+char *get_process_ttyname(char *name, size_t namelen);
 
 /* signal.c */
 struct sigaction;
diff -urNa sudo-1.8.14p1/src/ttyname.c sudo-1.8.14p2/src/ttyname.c
--- sudo-1.8.14p1/src/ttyname.c	Wed Jul 15 12:44:07 2015
+++ sudo-1.8.14p2/src/ttyname.c	Mon Jul 20 20:27:54 2015
@@ -96,52 +96,52 @@
 #if defined(sudo_kp_tdev)
 /*
  * Like ttyname() but uses a dev_t instead of an open fd.
- * Caller is responsible for freeing the returned string.
- * The BSD version uses devname()
+ * Returns name on success and NULL on failure, setting errno.
+ * The BSD version uses devname().
  */
 static char *
-sudo_ttyname_dev(dev_t tdev)
+sudo_ttyname_dev(dev_t tdev, char *name, size_t namelen)
 {
-    char *dev, *tty = NULL;
+    char *dev;
     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
 
     /* Some versions of devname() return NULL on failure, others do not. */
     dev = devname(tdev, S_IFCHR);
     if (dev != NULL && *dev != '?' && *dev != '#') {
-	if (*dev != '/') {
-	    /* devname() doesn't use the /dev/ prefix, add one... */
-	    size_t len = sizeof(_PATH_DEV) + strlen(dev);
-	    if ((tty = malloc(len)) != NULL) {
-		strlcpy(tty, _PATH_DEV, len);
-		strlcat(tty, dev, len);
-	    }
-	} else {
-	    /* Should not happen but just in case... */
-	    tty = strdup(dev);
-	}
+	if (strlcpy(name, _PATH_DEV, namelen) < namelen &&
+	    strlcat(name, dev, namelen) < namelen)
+	    debug_return_str(name);
+	errno = ERANGE;
     } else {
 	/* Not all versions of devname() set errno. */
 	errno = ENOENT;
     }
-    debug_return_str(tty);
+    debug_return_str(NULL);
 }
 #elif defined(HAVE__TTYNAME_DEV)
 extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen);
 
 /*
  * Like ttyname() but uses a dev_t instead of an open fd.
- * Caller is responsible for freeing the returned string.
+ * Returns name on success and NULL on failure, setting errno.
  * This version is just a wrapper around _ttyname_dev().
  */
 static char *
-sudo_ttyname_dev(dev_t tdev)
+sudo_ttyname_dev(dev_t tdev, char *name, size_t namelen)
 {
-    char buf[TTYNAME_MAX], *tty;
+    int serrno = errno;
     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
 
-    tty = _ttyname_dev(tdev, buf, sizeof(buf));
+    /*
+     * _ttyname_dev() sets errno to ERANGE if namelen is too small
+     * but does not modify it if tdev is not found.
+     */
+    errno = ENOENT;
+    if (_ttyname_dev(tdev, name, namelen) == NULL)
+	debug_return_str(NULL);
+    errno = serrno;
 
-    debug_return_str(tty ? strdup(tty) : NULL);
+    debug_return_str(name);
 }
 #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
 /*
@@ -167,29 +167,30 @@
 
 /*
  * Do a breadth-first scan of dir looking for the specified device.
+ * Returns name on success and NULL on failure, setting errno.
  */
 static char *
-sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin)
+sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin, char *name, size_t namelen)
 {
-    DIR *d = NULL;
-    char pathbuf[PATH_MAX], **subdirs = NULL, *devname = NULL;
     size_t sdlen, num_subdirs = 0, max_subdirs = 0;
+    char pathbuf[PATH_MAX], **subdirs = NULL;
+    char *rval = NULL;
     struct dirent *dp;
     unsigned int i;
+    DIR *d = NULL;
     debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
 
     if (dir[0] == '\0' || (d = opendir(dir)) == NULL)
 	goto done;
 
-    sudo_debug_printf(SUDO_DEBUG_INFO, "scanning for dev %u in %s",
-	(unsigned int)rdev, dir);
+    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+	"scanning for dev %u in %s", (unsigned int)rdev, dir);
 
     sdlen = strlen(dir);
     if (dir[sdlen - 1] == '/')
 	sdlen--;
     if (sdlen + 1 >= sizeof(pathbuf)) {
-	errno = ENAMETOOLONG;
-	sudo_warn("%.*s/", (int)sdlen, dir);
+	errno = ERANGE;
 	goto done;
     }
     memcpy(pathbuf, dir, sdlen);
@@ -233,17 +234,29 @@
 	}
 # if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
 	/*
-	 * Convert dp->d_type to sb.st_mode to avoid a stat(2) if possible.
-	 * We can't use it for links (since we want to follow them) or
-	 * char devs (since we need st_rdev to compare the device number).
+	 * Avoid excessive stat() calls by checking dp->d_type.
 	 */
-	if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK && dp->d_type != DT_CHR) {
-	    sb.st_mode = DTTOIF(dp->d_type);
-	    sb.st_rdev = 0;		/* quiet ccc-analyzer false positive */
-	} else
-# endif
+	switch (dp->d_type) {
+	    case DT_CHR:
+	    case DT_LNK:
+	    case DT_UNKNOWN:
+		/* Could be a character device, stat() it. */
+		if (stat(pathbuf, &sb) == -1)
+		    continue;
+		break;
+	    case DT_DIR:
+		/* Directory, no need to stat() it. */
+		sb.st_mode = DTTOIF(dp->d_type);
+		sb.st_rdev = 0;		/* quiet ccc-analyzer false positive */
+		break;
+	    default:
+		/* Not a character device, link or directory, skip it. */
+		continue;
+	}
+# else
 	if (stat(pathbuf, &sb) == -1)
 	    continue;
+# endif
 	if (S_ISDIR(sb.st_mode)) {
 	    if (!builtin) {
 		/* Add to list of subdirs to search. */
@@ -265,16 +278,23 @@
 	    continue;
 	}
 	if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
-	    devname = strdup(pathbuf);
-	    sudo_debug_printf(SUDO_DEBUG_INFO, "resolved dev %u as %s",
-		(unsigned int)rdev, pathbuf);
+	    sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+		"resolved dev %u as %s", (unsigned int)rdev, pathbuf);
+	    if (strlcpy(name, pathbuf, namelen) < namelen) {
+		rval = name;
+	    } else {
+		sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+		    "unable to store %s, have %zu, need %zu",
+		    pathbuf, namelen, strlen(pathbuf) + 1);
+		errno = ERANGE;
+	    }
 	    goto done;
 	}
     }
 
     /* Search subdirs if we didn't find it in the root level. */
-    for (i = 0; devname == NULL && i < num_subdirs; i++)
-	devname = sudo_ttyname_scan(subdirs[i], rdev, false);
+    for (i = 0; rval == NULL && i < num_subdirs; i++)
+	rval = sudo_ttyname_scan(subdirs[i], rdev, false, name, namelen);
 
 done:
     if (d != NULL)
@@ -282,20 +302,21 @@
     for (i = 0; i < num_subdirs; i++)
 	free(subdirs[i]);
     free(subdirs);
-    debug_return_str(devname);
+    debug_return_str(rval);
 }
 
 /*
  * Like ttyname() but uses a dev_t instead of an open fd.
- * Caller is responsible for freeing the returned string.
+ * Returns name on success and NULL on failure, setting errno.
  * Generic version.
  */
 static char *
-sudo_ttyname_dev(dev_t rdev)
+sudo_ttyname_dev(dev_t rdev, char *name, size_t namelen)
 {
+    char buf[PATH_MAX], **sd, *devname;
+    char *rval = NULL;
     struct stat sb;
     size_t len;
-    char buf[PATH_MAX], **sd, *devname, *tty = NULL;
     debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
 
     /*
@@ -310,22 +331,31 @@
 		    (unsigned int)minor(rdev));
 		if (stat(buf, &sb) == 0) {
 		    if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
-			tty = strdup(buf);
+			sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+			    "comparing dev %u to %s: match!",
+			    (unsigned int)rdev, buf);
+			if (strlcpy(name, buf, namelen) < namelen)
+			    rval = name;
+			else
+			    errno = ERANGE;
 			goto done;
 		    }
 		}
-		sudo_debug_printf(SUDO_DEBUG_INFO, "comparing dev %u to %s: %s",
-		    (unsigned int)rdev, buf, tty ? "yes" : "no");
+		sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+		    "comparing dev %u to %s: no", (unsigned int)rdev, buf);
 	    } else {
 		/* Traverse directory */
-		tty = sudo_ttyname_scan(devname, rdev, true);
-		if (tty != NULL || errno == ENOMEM)
+		rval = sudo_ttyname_scan(devname, rdev, true, name, namelen);
+		if (rval != NULL || errno == ENOMEM)
 		    goto done;
 	    }
 	} else {
 	    if (stat(devname, &sb) == 0) {
 		if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
-		    tty = strdup(devname);
+		    if (strlcpy(name, devname, namelen) < namelen)
+			rval = name;
+		    else
+			errno = ERANGE;
 		    goto done;
 		}
 	    }
@@ -335,24 +365,24 @@
     /*
      * Not found?  Do a breadth-first traversal of /dev/.
      */
-    tty = sudo_ttyname_scan(_PATH_DEV, rdev, false);
+    rval = sudo_ttyname_scan(_PATH_DEV, rdev, false, name, namelen);
 
 done:
-    debug_return_str(tty);
+    debug_return_str(rval);
 }
 #endif
 
 #if defined(sudo_kp_tdev)
 /*
- * Return a string from ttyname() containing the tty to which the process is
- * attached or NULL if the process has no controlling tty.
+ * Store the name of the tty to which the process is attached in name.
+ * Returns name on success and NULL on failure, setting errno.
  */
 char *
-get_process_ttyname(void)
+get_process_ttyname(char *name, size_t namelen)
 {
-    char *tty = NULL;
     struct sudo_kinfo_proc *ki_proc = NULL;
     size_t size = sizeof(*ki_proc);
+    char *rval = NULL;
     int mib[6], rc;
     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
 
@@ -379,30 +409,30 @@
     } while (rc == -1 && errno == ENOMEM);
     if (rc != -1) {
 	if ((dev_t)ki_proc->sudo_kp_tdev != (dev_t)-1) {
-	    tty = sudo_ttyname_dev(ki_proc->sudo_kp_tdev);
-	    if (tty == NULL) {
-		sudo_debug_printf(SUDO_DEBUG_WARN,
+	    rval = sudo_ttyname_dev(ki_proc->sudo_kp_tdev, name, namelen);
+	    if (rval == NULL) {
+		sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
 		    "unable to map device number %u to name",
 		    ki_proc->sudo_kp_tdev);
 	    }
 	}
     } else {
-	sudo_debug_printf(SUDO_DEBUG_WARN,
-	    "unable to resolve tty via KERN_PROC: %s", strerror(errno));
+	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+	    "unable to resolve tty via KERN_PROC");
     }
     free(ki_proc);
 
-    debug_return_str(tty);
+    debug_return_str(rval);
 }
 #elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
 /*
- * Return a string from ttyname() containing the tty to which the process is
- * attached or NULL if the process has no controlling tty.
+ * Store the name of the tty to which the process is attached in name.
+ * Returns name on success and NULL on failure, setting errno.
  */
 char *
-get_process_ttyname(void)
+get_process_ttyname(char *name, size_t namelen)
 {
-    char path[PATH_MAX], *tty = NULL;
+    char path[PATH_MAX], *rval = NULL;
     struct psinfo psinfo;
     ssize_t nread;
     int fd;
@@ -420,25 +450,26 @@
 		rdev = makedev(major64(psinfo.pr_ttydev), minor64(psinfo.pr_ttydev));
 #endif
 	    if (rdev != (dev_t)-1)
-		tty = sudo_ttyname_dev(rdev);
+		rval = sudo_ttyname_dev(rdev, name, namelen);
 	}
     }
 
-    if (tty == NULL)
-	sudo_debug_printf(SUDO_DEBUG_WARN,
-	    "unable to resolve tty via %s: %s", path, strerror(errno));
+    if (rval == NULL)
+	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+	    "unable to resolve tty via %s", path);
 
-    debug_return_str(tty);
+    debug_return_str(rval);
 }
 #elif defined(__linux__)
 /*
- * Return a string from ttyname() containing the tty to which the process is
- * attached or NULL if the process has no controlling tty.
+ * Store the name of the tty to which the process is attached in name.
+ * Returns name on success and NULL on failure, setting errno.
  */
 char *
-get_process_ttyname(void)
+get_process_ttyname(char *name, size_t namelen)
 {
-    char path[PATH_MAX], *line = NULL, *tty = NULL;
+    char path[PATH_MAX], *line = NULL;
+    char *rval = NULL;
     size_t linesize = 0;
     ssize_t len;
     FILE *fp;
@@ -465,7 +496,7 @@
 				"%s: tty device %s: %s", path, cp, errstr);
 			}
 			if (tdev > 0)
-			    tty = sudo_ttyname_dev(tdev);
+			    rval = sudo_ttyname_dev(tdev, name, namelen);
 			break;
 		    }
 		    cp = ep + 1;
@@ -475,22 +506,22 @@
 	free(line);
     }
 
-    if (tty == NULL)
-	sudo_debug_printf(SUDO_DEBUG_WARN,
-	    "unable to resolve tty via %s: %s", path, strerror(errno));
+    if (rval == NULL)
+	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+	    "unable to resolve tty via %s", path);
 
-    debug_return_str(tty);
+    debug_return_str(rval);
 }
 #elif defined(HAVE_PSTAT_GETPROC)
 /*
- * Return a string from ttyname() containing the tty to which the process is
- * attached or NULL if the process has no controlling tty.
+ * Store the name of the tty to which the process is attached in name.
+ * Returns name on success and NULL on failure, setting errno.
  */
 char *
-get_process_ttyname(void)
+get_process_ttyname(char *name, size_t namelen)
 {
     struct pst_status pstat;
-    char *tty = NULL;
+    char *rval = NULL;
     int rc;
     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
 
@@ -501,23 +532,23 @@
     rc = pstat_getproc(&pstat, sizeof(pstat), (size_t)0, (int)getpid());
     if (rc != -1 || errno == EOVERFLOW) {
 	if (pstat.pst_term.psd_major != -1 && pstat.pst_term.psd_minor != -1) {
-	    tty = sudo_ttyname_dev(makedev(pstat.pst_term.psd_major,
-		pstat.pst_term.psd_minor));
+	    rval = sudo_ttyname_dev(makedev(pstat.pst_term.psd_major,
+		pstat.pst_term.psd_minor), name, namelen);
 	}
     }
-    if (tty == NULL)
-	sudo_debug_printf(SUDO_DEBUG_WARN,
-	    "unable to resolve tty via pstat: %s", strerror(errno));
+    if (rval == NULL)
+	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+	    "unable to resolve tty via pstat");
 
-    debug_return_str(tty);
+    debug_return_str(rval);
 }
 #else
 /*
- * Return a string from ttyname() containing the tty to which the process is
- * attached or NULL if the process has no controlling tty.
+ * Store the name of the tty to which the process is attached in name.
+ * Returns name on success and NULL on failure, setting errno.
  */
 char *
-get_process_ttyname(void)
+get_process_ttyname(char *name, size_t namelen)
 {
     char *tty;
     debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
@@ -526,12 +557,18 @@
 	if ((tty = ttyname(STDOUT_FILENO)) == NULL)
 	    tty = ttyname(STDERR_FILENO);
     }
-    if (tty == NULL) {
-	sudo_debug_printf(SUDO_DEBUG_WARN,
-	    "unable to resolve tty via ttyname: %s", strerror(errno));
-	debug_return_str(NULL);
+    if (tty != NULL) {
+	if (strlcpy(name, tty, namelen) < namelen)
+	    debug_return_str(name);
+	errno = ERANGE;
+	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+	    "unable to store tty from ttyname");
+    } else {
+	sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+	    "unable to resolve tty via ttyname");
+	errno = ENOENT;
     }
 
-    debug_return_str(strdup(tty));
+    debug_return_str(NULL);
 }
 #endif