| /* pt_chmod - helper program for `grantpt'. |
| Copyright (C) 1998-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by C. Scott Ananian <cananian@alumni.princeton.edu>, 1998. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <argp.h> |
| #include <errno.h> |
| #include <error.h> |
| #include <grp.h> |
| #include <libintl.h> |
| #include <locale.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #ifdef HAVE_LIBCAP |
| # include <sys/capability.h> |
| # include <sys/prctl.h> |
| #endif |
| |
| #include "pty-private.h" |
| |
| /* Get libc version number. */ |
| #include "../version.h" |
| |
| #define PACKAGE _libc_intl_domainname |
| |
| /* Name and version of program. */ |
| static void print_version (FILE *stream, struct argp_state *state); |
| void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; |
| |
| /* Function to print some extra text in the help message. */ |
| static char *more_help (int key, const char *text, void *input); |
| |
| /* Data structure to communicate with argp functions. */ |
| static struct argp argp = |
| { |
| NULL, NULL, NULL, NULL, NULL, more_help |
| }; |
| |
| |
| /* Print the version information. */ |
| static void |
| print_version (FILE *stream, struct argp_state *state) |
| { |
| fprintf (stream, "pt_chown %s%s\n", PKGVERSION, VERSION); |
| fprintf (stream, gettext ("\ |
| Copyright (C) %s Free Software Foundation, Inc.\n\ |
| This is free software; see the source for copying conditions. There is NO\n\ |
| warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ |
| "), "2014"); |
| } |
| |
| static char * |
| more_help (int key, const char *text, void *input) |
| { |
| char *cp; |
| char *tp; |
| |
| switch (key) |
| { |
| case ARGP_KEY_HELP_PRE_DOC: |
| asprintf (&cp, gettext ("\ |
| Set the owner, group and access permission of the slave pseudo\ |
| terminal corresponding to the master pseudo terminal passed on\ |
| file descriptor `%d'. This is the helper program for the\ |
| `grantpt' function. It is not intended to be run directly from\ |
| the command line.\n"), |
| PTY_FILENO); |
| return cp; |
| case ARGP_KEY_HELP_EXTRA: |
| /* We print some extra information. */ |
| if (asprintf (&tp, gettext ("\ |
| For bug reporting instructions, please see:\n\ |
| %s.\n"), REPORT_BUGS_TO) < 0) |
| return NULL; |
| if (asprintf (&cp, gettext ("\ |
| The owner is set to the current user, the group is set to `%s',\ |
| and the access permission is set to `%o'.\n\n\ |
| %s"), |
| TTY_GROUP, S_IRUSR|S_IWUSR|S_IWGRP, tp) < 0) |
| { |
| free (tp); |
| return NULL; |
| } |
| return cp; |
| default: |
| break; |
| } |
| return (char *) text; |
| } |
| |
| static int |
| do_pt_chown (void) |
| { |
| char *pty; |
| struct stat64 st; |
| struct group *p; |
| gid_t gid; |
| |
| /* Check that PTY_FILENO is a valid master pseudo terminal. */ |
| pty = ptsname (PTY_FILENO); |
| if (pty == NULL) |
| return errno == EBADF ? FAIL_EBADF : FAIL_EINVAL; |
| |
| /* Check that the returned slave pseudo terminal is a |
| character device. */ |
| if (stat64 (pty, &st) < 0 || !S_ISCHR (st.st_mode)) |
| return FAIL_EINVAL; |
| |
| /* Get the group ID of the special `tty' group. */ |
| p = getgrnam (TTY_GROUP); |
| gid = p ? p->gr_gid : getgid (); |
| |
| /* Set the owner to the real user ID, and the group to that special |
| group ID. */ |
| if (chown (pty, getuid (), gid) < 0) |
| return FAIL_EACCES; |
| |
| /* Set the permission mode to readable and writable by the owner, |
| and writable by the group. */ |
| if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP) |
| && chmod (pty, S_IRUSR|S_IWUSR|S_IWGRP) < 0) |
| return FAIL_EACCES; |
| |
| return 0; |
| } |
| |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| uid_t euid = geteuid (); |
| uid_t uid = getuid (); |
| int remaining; |
| |
| if (argc == 1 && euid == 0) |
| { |
| #ifdef HAVE_LIBCAP |
| /* Drop privileges. */ |
| if (uid != euid) |
| { |
| static const cap_value_t cap_list[] = |
| { CAP_CHOWN, CAP_FOWNER }; |
| # define ncap_list (sizeof (cap_list) / sizeof (cap_list[0])) |
| cap_t caps = cap_init (); |
| if (caps == NULL) |
| return FAIL_ENOMEM; |
| |
| /* There is no reason why these should not work. */ |
| cap_set_flag (caps, CAP_PERMITTED, ncap_list, cap_list, CAP_SET); |
| cap_set_flag (caps, CAP_EFFECTIVE, ncap_list, cap_list, CAP_SET); |
| |
| int res = cap_set_proc (caps); |
| |
| cap_free (caps); |
| |
| if (__builtin_expect (res != 0, 0)) |
| return FAIL_EXEC; |
| } |
| #endif |
| |
| /* Normal invocation of this program is with no arguments and |
| with privileges. */ |
| return do_pt_chown (); |
| } |
| |
| /* We aren't going to be using privileges, so drop them right now. */ |
| setuid (uid); |
| |
| /* Set locale via LC_ALL. */ |
| setlocale (LC_ALL, ""); |
| |
| /* Set the text message domain. */ |
| textdomain (PACKAGE); |
| |
| /* parse and process arguments. */ |
| argp_parse (&argp, argc, argv, 0, &remaining, NULL); |
| |
| if (remaining < argc) |
| { |
| /* We should not be called with any non-option parameters. */ |
| error (0, 0, gettext ("too many arguments")); |
| argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, |
| program_invocation_short_name); |
| return EXIT_FAILURE; |
| } |
| |
| /* Check if we are properly installed. */ |
| if (euid != 0) |
| error (FAIL_EXEC, 0, gettext ("needs to be installed setuid `root'")); |
| |
| return EXIT_SUCCESS; |
| } |