1 /*        $NetBSD: pt_filter.c,v 1.13 2021/04/12 09:18:14 mrg Exp $   */
2 
3 /*
4  * Copyright (c) 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD
8  * Foundation by Brian Grayson, and is dedicated to Rebecca
9  * Margaret Pollard-Grayson.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __RCSID("$NetBSD: pt_filter.c,v 1.13 2021/04/12 09:18:14 mrg Exp $");
36 #endif                                  /* not lint */
37 
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <err.h>
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/syslog.h>
47 
48 #include "portald.h"
49 
50 /*
51  * Key will be <key><path>.  We let the configuration file
52  * tell us how to filter the file.
53  */
54 
55 #define FILTER_CMD_SIZE       8192
56 
57 static void fill_cmd(char **, char *, char *, int);
58 
59 static void
fill_cmd(char ** cmdv,char * path,char * buff,int n)60 fill_cmd(char **cmdv, char *path, char *buff, int n)
61 {
62           int     i;
63           /* Make tempbuff at least as large as buff. */
64           char      *tempbuff = malloc(n);
65           if (tempbuff == NULL)
66                     err(1, NULL);
67 
68           strncpy(tempbuff, cmdv[0], n - 1);
69           tempbuff[n - 1] = '\0';
70           for (i = 1; cmdv[i]; i++) {
71                     strncat(tempbuff, " ", n - strlen(tempbuff));
72                     strncat(tempbuff, cmdv[i], n - strlen(tempbuff));
73           }
74           strncat(tempbuff, " ", n - strlen(tempbuff));
75           /* Now do the snprintf into buff. */
76           snprintf(buff, n, tempbuff, path);
77           free(tempbuff);
78 }
79 
80 
81 /*
82  * Strip v[1], replace %s in v[2] v[3] ... with the remainder
83  * of the path, and exec v[2] v[3] ... on the remainder.
84  */
85 int
portal_rfilter(struct portal_cred * pcr,char * key,char ** v,int * fdp)86 portal_rfilter(struct portal_cred *pcr, char *key, char **v, int *fdp)
87 {
88           char    cmd[FILTER_CMD_SIZE];
89           char   *path;
90           FILE   *fp;
91           int     error = 0;
92           char      percent_s[] = "%s";
93 
94           error = lose_credentials(pcr);
95           if (error != 0)
96                     return error;
97 
98 #ifdef DEBUG
99           fprintf(stderr, "rfilter:  Got key %s\n", key);
100 #endif
101 
102           if (!v[1] || !v[2]) {
103                     syslog(LOG_ERR,
104                         "rfilter: got strip-key of %s, and command start of %s\n",
105                         v[1], v[2]);
106                     exit(1);
107           }
108           /*
109            * Format for rfilter in config file:
110            *
111            * matchkey rfilter stripkey cmd [arg1] [arg2] ...
112            * any of arg1, arg2, etc. can have %s, in which case %s
113            * will be replaced by the full path.  If arg1 is
114            * missing, %s is assumed, i.e.
115            *   bogus1 rfilter bogus1/ cmd1
116            * is equivalent to
117            *   bogus1 rfilter bogus1/ cmd1 %s
118            */
119           /*
120            * v[3] could be NULL, or could point to "".
121            */
122           if (!v[3] || strlen(v[3]) == 0)
123                     v[3] = percent_s;   /* Handle above assumption. */
124           path = key;
125           /* Strip out stripkey if it matches leading part of key. */
126           if (!strncmp(v[1], key, strlen(v[1])))
127                     path += strlen(v[1]);
128           /*
129            * v[0] is key match, v[1] says how much to strip, v[2]
130            * is beginning of command proper.  The first %s in v[2]
131            * ... will be replaced with the path.
132            */
133           fill_cmd(v + 2, path, cmd, FILTER_CMD_SIZE);
134           if (strlen(cmd) >= FILTER_CMD_SIZE) {
135                     syslog(LOG_WARNING,
136                         "Warning:  potential overflow on string!  Length was %lu\n",
137                         (unsigned long)strlen(cmd));
138                     return ENAMETOOLONG;
139           }
140 #ifdef DEBUG
141           fprintf(stderr, "rfilter:  Using cmd of %s\n", cmd);
142 #endif
143           fp = popen(cmd, "r");
144           if (fp == NULL)
145                     return errno;
146 
147           /* Before returning, restore original uid and gid. */
148           /* But only do this if we were root to start with. */
149           if (getuid() == 0) {
150                     if ((seteuid((uid_t) 0) < 0) || (setegid((gid_t) 0) < 0)) {
151                               error = errno;
152                               syslog(LOG_WARNING, "setcred: %m");
153                               pclose(fp);
154                               fp = NULL;
155                     }
156           }
157           if (fp)
158                     fdp[0] = fileno(fp);
159           return error;
160 }
161 
162 int
portal_wfilter(struct portal_cred * pcr,char * key,char ** v,int * fdp)163 portal_wfilter(struct portal_cred *pcr, char *key, char **v, int *fdp)
164 {
165           char    cmd[FILTER_CMD_SIZE];
166           char   *path;
167           FILE   *fp;
168           int     error = 0;
169           int     cred_change_err = 0;
170 
171           cred_change_err = lose_credentials(pcr);
172           if (cred_change_err != 0)
173                     return cred_change_err;
174 
175           path = key + (v[1] ? strlen(v[1]) : 0);
176           /*
177            * v[0] is key match, v[1] says how much to strip, v[2]
178            * is beginning of command proper.
179            */
180           fill_cmd(v + 2, path, cmd, FILTER_CMD_SIZE);
181           if (strlen(cmd) >= FILTER_CMD_SIZE) {
182                     syslog(LOG_WARNING,
183                         "Warning:  potential overflow on string!  Length was %lu\n",
184                         (unsigned long)strlen(cmd));
185                     return ENAMETOOLONG;
186           }
187           fp = popen(cmd, "w");
188           if (fp == NULL) {
189                     return errno;
190           }
191           /* Before returning, restore original uid and gid. */
192           /* But only do this if we were root to start with. */
193           if (getuid() == 0) {
194                     if ((seteuid((uid_t) 0) < 0) || (setegid((gid_t) 0) < 0)) {
195                               error = errno;
196                               syslog(LOG_WARNING, "setcred: %m");
197                               pclose(fp);
198                               fp = NULL;
199                     }
200           }
201           if (fp)
202                     fdp[0] = fileno(fp);
203           return error;
204 }
205