1 /*        $NetBSD: login_cap.c,v 1.33 2015/10/29 20:29:24 kamil Exp $ */
2 
3 /*-
4  * Copyright (c) 1995,1997 Berkeley Software Design, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *        This product includes software developed by Berkeley Software Design,
17  *        Inc.
18  * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19  *    or promote products derived from this software without specific prior
20  *    written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *        BSDI login_cap.c,v 2.13 1998/02/07 03:17:05 prb Exp
35  */
36 
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 __RCSID("$NetBSD: login_cap.c,v 1.33 2015/10/29 20:29:24 kamil Exp $");
40 #endif /* LIBC_SCCS and not lint */
41 
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <sys/resource.h>
46 #include <sys/param.h>
47 
48 #include <assert.h>
49 #include <ctype.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <limits.h>
54 #include <login_cap.h>
55 #include <paths.h>
56 #include <pwd.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <syslog.h>
61 #include <unistd.h>
62 #include <util.h>
63 
64 static u_quad_t     multiply(u_quad_t, u_quad_t);
65 static u_quad_t     strtolimit(const char *, char **, int);
66 static u_quad_t     strtosize(const char *, char **, int);
67 static int          gsetrl(login_cap_t *, int, const char *, int type);
68 static int          isinfinite(const char *);
69 static int          envset(void *, const char *, const char *, int);
70 
71 login_cap_t *
login_getclass(const char * class)72 login_getclass(const char *class)
73 {
74           const char *classfiles[2];
75           login_cap_t *lc;
76           int res;
77 
78           /* class may be NULL */
79 
80           if (secure_path(_PATH_LOGIN_CONF) == 0) {
81                     classfiles[0] = _PATH_LOGIN_CONF;
82                     classfiles[1] = NULL;
83           } else {
84                     classfiles[0] = NULL;
85           }
86 
87           if ((lc = malloc(sizeof(login_cap_t))) == NULL) {
88                     syslog(LOG_ERR, "%s:%d malloc: %m", __FILE__, __LINE__);
89                     return (0);
90           }
91 
92           lc->lc_cap = 0;
93           lc->lc_style = 0;
94 
95           if (class == NULL || class[0] == '\0')
96                     class = LOGIN_DEFCLASS;
97 
98           if ((lc->lc_class = strdup(class)) == NULL) {
99                     syslog(LOG_ERR, "%s:%d strdup: %m", __FILE__, __LINE__);
100                     free(lc);
101                     return (0);
102           }
103 
104           /*
105            * Not having a login.conf file is not an error condition.
106            * The individual routines deal reasonably with missing
107            * capabilities and use default values.
108            */
109           if (classfiles[0] == NULL)
110                     return(lc);
111 
112           if ((res = cgetent(&lc->lc_cap, classfiles, lc->lc_class)) != 0) {
113                     lc->lc_cap = 0;
114                     switch (res) {
115                     case 1:
116                               syslog(LOG_ERR, "%s: couldn't resolve 'tc'",
117                                         lc->lc_class);
118                               break;
119                     case -1:
120                               if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == 0)
121                                         return (lc);
122                               syslog(LOG_ERR, "%s: unknown class", lc->lc_class);
123                               break;
124                     case -2:
125                               syslog(LOG_ERR, "%s: getting class information: %m",
126                                         lc->lc_class);
127                               break;
128                     case -3:
129                               syslog(LOG_ERR, "%s: 'tc' reference loop",
130                                         lc->lc_class);
131                               break;
132                     default:
133                               syslog(LOG_ERR, "%s: unexpected cgetent error",
134                                         lc->lc_class);
135                               break;
136                     }
137                     free(lc->lc_class);
138                     free(lc);
139                     return (0);
140           }
141           return (lc);
142 }
143 
144 login_cap_t *
login_getpwclass(const struct passwd * pwd)145 login_getpwclass(const struct passwd *pwd)
146 {
147 
148           /* pwd may be NULL */
149 
150           return login_getclass(pwd ? pwd->pw_class : NULL);
151 }
152 
153 char *
login_getcapstr(login_cap_t * lc,const char * cap,char * def,char * e)154 login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *e)
155 {
156           char *res = NULL;
157           int status;
158 
159           errno = 0;
160 
161           _DIAGASSERT(cap != NULL);
162 
163           if (!lc || !lc->lc_cap)
164                     return (def);
165 
166           switch (status = cgetstr(lc->lc_cap, cap, &res)) {
167           case -1:
168                     if (res)
169                               free(res);
170                     return (def);
171           case -2:
172                     syslog(LOG_ERR, "%s: getting capability %s: %m",
173                         lc->lc_class, cap);
174                     if (res)
175                               free(res);
176                     return (e);
177           default:
178                     if (status >= 0)
179                               return (res);
180                     syslog(LOG_ERR, "%s: unexpected error with capability %s",
181                         lc->lc_class, cap);
182                     if (res)
183                               free(res);
184                     return (e);
185           }
186 }
187 
188 quad_t
login_getcaptime(login_cap_t * lc,const char * cap,quad_t def,quad_t e)189 login_getcaptime(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
190 {
191           char *ep;
192           char *res = NULL, *sres;
193           int status;
194           quad_t q, r;
195 
196           _DIAGASSERT(cap != NULL);
197 
198           errno = 0;
199           if (!lc || !lc->lc_cap)
200                     return (def);
201 
202           switch (status = cgetstr(lc->lc_cap, cap, &res)) {
203           case -1:
204                     if (res)
205                               free(res);
206                     return (def);
207           case -2:
208                     syslog(LOG_ERR, "%s: getting capability %s: %m",
209                         lc->lc_class, cap);
210                     errno = ERANGE;
211                     if (res)
212                               free(res);
213                     return (e);
214           default:
215                     if (status >= 0)
216                               break;
217                     syslog(LOG_ERR, "%s: unexpected error with capability %s",
218                         lc->lc_class, cap);
219                     errno = ERANGE;
220                     if (res)
221                               free(res);
222                     return (e);
223           }
224 
225           if (isinfinite(res))
226                     return (RLIM_INFINITY);
227 
228           errno = 0;
229 
230           q = 0;
231           sres = res;
232           while (*res) {
233                     r = strtoq(res, &ep, 0);
234                     if (!ep || ep == res ||
235                         ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) {
236 invalid:
237                               syslog(LOG_ERR, "%s:%s=%s: invalid time",
238                                   lc->lc_class, cap, sres);
239                               errno = ERANGE;
240                               free(sres);
241                               return (e);
242                     }
243                     switch (*ep++) {
244                     case '\0':
245                               --ep;
246                               break;
247                     case 's': case 'S':
248                               break;
249                     case 'm': case 'M':
250                               r *= 60;
251                               break;
252                     case 'h': case 'H':
253                               r *= 60 * 60;
254                               break;
255                     case 'd': case 'D':
256                               r *= 60 * 60 * 24;
257                               break;
258                     case 'w': case 'W':
259                               r *= 60 * 60 * 24 * 7;
260                               break;
261                     case 'y': case 'Y': /* Pretty absurd */
262                               r *= 60 * 60 * 24 * 365;
263                               break;
264                     default:
265                               goto invalid;
266                     }
267                     res = ep;
268                     q += r;
269           }
270           free(sres);
271           return (q);
272 }
273 
274 quad_t
login_getcapnum(login_cap_t * lc,const char * cap,quad_t def,quad_t e)275 login_getcapnum(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
276 {
277           char *ep;
278           char *res = NULL;
279           int status;
280           quad_t q;
281 
282           _DIAGASSERT(cap != NULL);
283 
284           errno = 0;
285           if (!lc || !lc->lc_cap)
286                     return (def);
287 
288           switch (status = cgetstr(lc->lc_cap, cap, &res)) {
289           case -1:
290                     if (res)
291                               free(res);
292                     return (def);
293           case -2:
294                     syslog(LOG_ERR, "%s: getting capability %s: %m",
295                         lc->lc_class, cap);
296                     errno = ERANGE;
297                     if (res)
298                               free(res);
299                     return (e);
300           default:
301                     if (status >= 0)
302                               break;
303                     syslog(LOG_ERR, "%s: unexpected error with capability %s",
304                         lc->lc_class, cap);
305                     errno = ERANGE;
306                     if (res)
307                               free(res);
308                     return (e);
309           }
310 
311           if (isinfinite(res))
312                     return (RLIM_INFINITY);
313 
314           errno = 0;
315           q = strtoq(res, &ep, 0);
316           if (!ep || ep == res || ep[0] ||
317               ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
318                     syslog(LOG_ERR, "%s:%s=%s: invalid number",
319                         lc->lc_class, cap, res);
320                     errno = ERANGE;
321                     free(res);
322                     return (e);
323           }
324           free(res);
325           return (q);
326 }
327 
328 quad_t
login_getcapsize(login_cap_t * lc,const char * cap,quad_t def,quad_t e)329 login_getcapsize(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
330 {
331           char *ep;
332           char *res = NULL;
333           int status;
334           quad_t q;
335 
336           _DIAGASSERT(cap != NULL);
337 
338           errno = 0;
339 
340           if (!lc || !lc->lc_cap)
341                     return (def);
342 
343           switch (status = cgetstr(lc->lc_cap, cap, &res)) {
344           case -1:
345                     if (res)
346                               free(res);
347                     return (def);
348           case -2:
349                     syslog(LOG_ERR, "%s: getting capability %s: %m",
350                         lc->lc_class, cap);
351                     errno = ERANGE;
352                     if (res)
353                               free(res);
354                     return (e);
355           default:
356                     if (status >= 0)
357                               break;
358                     syslog(LOG_ERR, "%s: unexpected error with capability %s",
359                         lc->lc_class, cap);
360                     errno = ERANGE;
361                     if (res)
362                               free(res);
363                     return (e);
364           }
365 
366           errno = 0;
367           q = strtolimit(res, &ep, 0);
368           if (!ep || ep == res || (ep[0] && ep[1]) ||
369               ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
370                     syslog(LOG_ERR, "%s:%s=%s: invalid size",
371                         lc->lc_class, cap, res);
372                     errno = ERANGE;
373                     free(res);
374                     return (e);
375           }
376           free(res);
377           return (q);
378 }
379 
380 int
login_getcapbool(login_cap_t * lc,const char * cap,u_int def)381 login_getcapbool(login_cap_t *lc, const char *cap, u_int def)
382 {
383 
384           _DIAGASSERT(cap != NULL);
385 
386           if (!lc || !lc->lc_cap)
387                     return (def);
388 
389           return (cgetcap(lc->lc_cap, cap, ':') != NULL);
390 }
391 
392 void
login_close(login_cap_t * lc)393 login_close(login_cap_t *lc)
394 {
395 
396           if (lc) {
397                     if (lc->lc_class)
398                               free(lc->lc_class);
399                     if (lc->lc_cap)
400                               free(lc->lc_cap);
401                     if (lc->lc_style)
402                               free(lc->lc_style);
403                     free(lc);
404           }
405 }
406 
407 #define   R_CTIME   1
408 #define   R_CSIZE   2
409 #define   R_CNUMB   3
410 
411 static struct {
412           int       what;
413           int       type;
414           const char *name;
415 } r_list[] = {
416           { RLIMIT_CPU,                 R_CTIME, "cputime", },
417           { RLIMIT_FSIZE,               R_CSIZE, "filesize", },
418           { RLIMIT_DATA,                R_CSIZE, "datasize", },
419           { RLIMIT_STACK,               R_CSIZE, "stacksize", },
420           { RLIMIT_RSS,                 R_CSIZE, "memoryuse", },
421           { RLIMIT_MEMLOCK,   R_CSIZE, "memorylocked", },
422           { RLIMIT_NPROC,               R_CNUMB, "maxproc", },
423           { RLIMIT_NTHR,                R_CNUMB, "maxthread", },
424           { RLIMIT_NOFILE,    R_CNUMB, "openfiles", },
425           { RLIMIT_CORE,                R_CSIZE, "coredumpsize", },
426           { RLIMIT_SBSIZE,    R_CSIZE, "sbsize", },
427           { RLIMIT_AS,                  R_CSIZE, "vmemoryuse", },
428           { -1, 0, 0 }
429 };
430 
431 static int
gsetrl(login_cap_t * lc,int what,const char * name,int type)432 gsetrl(login_cap_t *lc, int what, const char *name, int type)
433 {
434           struct rlimit rl;
435           struct rlimit r;
436           char name_cur[32];
437           char name_max[32];
438 
439           _DIAGASSERT(name != NULL);
440 
441           (void)snprintf(name_cur, sizeof(name_cur), "%s-cur", name);
442           (void)snprintf(name_max, sizeof(name_max), "%s-max", name);
443 
444           if (getrlimit(what, &r)) {
445                     syslog(LOG_ERR, "getting resource limit: %m");
446                     return (-1);
447           }
448 
449 #define   RCUR      ((quad_t)r.rlim_cur)
450 #define   RMAX      ((quad_t)r.rlim_max)
451 
452           switch (type) {
453           case R_CTIME:
454                     r.rlim_cur = login_getcaptime(lc, name, RCUR, RCUR);
455                     r.rlim_max = login_getcaptime(lc, name, RMAX, RMAX);
456                     rl.rlim_cur = login_getcaptime(lc, name_cur, RCUR, RCUR);
457                     rl.rlim_max = login_getcaptime(lc, name_max, RMAX, RMAX);
458                     break;
459           case R_CSIZE:
460                     r.rlim_cur = login_getcapsize(lc, name, RCUR, RCUR);
461                     r.rlim_max = login_getcapsize(lc, name, RMAX, RMAX);
462                     rl.rlim_cur = login_getcapsize(lc, name_cur, RCUR, RCUR);
463                     rl.rlim_max = login_getcapsize(lc, name_max, RMAX, RMAX);
464                     break;
465           case R_CNUMB:
466                     r.rlim_cur = login_getcapnum(lc, name, RCUR, RCUR);
467                     r.rlim_max = login_getcapnum(lc, name, RMAX, RMAX);
468                     rl.rlim_cur = login_getcapnum(lc, name_cur, RCUR, RCUR);
469                     rl.rlim_max = login_getcapnum(lc, name_max, RMAX, RMAX);
470                     break;
471           default:
472                     syslog(LOG_ERR, "%s: invalid type %d setting resource limit %s",
473                         lc->lc_class, type, name);
474                     return (-1);
475           }
476 
477           if (setrlimit(what, &rl)) {
478                     syslog(LOG_ERR, "%s: setting resource limit %s: %m",
479                         lc->lc_class, name);
480                     return (-1);
481           }
482 #undef    RCUR
483 #undef    RMAX
484           return (0);
485 }
486 
487 static int
488 /*ARGSUSED*/
envset(void * envp __unused,const char * name,const char * value,int overwrite)489 envset(void *envp __unused, const char *name, const char *value, int overwrite)
490 {
491           return setenv(name, value, overwrite);
492 }
493 
494 int
setuserenv(login_cap_t * lc,envfunc_t senv,void * envp)495 setuserenv(login_cap_t *lc, envfunc_t senv, void *envp)
496 {
497           const char *stop = ", \t";
498           size_t i, count;
499           char *ptr;
500           char **res;
501           char *str = login_getcapstr(lc, "setenv", NULL, NULL);
502 
503           if (str == NULL || *str == '\0')
504                     return 0;
505 
506           /*
507            * count the sub-strings, this may over-count since we don't
508            * account for escaped delimiters.
509            */
510           for (i = 1, ptr = str; *ptr; i++) {
511                     ptr += strcspn(ptr, stop);
512                     if (*ptr)
513                               ptr++;
514           }
515 
516           /* allocate ptr array and string */
517           count = i;
518           res = malloc(count * sizeof(*res) + strlen(str) + 1);
519 
520           if (!res)
521                     return -1;
522 
523           ptr = (char *)(void *)&res[count];
524           (void)strcpy(ptr, str);
525 
526           /* split string */
527           for (i = 0; (res[i] = stresep(&ptr, stop, '\\')) != NULL; )
528                     if (*res[i])
529                               i++;
530 
531           count = i;
532 
533           for (i = 0; i < count; i++) {
534                     if ((ptr = strchr(res[i], '=')) != NULL)
535                               *ptr++ = '\0';
536                     else
537                               ptr = NULL;
538                     (void)(*senv)(envp, res[i], ptr ? ptr : "", 1);
539           }
540 
541           free(res);
542           return 0;
543 }
544 
545 int
setclasscontext(const char * class,u_int flags)546 setclasscontext(const char *class, u_int flags)
547 {
548           int ret;
549           login_cap_t *lc;
550 
551           flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK |
552               LOGIN_SETPATH;
553 
554           lc = login_getclass(class);
555           ret = lc ? setusercontext(lc, NULL, 0, flags) : -1;
556           login_close(lc);
557           return (ret);
558 }
559 
560 int
setusercontext(login_cap_t * lc,struct passwd * pwd,uid_t uid,u_int flags)561 setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags)
562 {
563           char per_user_tmp[MAXPATHLEN + 1];
564           const char *component_name;
565           login_cap_t *flc;
566           quad_t p;
567           int i;
568           ssize_t len;
569 
570           flc = NULL;
571 
572           if (!lc)
573                     flc = lc = login_getclass(pwd ? pwd->pw_class : NULL);
574 
575           /*
576            * Without the pwd entry being passed we cannot set either
577            * the group or the login.  We could complain about it.
578            */
579           if (pwd == NULL)
580                     flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
581 
582 #ifdef LOGIN_OSETGROUP
583           if (pwd == NULL)
584                     flags &= ~LOGIN_OSETGROUP;
585           if (flags & LOGIN_OSETGROUP)
586                     flags = (flags & ~LOGIN_OSETGROUP) | LOGIN_SETGROUP;
587 #endif
588           if (flags & LOGIN_SETRESOURCES)
589                     for (i = 0; r_list[i].name; ++i)
590                               (void)gsetrl(lc, r_list[i].what, r_list[i].name,
591                                   r_list[i].type);
592 
593           if (flags & LOGIN_SETPRIORITY) {
594                     p = login_getcapnum(lc, "priority", (quad_t)0, (quad_t)0);
595 
596                     if (setpriority(PRIO_PROCESS, 0, (int)p) == -1)
597                               syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class);
598           }
599 
600           if (flags & LOGIN_SETUMASK) {
601                     p = login_getcapnum(lc, "umask", (quad_t) LOGIN_DEFUMASK,
602                         (quad_t)LOGIN_DEFUMASK);
603                     umask((mode_t)p);
604           }
605 
606           if (flags & LOGIN_SETGID) {
607                     if (setgid(pwd->pw_gid) == -1) {
608                               syslog(LOG_ERR, "setgid(%d): %m", pwd->pw_gid);
609                               login_close(flc);
610                               return (-1);
611                     }
612           }
613 
614           if (flags & LOGIN_SETGROUPS) {
615                     if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
616                               syslog(LOG_ERR, "initgroups(%s,%d): %m",
617                                   pwd->pw_name, pwd->pw_gid);
618                               login_close(flc);
619                               return (-1);
620                     }
621           }
622 
623           /* Create per-user temporary directories if needed. */
624           if ((len = readlink("/tmp", per_user_tmp,
625               sizeof(per_user_tmp) - 6)) != -1) {
626 
627                     static const char atuid[] = "/@ruid";
628                     char *lp;
629 
630                     /* readlink does not nul-terminate the string */
631                     per_user_tmp[len] = '\0';
632 
633                     /* Check if it's magic symlink. */
634                     lp = strstr(per_user_tmp, atuid);
635                     if (lp != NULL && *(lp + (sizeof(atuid) - 1)) == '\0') {
636                               lp++;
637 
638                               if (snprintf(lp, 11, "/%u", pwd->pw_uid) > 10) {
639                                         syslog(LOG_ERR, "real temporary path too long");
640                                         login_close(flc);
641                                         return (-1);
642                               }
643                               if (mkdir(per_user_tmp, S_IRWXU) != -1) {
644                                         if (chown(per_user_tmp, pwd->pw_uid,
645                                             pwd->pw_gid)) {
646                                                   component_name = "chown";
647                                                   goto out;
648                                         }
649 
650                                         /*
651                                          * Must set sticky bit for tmp directory, some
652                                          * programs rely on this.
653                                          */
654                                         if(chmod(per_user_tmp, S_IRWXU | S_ISVTX)) {
655                                                   component_name = "chmod";
656                                                   goto out;
657                                         }
658                               } else {
659                                         if (errno != EEXIST) {
660                                                   component_name = "mkdir";
661                                                   goto out;
662                                         } else {
663                                                   /*
664                                                    * We must ensure that we own the
665                                                    * directory and that is has the correct
666                                                    * permissions, otherwise a DOS attack
667                                                    * is possible.
668                                                    */
669                                                   struct stat sb;
670                                                   if (stat(per_user_tmp, &sb) == -1) {
671                                                             component_name = "stat";
672                                                             goto out;
673                                                   }
674 
675                                                   if (sb.st_uid != pwd->pw_uid) {
676                                                             if (chown(per_user_tmp,
677                                                                 pwd->pw_uid, pwd->pw_gid)) {
678                                                                       component_name = "chown";
679                                                                       goto out;
680                                                             }
681                                                   }
682 
683                                                   if (sb.st_mode != (S_IRWXU | S_ISVTX)) {
684                                                             if (chmod(per_user_tmp,
685                                                                 S_IRWXU | S_ISVTX)) {
686                                                                       component_name = "chmod";
687                                                                       goto out;
688                                                             }
689                                                   }
690                                         }
691                               }
692                     }
693           }
694           errno = 0;
695 
696           if (flags & LOGIN_SETLOGIN)
697                     if (setlogin(pwd->pw_name) == -1) {
698                               syslog(LOG_ERR, "setlogin(%s) failure: %m",
699                                   pwd->pw_name);
700                               login_close(flc);
701                               return (-1);
702                     }
703 
704           if (flags & LOGIN_SETUSER)
705                     if (setuid(uid) == -1) {
706                               syslog(LOG_ERR, "setuid(%d): %m", uid);
707                               login_close(flc);
708                               return (-1);
709                     }
710 
711           if (flags & LOGIN_SETENV)
712                     setuserenv(lc, envset, NULL);
713 
714           if (flags & LOGIN_SETPATH)
715                     setuserpath(lc, pwd ? pwd->pw_dir : "", envset, NULL);
716 
717           login_close(flc);
718           return (0);
719 
720 out:
721           if (component_name != NULL) {
722                     syslog(LOG_ERR, "%s %s: %m", component_name, per_user_tmp);
723                     login_close(flc);
724                     return (-1);
725           } else {
726                     syslog(LOG_ERR, "%s: %m", per_user_tmp);
727                     login_close(flc);
728                     return (-1);
729           }
730 }
731 
732 void
setuserpath(login_cap_t * lc,const char * home,envfunc_t senv,void * envp)733 setuserpath(login_cap_t *lc, const char *home, envfunc_t senv, void *envp)
734 {
735           size_t hlen, plen;
736           int cnt = 0;
737           char *path;
738           const char *cpath;
739           char *p, *q;
740 
741           _DIAGASSERT(home != NULL);
742 
743           hlen = strlen(home);
744 
745           p = path = login_getcapstr(lc, "path", NULL, NULL);
746           if (p) {
747                     while (*p)
748                               if (*p++ == '~')
749                                         ++cnt;
750                     plen = (p - path) + cnt * (hlen + 1) + 1;
751                     p = path;
752                     q = path = malloc(plen);
753                     if (q) {
754                               while (*p) {
755                                         p += strspn(p, " \t");
756                                         if (*p == '\0')
757                                                   break;
758                                         plen = strcspn(p, " \t");
759                                         if (hlen == 0 && *p == '~') {
760                                                   p += plen;
761                                                   continue;
762                                         }
763                                         if (q != path)
764                                                   *q++ = ':';
765                                         if (*p == '~') {
766                                                   strcpy(q, home);
767                                                   q += hlen;
768                                                   ++p;
769                                                   --plen;
770                                         }
771                                         memcpy(q, p, plen);
772                                         p += plen;
773                                         q += plen;
774                               }
775                               *q = '\0';
776                               cpath = path;
777                     } else
778                               cpath = _PATH_DEFPATH;
779           } else
780                     cpath = _PATH_DEFPATH;
781           if ((*senv)(envp, "PATH", cpath, 1))
782                     warn("could not set PATH");
783 }
784 
785 /*
786  * Convert an expression of the following forms
787  *        1) A number.
788  *        2) A number followed by a b (mult by 512).
789  *        3) A number followed by a k (mult by 1024).
790  *        5) A number followed by a m (mult by 1024 * 1024).
791  *        6) A number followed by a g (mult by 1024 * 1024 * 1024).
792  *        7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
793  *        8) Two or more numbers (with/without k,b,m,g, or t).
794  *           separated by x (also * for backwards compatibility), specifying
795  *           the product of the indicated values.
796  */
797 static u_quad_t
strtosize(const char * str,char ** endptr,int radix)798 strtosize(const char *str, char **endptr, int radix)
799 {
800           u_quad_t num, num2;
801           char *expr, *expr2;
802 
803           _DIAGASSERT(str != NULL);
804           /* endptr may be NULL */
805 
806           errno = 0;
807           num = strtouq(str, &expr, radix);
808           if (errno || expr == str) {
809                     if (endptr)
810                               *endptr = expr;
811                     return (num);
812           }
813 
814           switch(*expr) {
815           case 'b': case 'B':
816                     num = multiply(num, (u_quad_t)512);
817                     ++expr;
818                     break;
819           case 'k': case 'K':
820                     num = multiply(num, (u_quad_t)1024);
821                     ++expr;
822                     break;
823           case 'm': case 'M':
824                     num = multiply(num, (u_quad_t)1024 * 1024);
825                     ++expr;
826                     break;
827           case 'g': case 'G':
828                     num = multiply(num, (u_quad_t)1024 * 1024 * 1024);
829                     ++expr;
830                     break;
831           case 't': case 'T':
832                     num = multiply(num, (u_quad_t)1024 * 1024);
833                     num = multiply(num, (u_quad_t)1024 * 1024);
834                     ++expr;
835                     break;
836           }
837 
838           if (errno)
839                     goto erange;
840 
841           switch(*expr) {
842           case '*':                     /* Backward compatible. */
843           case 'x':
844                     num2 = strtosize(expr+1, &expr2, radix);
845                     if (errno) {
846                               expr = expr2;
847                               goto erange;
848                     }
849 
850                     if (expr2 == expr + 1) {
851                               if (endptr)
852                                         *endptr = expr;
853                               return (num);
854                     }
855                     expr = expr2;
856                     num = multiply(num, num2);
857                     if (errno)
858                               goto erange;
859                     break;
860           }
861           if (endptr)
862                     *endptr = expr;
863           return (num);
864 erange:
865           if (endptr)
866                     *endptr = expr;
867           errno = ERANGE;
868           return (UQUAD_MAX);
869 }
870 
871 static u_quad_t
strtolimit(const char * str,char ** endptr,int radix)872 strtolimit(const char *str, char **endptr, int radix)
873 {
874 
875           _DIAGASSERT(str != NULL);
876           /* endptr may be NULL */
877 
878           if (isinfinite(str)) {
879                     if (endptr)
880                               *endptr = (char *)__UNCONST(str) + strlen(str);
881                     return ((u_quad_t)RLIM_INFINITY);
882           }
883           return (strtosize(str, endptr, radix));
884 }
885 
886 static int
isinfinite(const char * s)887 isinfinite(const char *s)
888 {
889           static const char *infs[] = {
890                     "infinity",
891                     "inf",
892                     "unlimited",
893                     "unlimit",
894                     NULL
895           };
896           const char **i;
897 
898           _DIAGASSERT(s != NULL);
899 
900           for (i = infs; *i; i++) {
901                     if (!strcasecmp(s, *i))
902                               return 1;
903           }
904           return 0;
905 }
906 
907 static u_quad_t
multiply(u_quad_t n1,u_quad_t n2)908 multiply(u_quad_t n1, u_quad_t n2)
909 {
910           static int bpw = 0;
911           u_quad_t m;
912           u_quad_t r;
913           int b1, b2;
914 
915           /*
916            * Get rid of the simple cases
917            */
918           if (n1 == 0 || n2 == 0)
919                     return (0);
920           if (n1 == 1)
921                     return (n2);
922           if (n2 == 1)
923                     return (n1);
924 
925           /*
926            * sizeof() returns number of bytes needed for storage.
927            * This may be different from the actual number of useful bits.
928            */
929           if (!bpw) {
930                     bpw = sizeof(u_quad_t) * 8;
931                     while (((u_quad_t)1 << (bpw-1)) == 0)
932                               --bpw;
933           }
934 
935           /*
936            * First check the magnitude of each number.  If the sum of the
937            * magnitude is to high, reject the number.  (If this test
938            * is not done then the first multiply below may overflow.)
939            */
940           for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
941                     ;
942           for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
943                     ;
944           if (b1 + b2 - 2 > bpw) {
945                     errno = ERANGE;
946                     return (UQUAD_MAX);
947           }
948 
949           /*
950            * Decompose the multiplication to be:
951            * h1 = n1 & ~1
952            * h2 = n2 & ~1
953            * l1 = n1 & 1
954            * l2 = n2 & 1
955            * (h1 + l1) * (h2 + l2)
956            * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
957            *
958            * Since h1 && h2 do not have the low bit set, we can then say:
959            *
960            * (h1>>1 * h2>>1 * 4) + ...
961            *
962            * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
963            * overflow.
964            *
965            * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
966            * then adding in residual amount will cause an overflow.
967            */
968 
969           m = (n1 >> 1) * (n2 >> 1);
970 
971           if (m >= ((u_quad_t)1 << (bpw-2))) {
972                     errno = ERANGE;
973                     return (UQUAD_MAX);
974           }
975 
976           m *= 4;
977 
978           r = (n1 & n2 & 1)
979             + (n2 & 1) * (n1 & ~(u_quad_t)1)
980             + (n1 & 1) * (n2 & ~(u_quad_t)1);
981 
982           if ((u_quad_t)(m + r) < m) {
983                     errno = ERANGE;
984                     return (UQUAD_MAX);
985           }
986           m += r;
987 
988           return (m);
989 }
990