]> granicus.if.org Git - linux-pam/blob - modules/pam_limits/pam_limits.c
Relevant BUGIDs:
[linux-pam] / modules / pam_limits / pam_limits.c
1 /*
2  * pam_limits - impose resource limits when opening a user session
3  *
4  * 1.6 - modified for PLD (added process priority settings)
5  *       by Marcin Korzonek <mkorz@shadow.eu.org>
6  * 1.5 - Elliot Lee's "max system logins patch"
7  * 1.4 - addressed bug in configuration file parser
8  * 1.3 - modified the configuration file format
9  * 1.2 - added 'debug' and 'conf=' arguments
10  * 1.1 - added @group support
11  * 1.0 - initial release - Linux ONLY
12  *
13  * See end for Copyright information
14  */
15
16 #if !(defined(linux))
17 #error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
18 #endif
19
20 #include <security/_pam_aconf.h>
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <syslog.h>
29 #include <stdarg.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/resource.h>
33
34 #include <utmp.h>
35 #ifndef UT_USER  /* some systems have ut_name instead of ut_user */
36 #define UT_USER ut_user
37 #endif
38
39 #include <grp.h>
40 #include <pwd.h>
41
42 /* Module defines */
43 #define LINE_LENGTH 1024
44
45 #define LIMITS_DEF_USER     0 /* limit was set by an user entry */
46 #define LIMITS_DEF_GROUP    1 /* limit was set by a group entry */
47 #define LIMITS_DEF_DEFAULT  2 /* limit was set by an default entry */
48 #define LIMITS_DEF_NONE     3 /* this limit was not set yet */
49 #define LIMITS_DEF_ALL      4 /* limit was set by an default entry */
50 #define LIMITS_DEF_ALLGROUP 5 /* limit was set by a group entry */
51
52 static const char *limits_def_names[] = {
53        "USER",
54        "GROUP",
55        "DEFAULT",
56        "NONE",
57        "ALL",
58        "ALLGROUP",
59        NULL,
60 };
61
62 struct user_limits_struct {
63     int src_soft;
64     int src_hard;
65     struct rlimit limit;
66 };
67
68 /* internal data */
69 struct pam_limit_s {
70     int login_limit;     /* the max logins limit */
71     int login_limit_def; /* which entry set the login limit */
72     int flag_numsyslogins; /* whether to limit logins only for a
73                               specific user or to count all logins */
74     int priority;        /* the priority to run user process with */
75     int supported[RLIM_NLIMITS];
76     struct user_limits_struct limits[RLIM_NLIMITS];
77     char conf_file[BUFSIZ];
78     int utmp_after_pam_call;
79 };
80
81 #define LIMIT_LOGIN RLIM_NLIMITS+1
82 #define LIMIT_NUMSYSLOGINS RLIM_NLIMITS+2
83
84 #define LIMIT_PRI RLIM_NLIMITS+3
85
86 #define LIMIT_SOFT  1
87 #define LIMIT_HARD  2
88
89 #define PAM_SM_SESSION
90
91 #include <security/pam_modules.h>
92 #include <security/_pam_macros.h>
93 #include <security/_pam_modutil.h>
94
95 /* logging */
96 static void _pam_log(int err, const char *format, ...)
97 {
98     va_list args;
99
100     va_start(args, format);
101     openlog("pam_limits", LOG_CONS|LOG_PID, LOG_AUTH);
102     vsyslog(err, format, args);
103     va_end(args);
104     closelog();
105 }
106
107 /* argument parsing */
108
109 #define PAM_DEBUG_ARG       0x0001
110 #define PAM_DO_SETREUID     0x0002
111 #define PAM_UTMP_EARLY      0x0004
112
113 static int _pam_parse(int argc, const char **argv, struct pam_limit_s *pl)
114 {
115     int ctrl=0;
116
117     /* step through arguments */
118     for (ctrl=0; argc-- > 0; ++argv) {
119
120         /* generic options */
121
122         if (!strcmp(*argv,"debug")) {
123             ctrl |= PAM_DEBUG_ARG;
124         } else if (!strncmp(*argv,"conf=",5)) {
125             strncpy(pl->conf_file,*argv+5,sizeof(pl->conf_file)-1);
126         } else if (!strncmp(*argv,"change_uid",10)) {
127             ctrl |= PAM_DO_SETREUID;
128         } else if (!strcmp(*argv,"utmp_early")) {
129             ctrl |= PAM_UTMP_EARLY;
130         } else {
131             _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
132         }
133     }
134     pl->conf_file[sizeof(pl->conf_file) - 1] = '\0';
135
136     return ctrl;
137 }
138
139
140 /* limits stuff */
141 #ifdef DEFAULT_CONF_FILE
142 # define LIMITS_FILE DEFAULT_CONF_FILE
143 #else
144 # define LIMITS_FILE "/etc/security/limits.conf"
145 #endif
146
147 #define LIMITED_OK 0 /* limit setting appeared to work */
148 #define LIMIT_ERR  1 /* error setting a limit */
149 #define LOGIN_ERR  2 /* too many logins err */
150
151 /* Counts the number of user logins and check against the limit*/
152 static int
153 check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl,
154               struct pam_limit_s *pl)
155 {
156     struct utmp *ut;
157     unsigned int count;
158
159     if (ctrl & PAM_DEBUG_ARG) {
160         _pam_log(LOG_DEBUG, "checking logins for '%s' (maximum of %d)\n",
161                  name, limit);
162     }
163
164     if (limit < 0)
165         return 0; /* no limits imposed */
166     if (limit == 0) /* maximum 0 logins ? */ {
167         _pam_log(LOG_WARNING, "No logins allowed for '%s'\n", name);
168         return LOGIN_ERR;
169     }
170
171     setutent();
172
173     /* Because there is no definition about when an application
174        actually adds a utmp entry, some applications bizarrely do the
175        utmp call before the have PAM authenticate them to the system:
176        you're logged it, sort of...? Anyway, you can use the
177        "utmp_early" module argument in your PAM config file to make
178        allowances for this sort of problem. (There should be a PAM
179        standard for this, since if a module wants to actually map a
180        username then any early utmp entry will be for the unmapped
181        name = broken.) */
182
183     if (ctrl & PAM_UTMP_EARLY) {
184         count = 0;
185     } else {
186         count = 1;
187     }
188
189     while((ut = getutent())) {
190 #ifdef USER_PROCESS
191         if (ut->ut_type != USER_PROCESS) {
192             continue;
193         }
194 #endif
195         if (ut->UT_USER[0] == '\0') {
196             continue;
197         }
198         if (!pl->flag_numsyslogins) {
199             if (((pl->login_limit_def == LIMITS_DEF_USER)
200                  || (pl->login_limit_def == LIMITS_DEF_GROUP)
201                  || (pl->login_limit_def == LIMITS_DEF_DEFAULT))
202                 && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0) {
203                 continue;
204             }
205             if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP)
206                 && !_pammodutil_user_in_group_nam_nam(pamh, ut->UT_USER, name)) {
207                 continue;
208             }
209         }
210         if (++count > limit) {
211             break;
212         }
213     }
214     endutent();
215     if (count > limit) {
216         if (name) {
217             _pam_log(LOG_WARNING, "Too many logins (max %d) for %s",
218                      limit, name);
219         } else {
220             _pam_log(LOG_WARNING, "Too many system logins (max %d)", limit);
221         }
222         return LOGIN_ERR;
223     }
224     return 0;
225 }
226
227 static int init_limits(struct pam_limit_s *pl)
228 {
229     int i;
230     int retval = PAM_SUCCESS;
231
232     D(("called."));
233
234     for(i = 0; i < RLIM_NLIMITS; i++) {
235         int r = getrlimit(i, &pl->limits[i].limit);
236         if (r == -1) {
237             if (errno == EINVAL) {
238                 pl->supported[i] = 0;
239             } else {
240                 retval = !PAM_SUCCESS;
241             }
242         } else {
243             pl->supported[i] = 1;
244             pl->limits[i].src_soft = LIMITS_DEF_NONE;
245             pl->limits[i].src_hard = LIMITS_DEF_NONE;
246         }
247     }
248
249     pl->priority = 0;
250     pl->login_limit = -2;
251     pl->login_limit_def = LIMITS_DEF_NONE;
252
253     return retval;
254 }
255
256 static void process_limit(int source, const char *lim_type,
257                           const char *lim_item, const char *lim_value,
258                           int ctrl, struct pam_limit_s *pl)
259 {
260     int limit_item;
261     int limit_type = 0;
262     long limit_value;
263     char *endptr;
264     const char *value_orig = lim_value;
265
266     if (ctrl & PAM_DEBUG_ARG)
267          _pam_log(LOG_DEBUG, "%s: processing %s %s %s for %s\n",
268                   __FUNCTION__,lim_type,lim_item,lim_value,
269                   limits_def_names[source]);
270
271     if (strcmp(lim_item, "cpu") == 0)
272         limit_item = RLIMIT_CPU;
273     else if (strcmp(lim_item, "fsize") == 0)
274         limit_item = RLIMIT_FSIZE;
275     else if (strcmp(lim_item, "data") == 0)
276         limit_item = RLIMIT_DATA;
277     else if (strcmp(lim_item, "stack") == 0)
278         limit_item = RLIMIT_STACK;
279     else if (strcmp(lim_item, "core") == 0)
280         limit_item = RLIMIT_CORE;
281     else if (strcmp(lim_item, "rss") == 0)
282         limit_item = RLIMIT_RSS;
283     else if (strcmp(lim_item, "nproc") == 0)
284         limit_item = RLIMIT_NPROC;
285     else if (strcmp(lim_item, "nofile") == 0)
286         limit_item = RLIMIT_NOFILE;
287     else if (strcmp(lim_item, "memlock") == 0)
288         limit_item = RLIMIT_MEMLOCK;
289     else if (strcmp(lim_item, "as") == 0)
290         limit_item = RLIMIT_AS;
291 #ifdef RLIMIT_LOCKS
292     else if (strcmp(lim_item, "locks") == 0)
293         limit_item = RLIMIT_LOCKS;
294 #endif
295     else if (strcmp(lim_item, "maxlogins") == 0) {
296         limit_item = LIMIT_LOGIN;
297         pl->flag_numsyslogins = 0;
298     } else if (strcmp(lim_item, "maxsyslogins") == 0) {
299         limit_item = LIMIT_NUMSYSLOGINS;
300         pl->flag_numsyslogins = 1;
301     } else if (strcmp(lim_item, "priority") == 0) {
302         limit_item = LIMIT_PRI;
303     } else {
304         _pam_log(LOG_DEBUG,"unknown limit item '%s'", lim_item);
305         return;
306     }
307
308     if (strcmp(lim_type,"soft")==0)
309         limit_type=LIMIT_SOFT;
310     else if (strcmp(lim_type, "hard")==0)
311         limit_type=LIMIT_HARD;
312     else if (strcmp(lim_type,"-")==0)
313         limit_type=LIMIT_SOFT | LIMIT_HARD;
314     else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS) {
315         _pam_log(LOG_DEBUG,"unknown limit type '%s'", lim_type);
316         return;
317     }
318
319     limit_value = strtol (lim_value, &endptr, 10);
320
321     /* special case value when limiting logins */
322     if (limit_value == 0 && value_orig == endptr) { /* no chars read */
323         if (strcmp(lim_value,"-") != 0) {
324             _pam_log(LOG_DEBUG,"wrong limit value '%s'", lim_value);
325             return;
326         } else
327             if (limit_item != LIMIT_LOGIN) {
328                 if (ctrl & PAM_DEBUG_ARG)
329                     _pam_log(LOG_DEBUG,
330                             "'-' limit value valid for maxlogins type only");
331                 return;
332             } else
333                 limit_value = -1;
334     }
335
336     /* one more special case when limiting logins */
337     if ((source == LIMITS_DEF_ALL || source == LIMITS_DEF_ALLGROUP)
338                 && (limit_item != LIMIT_LOGIN)) {
339         if (ctrl & PAM_DEBUG_ARG)
340             _pam_log(LOG_DEBUG,
341                         "'%%' domain valid for maxlogins type only");
342         return;
343     }
344
345     switch(limit_item) {
346         case RLIMIT_CPU:
347             limit_value *= 60;
348             break;
349         case RLIMIT_FSIZE:
350         case RLIMIT_DATA:
351         case RLIMIT_STACK:
352         case RLIMIT_CORE:
353         case RLIMIT_RSS:
354         case RLIMIT_MEMLOCK:
355         case RLIMIT_AS:
356             limit_value *= 1024;
357             break;
358     }
359
360     if ( (limit_item != LIMIT_LOGIN)
361          && (limit_item != LIMIT_NUMSYSLOGINS)
362          && (limit_item != LIMIT_PRI) ) {
363         if (limit_type & LIMIT_SOFT) {
364             if (pl->limits[limit_item].src_soft < source) {
365                 return;
366             } else {
367                 pl->limits[limit_item].limit.rlim_cur = limit_value;
368                 pl->limits[limit_item].src_soft = source;
369             }
370         }
371         if (limit_type & LIMIT_HARD) {
372             if (pl->limits[limit_item].src_hard < source) {
373                 return;
374             } else {
375                 pl->limits[limit_item].limit.rlim_max = limit_value;
376                 pl->limits[limit_item].src_hard = source;
377             }
378         }
379     } else {
380         /* recent kernels support negative priority limits (=raise priority) */
381
382         if (limit_item == LIMIT_PRI) {
383                 pl->priority = limit_value;
384         } else {
385                 if (pl->login_limit_def < source) {
386                     return;
387                 } else {
388                     pl->login_limit = limit_value;
389                     pl->login_limit_def = source;
390                 }
391         }
392     }
393     return;
394 }
395
396 static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl,
397                              struct pam_limit_s *pl)
398 {
399     FILE *fil;
400     char buf[LINE_LENGTH];
401
402 #define CONF_FILE (pl->conf_file[0])?pl->conf_file:LIMITS_FILE
403     /* check for the LIMITS_FILE */
404     if (ctrl & PAM_DEBUG_ARG)
405         _pam_log(LOG_DEBUG,"reading settings from '%s'", CONF_FILE);
406     fil = fopen(CONF_FILE, "r");
407     if (fil == NULL) {
408         _pam_log (LOG_WARNING, "can not read settings from %s", CONF_FILE);
409         return PAM_SERVICE_ERR;
410     }
411 #undef CONF_FILE
412
413     /* init things */
414     memset(buf, 0, sizeof(buf));
415     /* start the show */
416     while (fgets(buf, LINE_LENGTH, fil) != NULL) {
417         char domain[LINE_LENGTH];
418         char ltype[LINE_LENGTH];
419         char item[LINE_LENGTH];
420         char value[LINE_LENGTH];
421         int i,j;
422         char *tptr;
423
424         tptr = buf;
425         /* skip the leading white space */
426         while (*tptr && isspace(*tptr))
427             tptr++;
428         strncpy(buf, tptr, sizeof(buf)-1);
429         buf[sizeof(buf)-1] = '\0';
430
431         /* Rip off the comments */
432         tptr = strchr(buf,'#');
433         if (tptr)
434             *tptr = '\0';
435         /* Rip off the newline char */
436         tptr = strchr(buf,'\n');
437         if (tptr)
438             *tptr = '\0';
439         /* Anything left ? */
440         if (!strlen(buf)) {
441             memset(buf, 0, sizeof(buf));
442             continue;
443         }
444
445         memset(domain, 0, sizeof(domain));
446         memset(ltype, 0, sizeof(ltype));
447         memset(item, 0, sizeof(item));
448         memset(value, 0, sizeof(value));
449
450         i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value);
451         D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]",
452            i, domain, ltype, item, value));
453
454         for(j=0; j < strlen(domain); j++)
455             domain[j]=tolower(domain[j]);
456         for(j=0; j < strlen(ltype); j++)
457             ltype[j]=tolower(ltype[j]);
458         for(j=0; j < strlen(item); j++)
459             item[j]=tolower(item[j]);
460         for(j=0; j < strlen(value); j++)
461             value[j]=tolower(value[j]);
462
463         if (i == 4) { /* a complete line */
464             if (strcmp(uname, domain) == 0) /* this user have a limit */
465                 process_limit(LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
466             else if (domain[0]=='@') {
467                     if (ctrl & PAM_DEBUG_ARG) {
468                         _pam_log(LOG_DEBUG, "checking if %s is in group %s",
469                                 uname, domain + 1);
470                     }
471                 if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1))
472                     process_limit(LIMITS_DEF_GROUP, ltype, item, value, ctrl,
473                                   pl);
474             } else if (domain[0]=='%') {
475                     if (ctrl & PAM_DEBUG_ARG) {
476                         _pam_log(LOG_DEBUG, "checking if %s is in group %s",
477                                 uname, domain + 1);
478                     }
479                 if (strcmp(domain,"%") == 0)
480                     process_limit(LIMITS_DEF_ALL, ltype, item, value, ctrl,
481                                   pl);
482                 else if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1))
483                     process_limit(LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl,
484                                   pl);
485             } else if (strcmp(domain, "*") == 0)
486                 process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, ctrl,
487                               pl);
488         } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
489             if (strcmp(uname, domain) == 0) {
490                 if (ctrl & PAM_DEBUG_ARG) {
491                     _pam_log(LOG_DEBUG, "no limits for '%s'", uname);
492                 }
493                 fclose(fil);
494                 return PAM_IGNORE;
495             } else if (domain[0] == '@' && _pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) {
496                 if (ctrl & PAM_DEBUG_ARG) {
497                     _pam_log(LOG_DEBUG, "no limits for '%s' in group '%s'",
498                              uname, domain+1);
499                 }
500                 fclose(fil);
501                 return PAM_IGNORE;
502             }
503         } else {
504             _pam_log(LOG_DEBUG,"invalid line '%s' - skipped", buf);
505         }
506     }
507     fclose(fil);
508     return PAM_SUCCESS;
509 }
510
511 static int setup_limits(pam_handle_t *pamh,
512                         const char *uname, uid_t uid, int ctrl,
513                         struct pam_limit_s *pl)
514 {
515     int i;
516     int status;
517     int retval = LIMITED_OK;
518
519     if (uid == 0) {
520         /* do not impose limits (+ve limits anyway) on the superuser */
521         if (pl->priority > 0) {
522             if (ctrl & PAM_DEBUG_ARG) {
523                 _pam_log(LOG_DEBUG, "user '%s' has UID 0 - no limits imposed",
524                          uname);
525             }
526             pl->priority = 0;
527         }
528     }
529
530     for (i=0, status=LIMITED_OK; i<RLIM_NLIMITS; i++) {
531         if (pl->limits[i].limit.rlim_cur > pl->limits[i].limit.rlim_max)
532             pl->limits[i].limit.rlim_cur = pl->limits[i].limit.rlim_max;
533         if (!pl->supported[i]) {
534             /* skip it if its not known to the system */
535             continue;
536         }
537         status |= setrlimit(i, &pl->limits[i].limit);
538     }
539
540     if (status) {
541         retval = LIMIT_ERR;
542     }
543
544     status = setpriority(PRIO_PROCESS, 0, pl->priority);
545     if (status != 0) {
546         retval = LIMIT_ERR;
547     }
548
549     if (uid == 0) {
550         D(("skip login limit check for uid=0"));
551     } else if (pl->login_limit > 0) {
552         if (check_logins(pamh, uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) {
553             retval |= LOGIN_ERR;
554         }
555     } else if (pl->login_limit == 0) {
556         retval |= LOGIN_ERR;
557     }
558
559     return retval;
560 }
561
562 /* now the session stuff */
563 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
564                                    int argc, const char **argv)
565 {
566     int retval;
567     char *user_name;
568     struct passwd *pwd;
569     int ctrl;
570     struct pam_limit_s pl;
571
572     D(("called."));
573
574     memset(&pl, 0, sizeof(pl));
575
576     ctrl = _pam_parse(argc, argv, &pl);
577     retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
578     if ( user_name == NULL || retval != PAM_SUCCESS ) {
579         _pam_log(LOG_CRIT, "open_session - error recovering username");
580         return PAM_SESSION_ERR;
581      }
582
583     pwd = getpwnam(user_name);
584     if (!pwd) {
585         if (ctrl & PAM_DEBUG_ARG)
586             _pam_log(LOG_WARNING, "open_session username '%s' does not exist",
587                                    user_name);
588         return PAM_SESSION_ERR;
589     }
590
591     retval = init_limits(&pl);
592     if (retval != PAM_SUCCESS) {
593         _pam_log(LOG_WARNING, "cannot initialize");
594         return PAM_IGNORE;
595     }
596
597     retval = parse_config_file(pamh, pwd->pw_name, ctrl, &pl);
598     if (retval == PAM_IGNORE) {
599         D(("the configuration file has an applicable '<domain> -' entry"));
600         return PAM_SUCCESS;
601     }
602     if (retval != PAM_SUCCESS) {
603         _pam_log(LOG_WARNING, "error parsing the configuration file");
604         return PAM_IGNORE;
605     }
606
607     if (ctrl & PAM_DO_SETREUID) {
608         setreuid(pwd->pw_uid, -1);
609     }
610     retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, &pl);
611     if (retval != LIMITED_OK) {
612         return PAM_PERM_DENIED;
613     }
614
615     return PAM_SUCCESS;
616 }
617
618 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
619                                     int argc, const char **argv)
620 {
621      /* nothing to do */
622      return PAM_SUCCESS;
623 }
624
625 #ifdef PAM_STATIC
626
627 /* static module data */
628
629 struct pam_module _pam_limits_modstruct = {
630      "pam_limits",
631      NULL,
632      NULL,
633      NULL,
634      pam_sm_open_session,
635      pam_sm_close_session,
636      NULL
637 };
638 #endif
639
640 /*
641  * Copyright (c) Cristian Gafton, 1996-1997, <gafton@redhat.com>
642  *                                              All rights reserved.
643  *
644  * Redistribution and use in source and binary forms, with or without
645  * modification, are permitted provided that the following conditions
646  * are met:
647  * 1. Redistributions of source code must retain the above copyright
648  *    notice, and the entire permission notice in its entirety,
649  *    including the disclaimer of warranties.
650  * 2. Redistributions in binary form must reproduce the above copyright
651  *    notice, this list of conditions and the following disclaimer in the
652  *    documentation and/or other materials provided with the distribution.
653  * 3. The name of the author may not be used to endorse or promote
654  *    products derived from this software without specific prior
655  *    written permission.
656  *
657  * ALTERNATIVELY, this product may be distributed under the terms of
658  * the GNU Public License, in which case the provisions of the GPL are
659  * required INSTEAD OF the above restrictions.  (This clause is
660  * necessary due to a potential bad interaction between the GPL and
661  * the restrictions contained in a BSD-style copyright.)
662  *
663  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
664  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
665  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
666  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
667  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
668  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
669  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
670  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
671  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
672  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
673  * OF THE POSSIBILITY OF SUCH DAMAGE.
674  */