1 /* pt_chmod - helper program for `grantpt'.
2    Copyright (C) 1998-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <argp.h>
20 #include <errno.h>
21 #include <error.h>
22 #include <grp.h>
23 #include <libintl.h>
24 #include <locale.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #ifdef HAVE_LIBCAP
32 # include <sys/capability.h>
33 # include <sys/prctl.h>
34 #endif
35 
36 #include "pty-private.h"
37 
38 /* Get libc version number.  */
39 #include "../version.h"
40 
41 #define PACKAGE _libc_intl_domainname
42 
43 /* Name and version of program.  */
44 static void print_version (FILE *stream, struct argp_state *state);
45 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
46 
47 /* Function to print some extra text in the help message.  */
48 static char *more_help (int key, const char *text, void *input);
49 
50 /* Data structure to communicate with argp functions.  */
51 static struct argp argp =
52 {
53   NULL, NULL, NULL, NULL, NULL, more_help
54 };
55 
56 
57 /* Print the version information.  */
58 static void
print_version(FILE * stream,struct argp_state * state)59 print_version (FILE *stream, struct argp_state *state)
60 {
61   fprintf (stream, "pt_chown %s%s\n", PKGVERSION, VERSION);
62   fprintf (stream, gettext ("\
63 Copyright (C) %s Free Software Foundation, Inc.\n\
64 This is free software; see the source for copying conditions.  There is NO\n\
65 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
66 "), "2021");
67 }
68 
69 static char *
more_help(int key,const char * text,void * input)70 more_help (int key, const char *text, void *input)
71 {
72   char *cp;
73   char *tp;
74 
75   switch (key)
76     {
77     case ARGP_KEY_HELP_PRE_DOC:
78       asprintf (&cp, gettext ("\
79 Set the owner, group and access permission of the slave pseudo\
80  terminal corresponding to the master pseudo terminal passed on\
81  file descriptor `%d'.  This is the helper program for the\
82  `grantpt' function.  It is not intended to be run directly from\
83  the command line.\n"),
84 		PTY_FILENO);
85       return cp;
86     case ARGP_KEY_HELP_EXTRA:
87       /* We print some extra information.  */
88       if (asprintf (&tp, gettext ("\
89 For bug reporting instructions, please see:\n\
90 %s.\n"), REPORT_BUGS_TO) < 0)
91 	return NULL;
92       if (asprintf (&cp, gettext ("\
93 The owner is set to the current user, the group is set to `%s',\
94  and the access permission is set to `%o'.\n\n\
95 %s"),
96 		    TTY_GROUP, S_IRUSR|S_IWUSR|S_IWGRP, tp) < 0)
97 	{
98 	  free (tp);
99 	  return NULL;
100 	}
101       return cp;
102     default:
103       break;
104     }
105   return (char *) text;
106 }
107 
108 static int
do_pt_chown(void)109 do_pt_chown (void)
110 {
111   char *pty;
112   struct stat64 st;
113   struct group *p;
114   gid_t gid;
115 
116   /* Check that PTY_FILENO is a valid master pseudo terminal.  */
117   pty = ptsname (PTY_FILENO);
118   if (pty == NULL)
119     return errno == EBADF ? FAIL_EBADF : FAIL_EINVAL;
120 
121   /* Check that the returned slave pseudo terminal is a
122      character device.  */
123   if (stat64 (pty, &st) < 0 || !S_ISCHR (st.st_mode))
124     return FAIL_EINVAL;
125 
126   /* Get the group ID of the special `tty' group.  */
127   p = getgrnam (TTY_GROUP);
128   gid = p ? p->gr_gid : getgid ();
129 
130   /* Set the owner to the real user ID, and the group to that special
131      group ID.  */
132   if (chown (pty, getuid (), gid) < 0)
133     return FAIL_EACCES;
134 
135   /* Set the permission mode to readable and writable by the owner,
136      and writable by the group.  */
137   if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP)
138       && chmod (pty, S_IRUSR|S_IWUSR|S_IWGRP) < 0)
139     return FAIL_EACCES;
140 
141   return 0;
142 }
143 
144 
145 int
main(int argc,char * argv[])146 main (int argc, char *argv[])
147 {
148   uid_t euid = geteuid ();
149   uid_t uid = getuid ();
150   int remaining;
151   sigset_t signalset;
152 
153   /* Clear any signal mask from the parent process.  */
154   sigemptyset (&signalset);
155   sigprocmask (SIG_SETMASK, &signalset, NULL);
156 
157   if (argc == 1 && euid == 0)
158     {
159 #ifdef HAVE_LIBCAP
160   /* Drop privileges.  */
161       if (uid != euid)
162 	{
163 	  static const cap_value_t cap_list[] =
164 	    { CAP_CHOWN, CAP_FOWNER	};
165 # define ncap_list (sizeof (cap_list) / sizeof (cap_list[0]))
166 	  cap_t caps = cap_init ();
167 	  if (caps == NULL)
168 	    return FAIL_ENOMEM;
169 
170 	  /* There is no reason why these should not work.  */
171 	  cap_set_flag (caps, CAP_PERMITTED, ncap_list, cap_list, CAP_SET);
172 	  cap_set_flag (caps, CAP_EFFECTIVE, ncap_list, cap_list, CAP_SET);
173 
174 	  int res = cap_set_proc (caps);
175 
176 	  cap_free (caps);
177 
178 	  if (__glibc_unlikely (res != 0))
179 	    return FAIL_EXEC;
180 	}
181 #endif
182 
183       /* Normal invocation of this program is with no arguments and
184 	 with privileges.  */
185       return do_pt_chown ();
186     }
187 
188   /* We aren't going to be using privileges, so drop them right now. */
189   setuid (uid);
190 
191   /* Set locale via LC_ALL.  */
192   setlocale (LC_ALL, "");
193 
194   /* Set the text message domain.  */
195   textdomain (PACKAGE);
196 
197   /* parse and process arguments.  */
198   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
199 
200   if (remaining < argc)
201     {
202       /* We should not be called with any non-option parameters.  */
203       error (0, 0, gettext ("too many arguments"));
204       argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
205 		 program_invocation_short_name);
206       return EXIT_FAILURE;
207     }
208 
209   /* Check if we are properly installed.  */
210   if (euid != 0)
211     error (FAIL_EXEC, 0, gettext ("needs to be installed setuid `root'"));
212 
213   return EXIT_SUCCESS;
214 }
215