Merge from vendor branch GPERF:
[dragonfly.git] / contrib / smbfs / lib / smb / subr.c
1 /*
2  * Copyright (c) 2000, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: subr.c,v 1.12 2001/08/22 03:31:37 bp Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/errno.h>
38 #include <sys/sysctl.h>
39 #include <sys/syscall.h>
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <err.h>
47
48 #include <netsmb/netbios.h>
49 #include <netsmb/smb_lib.h>
50 #include <netsmb/nb_lib.h>
51 #include <cflib.h>
52
53 #ifdef APPLE
54 #include <sysexits.h>
55 #include <sys/wait.h>
56 #include <mach/mach.h>
57 #include <mach/mach_error.h>
58
59 uid_t real_uid, eff_uid;
60 #endif
61
62 extern char *__progname;
63
64 static int smblib_initialized;
65
66 struct rcfile *smb_rc;
67
68 int
69 smb_lib_init(void)
70 {
71         int error;
72         int kv;
73         size_t kvlen = sizeof(kv);
74
75         if (smblib_initialized)
76                 return 0;
77 #if __FreeBSD_version > 400000
78         error = sysctlbyname("net.smb.version", &kv, &kvlen, NULL, 0);
79         if (error) {
80                 warnx("%s: can't find kernel module\n", __FUNCTION__);
81                 return error;
82         }
83         if (NSMB_VERSION != kv) {
84                 warnx("%s: kernel module version(%d) don't match library(%d).\n", __FUNCTION__, kv, NSMB_VERSION);
85                 return EINVAL;
86         }
87 #endif
88         if ((error = nls_setlocale("")) != 0) {
89                 warnx("%s: can't initialise locale\n", __FUNCTION__);
90                 return error;
91         }
92         smblib_initialized++;
93         return 0;
94 }
95
96 /*
97  * Print a (descriptive) error message
98  * error values:
99  *         0 - no specific error code available;
100  *  1..32767 - system error
101  */
102 void
103 smb_error(const char *fmt, int error,...) {
104         va_list ap;
105         const char *cp;
106         int errtype = error & SMB_ERRTYPE_MASK;
107
108         fprintf(stderr, "%s: ", __progname);
109         va_start(ap, error);
110         vfprintf(stderr, fmt, ap);
111         va_end(ap);
112         if (error == -1)
113                 error = errno;
114         else
115                 error &= ~SMB_ERRTYPE_MASK;
116         switch (errtype) {
117             case SMB_SYS_ERROR:
118                 if (error)
119                         fprintf(stderr, ": syserr = %s\n", strerror(error));
120                 else
121                         fprintf(stderr, "\n");
122                 break;
123             case SMB_RAP_ERROR:
124                 fprintf(stderr, ": raperr = %d (0x%04x)\n", error, error);
125                 break;
126             case SMB_NB_ERROR:
127                 cp = nb_strerror(error);
128                 if (cp == NULL)
129                         fprintf(stderr, ": nberr = unknown (0x%04x)\n", error);
130                 else
131                         fprintf(stderr, ": nberr = %s\n", cp);
132                 break;
133             default:
134                 fprintf(stderr, "\n");
135         }
136 }
137
138 char *
139 smb_printb(char *dest, int flags, const struct smb_bitname *bnp) {
140         int first = 1;
141
142         strcpy(dest, "<");
143         for(; bnp->bn_bit; bnp++) {
144                 if (flags & bnp->bn_bit) {
145                         strcat(dest, bnp->bn_name);
146                         first = 0;
147                 }
148                 if (!first && (flags & bnp[1].bn_bit))
149                         strcat(dest, "|");
150         }
151         strcat(dest, ">");
152         return dest;
153 }
154
155 /*
156  * first read ~/.smbrc, next try to merge SMB_CFG_FILE
157  */
158 int
159 smb_open_rcfile(void)
160 {
161         char *home, *fn;
162         int error;
163
164         home = getenv("HOME");
165         if (home) {
166                 fn = malloc(strlen(home) + 20);
167                 sprintf(fn, "%s/.nsmbrc", home);
168                 error = rc_open(fn, "r", &smb_rc);
169                 free(fn);
170         }
171         error = rc_merge(SMB_CFG_FILE, &smb_rc);
172         if (smb_rc == NULL) {
173                 printf("Warning: no cfg file(s) found.\n");
174                 return ENOENT;
175         }
176         return 0;
177 }
178
179 void *
180 smb_dumptree(void)
181 {
182         size_t len;
183         void *p;
184         int error;
185         
186 #ifdef APPLE
187         seteuid(eff_uid); /* restore setuid root briefly */
188 #endif
189         error = sysctlbyname("net.smb.treedump", NULL, &len, NULL, 0);
190 #ifdef APPLE
191         seteuid(real_uid); /* and back to real user */
192 #endif
193         if (error)
194                 return NULL;
195         p = malloc(len);
196         if (p == NULL)
197                 return NULL;
198 #ifdef APPLE
199         seteuid(eff_uid); /* restore setuid root briefly */
200 #endif
201         error = sysctlbyname("net.smb.treedump", p, &len, NULL, 0);
202 #ifdef APPLE
203         seteuid(real_uid); /* and back to real user */
204 #endif
205         if (error) {
206                 free(p);
207                 return NULL;
208         }
209         return p;
210 }
211
212 char *
213 smb_simplecrypt(char *dst, const char *src)
214 {
215         int ch, pos;
216         char *dp;
217
218         if (dst == NULL) {
219                 dst = malloc(4 + 2 * strlen(src));
220                 if (dst == NULL)
221                         return NULL;
222         }
223         dp = dst;
224         *dst++ = '$';
225         *dst++ = '$';
226         *dst++ = '1';
227         pos = 27;
228         while (*src) {
229                 ch = *src++;
230                 if (isascii(ch))
231                     ch = (isupper(ch) ? ('A' + (ch - 'A' + 13) % 26) :
232                           islower(ch) ? ('a' + (ch - 'a' + 13) % 26) : ch);
233                 ch ^= pos;
234                 pos += 13;
235                 sprintf(dst, "%02x", ch);
236                 dst += 2;
237         }
238         *dst = 0;
239         return dp;
240 }
241
242 int
243 smb_simpledecrypt(char *dst, const char *src)
244 {
245         char *ep, hexval[3];
246         int len, ch, pos;
247
248         if (strncmp(src, "$$1", 3) != 0)
249                 return EINVAL;
250         src += 3;
251         len = strlen(src);
252         if (len & 1)
253                 return EINVAL;
254         len /= 2;
255         hexval[2] = 0;
256         pos = 27;
257         while (len--) {
258                 hexval[0] = *src++;
259                 hexval[1] = *src++;
260                 ch = strtoul(hexval, &ep, 16);
261                 if (*ep != 0)
262                         return EINVAL;
263                 ch ^= pos;
264                 pos += 13;
265                 if (isascii(ch))
266                     ch = (isupper(ch) ? ('A' + (ch - 'A' + 13) % 26) :
267                           islower(ch) ? ('a' + (ch - 'a' + 13) % 26) : ch);
268                 *dst++ = ch;
269         }
270         *dst = 0;
271         return 0;
272 }
273
274
275 #ifdef APPLE
276 static int
277 safe_execv(char *args[])
278 {       
279         int          pid;   
280         union wait      status;
281         
282         pid = fork();  
283         if (pid == 0) {
284                 (void)execv(args[0], args);
285                 errx(EX_OSERR, "%s: execv %s failed, %s\n", __progname,
286                      args[0], strerror(errno));
287         }
288         if (pid == -1) {
289                 fprintf(stderr, "%s: fork failed, %s\n", __progname,
290                         strerror(errno));
291                 return (1);
292         }
293         if (wait4(pid, (int *)&status, 0, NULL) != pid) {
294                 fprintf(stderr, "%s: BUG executing %s command\n", __progname,
295                         args[0]);  
296                 return (1);
297         } else if (!WIFEXITED(status)) {
298                 fprintf(stderr, "%s: %s command aborted by signal %d\n",
299                         __progname, args[0], WTERMSIG(status));
300                 return (1);
301         } else if (WEXITSTATUS(status)) {
302                 fprintf(stderr, "%s: %s command failed, exit status %d: %s\n",
303                         __progname, args[0], WEXITSTATUS(status),
304                         strerror(WEXITSTATUS(status)));
305                 return (1);
306         }       
307         return (0);
308 }       
309
310
311 void
312 dropsuid()
313 {
314         /* drop setuid root privs asap */
315         eff_uid = geteuid();
316         real_uid = getuid();
317         seteuid(real_uid);
318         return;
319 }
320
321
322 static int
323 kextisloaded(char * kextname)
324 {
325         mach_port_t kernel_port;
326         kmod_info_t *k, *loaded_modules = 0;
327         int err, loaded_count = 0;
328
329         /* on error return not loaded - to make loadsmbvfs fail */
330
331         err = task_for_pid(mach_task_self(), 0, &kernel_port);
332         if (err) {
333                 fprintf(stderr, "%s: %s: %s\n", __progname,
334                         "unable to get kernel task port",
335                         mach_error_string(err));
336                 return (0);
337         }
338         err = kmod_get_info(kernel_port, (void *)&loaded_modules,
339                             &loaded_count); /* never freed */
340         if (err) {
341                 fprintf(stderr, "%s: %s: %s\n", __progname,
342                         "kmod_get_info() failed",
343                         mach_error_string(err));
344                 return (0);
345         }
346         for (k = loaded_modules; k; k = k->next ? k+1 : 0)
347                 if (!strcmp(k->name, kextname))
348                         return (1);
349         return (0);
350 }
351
352
353 #define KEXTLOAD_COMMAND        "/sbin/kextload"
354 #define FS_KEXT_DIR             "/System/Library/Extensions/smbfs.kext"
355 #define FULL_KEXTNAME           "com.apple.filesystems.smbfs"
356
357
358 int
359 loadsmbvfs()
360 {       
361         const char *kextargs[] = {KEXTLOAD_COMMAND, FS_KEXT_DIR, NULL};
362         int error = 0;
363
364         /*
365          * temporarily revert to root (required for kextload)
366          */
367         seteuid(eff_uid);
368         if (!kextisloaded(FULL_KEXTNAME)) {
369                 error = safe_execv(kextargs);
370                 if (!error)
371                         error = !kextisloaded(FULL_KEXTNAME);
372         }
373         seteuid(real_uid); /* and back to real user */
374         return (error);
375 }       
376 #endif /* APPLE */