linux/security/seclvl.c
<<
>>
Prefs
   1/**
   2 * BSD Secure Levels LSM
   3 *
   4 * Maintainers:
   5 *      Michael A. Halcrow <mike@halcrow.us>
   6 *      Serge Hallyn <hallyn@cs.wm.edu>
   7 *
   8 * Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com>
   9 * Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com>
  10 * Copyright (c) 2002 International Business Machines <robb@austin.ibm.com>
  11 * Copyright (c) 2006 Davi E. M. Arnaut <davi.arnaut@gmail.com>
  12 *
  13 *      This program is free software; you can redistribute it and/or modify
  14 *      it under the terms of the GNU General Public License as published by
  15 *      the Free Software Foundation; either version 2 of the License, or
  16 *      (at your option) any later version.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/moduleparam.h>
  21#include <linux/kernel.h>
  22#include <linux/init.h>
  23#include <linux/security.h>
  24#include <linux/netlink.h>
  25#include <linux/fs.h>
  26#include <linux/namei.h>
  27#include <linux/mount.h>
  28#include <linux/capability.h>
  29#include <linux/time.h>
  30#include <linux/proc_fs.h>
  31#include <linux/kobject.h>
  32#include <linux/crypto.h>
  33#include <asm/scatterlist.h>
  34#include <linux/scatterlist.h>
  35#include <linux/gfp.h>
  36#include <linux/sysfs.h>
  37
  38#define SHA1_DIGEST_SIZE 20
  39
  40/**
  41 * Module parameter that defines the initial secure level.
  42 *
  43 * When built as a module, it defaults to seclvl 1, which is the
  44 * behavior of BSD secure levels.  Note that this default behavior
  45 * wrecks havoc on a machine when the seclvl module is compiled into
  46 * the kernel.  In that case, we default to seclvl 0.
  47 */
  48#ifdef CONFIG_SECURITY_SECLVL_MODULE
  49static int initlvl = 1;
  50#else
  51static int initlvl;
  52#endif
  53module_param(initlvl, int, 0);
  54MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)");
  55
  56/* Module parameter that defines the verbosity level */
  57static int verbosity;
  58module_param(verbosity, int, 0);
  59MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to "
  60                 "0, which is Quiet)");
  61
  62/**
  63 * Optional password which can be passed in to bring seclvl to 0
  64 * (i.e., for halt/reboot).  Defaults to NULL (the passwd attribute
  65 * file will not be registered in sysfs).
  66 *
  67 * This gets converted to its SHA1 hash when stored.  It's probably
  68 * not a good idea to use this parameter when loading seclvl from a
  69 * script; use sha1_passwd instead.
  70 */
  71
  72#define MAX_PASSWD_SIZE 32
  73static char passwd[MAX_PASSWD_SIZE];
  74module_param_string(passwd, passwd, sizeof(passwd), 0);
  75MODULE_PARM_DESC(passwd,
  76                 "Plaintext of password that sets seclvl=0 when written to "
  77                 "(sysfs mount point)/seclvl/passwd\n");
  78
  79/**
  80 * SHA1 hashed version of the optional password which can be passed in
  81 * to bring seclvl to 0 (i.e., for halt/reboot).  Must be in
  82 * hexadecimal format (40 characters).  Defaults to NULL (the passwd
  83 * attribute file will not be registered in sysfs).
  84 *
  85 * Use the sha1sum utility to generate the SHA1 hash of a password:
  86 *
  87 * echo -n "secret" | sha1sum
  88 */
  89#define MAX_SHA1_PASSWD 41
  90static char sha1_passwd[MAX_SHA1_PASSWD];
  91module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0);
  92MODULE_PARM_DESC(sha1_passwd,
  93                 "SHA1 hash (40 hexadecimal characters) of password that "
  94                 "sets seclvl=0 when plaintext password is written to "
  95                 "(sysfs mount point)/seclvl/passwd\n");
  96
  97static int hideHash = 1;
  98module_param(hideHash, int, 0);
  99MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs "
 100                 "will return the SHA1-hashed value of the password that "
 101                 "lowers the secure level to 0.\n");
 102
 103#define MY_NAME "seclvl"
 104
 105/**
 106 * This time-limits log writes to one per second.
 107 */
 108#define seclvl_printk(verb, type, fmt, arg...)                  \
 109        do {                                                    \
 110                if (verbosity >= verb) {                        \
 111                        static unsigned long _prior;            \
 112                        unsigned long _now = jiffies;           \
 113                        if ((_now - _prior) > HZ) {             \
 114                                printk(type "%s: %s: " fmt,     \
 115                                        MY_NAME, __FUNCTION__ , \
 116                                        ## arg);                \
 117                                _prior = _now;                  \
 118                        }                                       \
 119                }                                               \
 120        } while (0)
 121
 122/**
 123 * The actual security level.  Ranges between -1 and 2 inclusive.
 124 */
 125static int seclvl;
 126
 127/**
 128 * flag to keep track of how we were registered
 129 */
 130static int secondary;
 131
 132/**
 133 * Verifies that the requested secure level is valid, given the current
 134 * secure level.
 135 */
 136static int seclvl_sanity(int reqlvl)
 137{
 138        if ((reqlvl < -1) || (reqlvl > 2)) {
 139                seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of "
 140                              "range: [%d]\n", reqlvl);
 141                return -EINVAL;
 142        }
 143        if ((seclvl == 0) && (reqlvl == -1))
 144                return 0;
 145        if (reqlvl < seclvl) {
 146                seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to "
 147                              "[%d]\n", reqlvl);
 148                return -EPERM;
 149        }
 150        return 0;
 151}
 152
 153/**
 154 * security level advancement rules:
 155 *   Valid levels are -1 through 2, inclusive.
 156 *   From -1, stuck.  [ in case compiled into kernel ]
 157 *   From 0 or above, can only increment.
 158 */
 159static void do_seclvl_advance(void *data, u64 val)
 160{
 161        int ret;
 162        int newlvl = (int)val;
 163
 164        ret = seclvl_sanity(newlvl);
 165        if (ret)
 166                return;
 167
 168        if (newlvl > 2) {
 169                seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
 170                              "[%d]\n", newlvl);
 171                return;
 172        }
 173        if (seclvl == -1) {
 174                seclvl_printk(1, KERN_WARNING, "Not allowed to advance to "
 175                              "seclvl [%d]\n", seclvl);
 176                return;
 177        }
 178        seclvl = newlvl;  /* would it be more "correct" to set *data? */
 179        return;
 180}
 181
 182static u64 seclvl_int_get(void *data)
 183{
 184        return *(int *)data;
 185}
 186
 187DEFINE_SIMPLE_ATTRIBUTE(seclvl_file_ops, seclvl_int_get, do_seclvl_advance, "%lld\n");
 188
 189static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
 190
 191/**
 192 * Converts a block of plaintext of into its SHA1 hashed value.
 193 *
 194 * It would be nice if crypto had a wrapper to do this for us linear
 195 * people...
 196 */
 197static int
 198plaintext_to_sha1(unsigned char *hash, const char *plaintext, unsigned int len)
 199{
 200        struct crypto_tfm *tfm;
 201        struct scatterlist sg;
 202        if (len > PAGE_SIZE) {
 203                seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "
 204                              "characters).  Largest possible is %lu "
 205                              "bytes.\n", len, PAGE_SIZE);
 206                return -EINVAL;
 207        }
 208        tfm = crypto_alloc_tfm("sha1", CRYPTO_TFM_REQ_MAY_SLEEP);
 209        if (tfm == NULL) {
 210                seclvl_printk(0, KERN_ERR,
 211                              "Failed to load transform for SHA1\n");
 212                return -EINVAL;
 213        }
 214        sg_init_one(&sg, (u8 *)plaintext, len);
 215        crypto_digest_init(tfm);
 216        crypto_digest_update(tfm, &sg, 1);
 217        crypto_digest_final(tfm, hash);
 218        crypto_free_tfm(tfm);
 219        return 0;
 220}
 221
 222/**
 223 * Called whenever the user writes to the sysfs passwd handle to this kernel
 224 * object.  It hashes the password and compares the hashed results.
 225 */
 226static ssize_t
 227passwd_write_file(struct file * file, const char __user * buf,
 228                                size_t count, loff_t *ppos)
 229{
 230        char *p;
 231        int len;
 232        unsigned char tmp[SHA1_DIGEST_SIZE];
 233
 234        if (!*passwd && !*sha1_passwd) {
 235                seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "
 236                              "seclvl module, but neither a plain text "
 237                              "password nor a SHA1 hashed password was "
 238                              "passed in as a module parameter!  This is a "
 239                              "bug, since it should not be possible to be in "
 240                              "this part of the module; please tell a "
 241                              "maintainer about this event.\n");
 242                return -EINVAL;
 243        }
 244
 245        if (count >= PAGE_SIZE)
 246                return -EINVAL;
 247        if (*ppos != 0)
 248                return -EINVAL;
 249        p = kmalloc(count, GFP_KERNEL);
 250        if (!p)
 251                return -ENOMEM;
 252        len = -EFAULT;
 253        if (copy_from_user(p, buf, count))
 254                goto out;
 255        
 256        len = count;
 257        /* ``echo "secret" > seclvl/passwd'' includes a newline */
 258        if (p[len - 1] == '\n')
 259                len--;
 260        /* Hash the password, then compare the hashed values */
 261        if ((len = plaintext_to_sha1(tmp, p, len))) {
 262                seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "
 263                              "[%d]\n", len);
 264                goto out;
 265        }
 266
 267        len = -EPERM;
 268        if (memcmp(hashedPassword, tmp, SHA1_DIGEST_SIZE))
 269                goto out;
 270
 271        seclvl_printk(0, KERN_INFO,
 272                      "Password accepted; seclvl reduced to 0.\n");
 273        seclvl = 0;
 274        len = count;
 275
 276out:
 277        kfree (p);
 278        return len;
 279}
 280
 281static struct file_operations passwd_file_ops = {
 282        .write = passwd_write_file,
 283};
 284
 285/**
 286 * Explicitely disallow ptrace'ing the init process.
 287 */
 288static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child)
 289{
 290        if (seclvl >= 0 && child->pid == 1) {
 291                seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "
 292                              "the init process dissallowed in "
 293                              "secure level %d\n", seclvl);
 294                return -EPERM;
 295        }
 296        return 0;
 297}
 298
 299/**
 300 * Capability checks for seclvl.  The majority of the policy
 301 * enforcement for seclvl takes place here.
 302 */
 303static int seclvl_capable(struct task_struct *tsk, int cap)
 304{
 305        int rc = 0;
 306
 307        /* init can do anything it wants */
 308        if (tsk->pid == 1)
 309                return 0;
 310
 311        if (seclvl > 0) {
 312                rc = -EPERM;
 313
 314                if (cap == CAP_LINUX_IMMUTABLE)
 315                        seclvl_printk(1, KERN_WARNING, "Attempt to modify "
 316                                      "the IMMUTABLE and/or APPEND extended "
 317                                      "attribute on a file with the IMMUTABLE "
 318                                      "and/or APPEND extended attribute set "
 319                                      "denied in seclvl [%d]\n", seclvl);
 320                else if (cap == CAP_SYS_RAWIO)
 321                        seclvl_printk(1, KERN_WARNING, "Attempt to perform "
 322                                      "raw I/O while in secure level [%d] "
 323                                      "denied\n", seclvl);
 324                else if (cap == CAP_NET_ADMIN)
 325                        seclvl_printk(1, KERN_WARNING, "Attempt to perform "
 326                                      "network administrative task while "
 327                                      "in secure level [%d] denied\n", seclvl);
 328                else if (cap == CAP_SETUID)
 329                        seclvl_printk(1, KERN_WARNING, "Attempt to setuid "
 330                                      "while in secure level [%d] denied\n",
 331                                      seclvl);
 332                else if (cap == CAP_SETGID)
 333                        seclvl_printk(1, KERN_WARNING, "Attempt to setgid "
 334                                      "while in secure level [%d] denied\n",
 335                                      seclvl);
 336                else if (cap == CAP_SYS_MODULE)
 337                        seclvl_printk(1, KERN_WARNING, "Attempt to perform "
 338                                      "a module operation while in secure "
 339                                      "level [%d] denied\n", seclvl);
 340                else
 341                        rc = 0;
 342        }
 343
 344        if (!rc) {
 345                if (!(cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0))
 346                        rc = -EPERM;
 347        }
 348
 349        if (rc)
 350                seclvl_printk(1, KERN_WARNING, "Capability denied\n");
 351
 352        return rc;
 353}
 354
 355/**
 356 * Disallow reversing the clock in seclvl > 1
 357 */
 358static int seclvl_settime(struct timespec *tv, struct timezone *tz)
 359{
 360        if (tv && seclvl > 1) {
 361                struct timespec now;
 362                now = current_kernel_time();
 363                if (tv->tv_sec < now.tv_sec ||
 364                    (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) {
 365                        seclvl_printk(1, KERN_WARNING, "Attempt to decrement "
 366                                      "time in secure level %d denied: "
 367                                      "current->pid = [%d], "
 368                                      "current->group_leader->pid = [%d]\n",
 369                                      seclvl, current->pid,
 370                                      current->group_leader->pid);
 371                        return -EPERM;
 372                }               /* if attempt to decrement time */
 373        }                       /* if seclvl > 1 */
 374        return 0;
 375}
 376
 377/* claim the blockdev to exclude mounters, release on file close */
 378static int seclvl_bd_claim(struct inode *inode)
 379{
 380        int holder;
 381        struct block_device *bdev = NULL;
 382        dev_t dev = inode->i_rdev;
 383        bdev = open_by_devnum(dev, FMODE_WRITE);
 384        if (bdev) {
 385                if (bd_claim(bdev, &holder)) {
 386                        blkdev_put(bdev);
 387                        return -EPERM;
 388                }
 389                /* claimed, mark it to release on close */
 390                inode->i_security = current;
 391        }
 392        return 0;
 393}
 394
 395/* release the blockdev if you claimed it */
 396static void seclvl_bd_release(struct inode *inode)
 397{
 398        if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {
 399                struct block_device *bdev = inode->i_bdev;
 400                if (bdev) {
 401                        bd_release(bdev);
 402                        blkdev_put(bdev);
 403                        inode->i_security = NULL;
 404                }
 405        }
 406}
 407
 408/**
 409 * Security for writes to block devices is regulated by this seclvl
 410 * function.  Deny all writes to block devices in seclvl 2.  In
 411 * seclvl 1, we only deny writes to *mounted* block devices.
 412 */
 413static int
 414seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
 415{
 416        if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) {
 417                switch (seclvl) {
 418                case 2:
 419                        seclvl_printk(1, KERN_WARNING, "Write to block device "
 420                                      "denied in secure level [%d]\n", seclvl);
 421                        return -EPERM;
 422                case 1:
 423                        if (seclvl_bd_claim(inode)) {
 424                                seclvl_printk(1, KERN_WARNING,
 425                                              "Write to mounted block device "
 426                                              "denied in secure level [%d]\n",
 427                                              seclvl);
 428                                return -EPERM;
 429                        }
 430                }
 431        }
 432        return 0;
 433}
 434
 435/**
 436 * The SUID and SGID bits cannot be set in seclvl >= 1
 437 */
 438static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr)
 439{
 440        if (seclvl > 0) {
 441                if (iattr->ia_valid & ATTR_MODE)
 442                        if (iattr->ia_mode & S_ISUID ||
 443                            iattr->ia_mode & S_ISGID) {
 444                                seclvl_printk(1, KERN_WARNING, "Attempt to "
 445                                              "modify SUID or SGID bit "
 446                                              "denied in seclvl [%d]\n",
 447                                              seclvl);
 448                                return -EPERM;
 449                        }
 450        }
 451        return 0;
 452}
 453
 454/* release busied block devices */
 455static void seclvl_file_free_security(struct file *filp)
 456{
 457        struct dentry *dentry = filp->f_dentry;
 458
 459        if (dentry)
 460                seclvl_bd_release(dentry->d_inode);
 461}
 462
 463/**
 464 * Cannot unmount in secure level 2
 465 */
 466static int seclvl_umount(struct vfsmount *mnt, int flags)
 467{
 468        if (current->pid != 1 && seclvl == 2) {
 469                seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure "
 470                              "level %d\n", seclvl);
 471                return -EPERM;
 472        }
 473        return 0;
 474}
 475
 476static struct security_operations seclvl_ops = {
 477        .ptrace = seclvl_ptrace,
 478        .capable = seclvl_capable,
 479        .inode_permission = seclvl_inode_permission,
 480        .inode_setattr = seclvl_inode_setattr,
 481        .file_free_security = seclvl_file_free_security,
 482        .settime = seclvl_settime,
 483        .sb_umount = seclvl_umount,
 484};
 485
 486/**
 487 * Process the password-related module parameters
 488 */
 489static int processPassword(void)
 490{
 491        int rc = 0;
 492        if (*passwd) {
 493                char *p;
 494
 495                if (*sha1_passwd) {
 496                        seclvl_printk(0, KERN_ERR, "Error: Both "
 497                                      "passwd and sha1_passwd "
 498                                      "were set, but they are mutually "
 499                                      "exclusive.\n");
 500                        return -EINVAL;
 501                }
 502
 503                p = kstrdup(passwd, GFP_KERNEL);
 504                if (p == NULL)
 505                        return -ENOMEM;
 506
 507                if ((rc = plaintext_to_sha1(hashedPassword, p, strlen(p))))
 508                        seclvl_printk(0, KERN_ERR, "Error: SHA1 support not "
 509                                      "in kernel\n");
 510
 511                kfree (p);
 512                /* All static data goes to the BSS, which zero's the
 513                 * plaintext password out for us. */
 514        } else if (*sha1_passwd) {      // Base 16
 515                int i;
 516                i = strlen(sha1_passwd);
 517                if (i != (SHA1_DIGEST_SIZE * 2)) {
 518                        seclvl_printk(0, KERN_ERR, "Received [%d] bytes; "
 519                                      "expected [%d] for the hexadecimal "
 520                                      "representation of the SHA1 hash of "
 521                                      "the password.\n",
 522                                      i, (SHA1_DIGEST_SIZE * 2));
 523                        return -EINVAL;
 524                }
 525                while ((i -= 2) + 2) {
 526                        unsigned char tmp;
 527                        tmp = sha1_passwd[i + 2];
 528                        sha1_passwd[i + 2] = '\0';
 529                        hashedPassword[i / 2] = (unsigned char)
 530                            simple_strtol(&sha1_passwd[i], NULL, 16);
 531                        sha1_passwd[i + 2] = tmp;
 532                }
 533        }
 534        return rc;
 535}
 536
 537/**
 538 * securityfs registrations
 539 */
 540struct dentry *dir_ino, *seclvl_ino, *passwd_ino;
 541
 542static int seclvlfs_register(void)
 543{
 544        int rc = 0;
 545
 546        dir_ino = securityfs_create_dir("seclvl", NULL);
 547
 548        if (IS_ERR(dir_ino))
 549                return PTR_ERR(dir_ino);
 550
 551        seclvl_ino = securityfs_create_file("seclvl", S_IRUGO | S_IWUSR,
 552                                dir_ino, &seclvl, &seclvl_file_ops);
 553        if (IS_ERR(seclvl_ino)) {
 554                rc = PTR_ERR(seclvl_ino);
 555                goto out_deldir;
 556        }
 557        if (*passwd || *sha1_passwd) {
 558                passwd_ino = securityfs_create_file("passwd", S_IRUGO | S_IWUSR,
 559                                dir_ino, NULL, &passwd_file_ops);
 560                if (IS_ERR(passwd_ino)) {
 561                        rc = PTR_ERR(passwd_ino);
 562                        goto out_delf;
 563                }
 564        }
 565        return rc;
 566
 567out_delf:
 568        securityfs_remove(seclvl_ino);
 569
 570out_deldir:
 571        securityfs_remove(dir_ino);
 572
 573        return rc;
 574}
 575
 576static void seclvlfs_unregister(void)
 577{
 578        securityfs_remove(seclvl_ino);
 579
 580        if (*passwd || *sha1_passwd)
 581                securityfs_remove(passwd_ino);
 582
 583        securityfs_remove(dir_ino);
 584}
 585
 586/**
 587 * Initialize the seclvl module.
 588 */
 589static int __init seclvl_init(void)
 590{
 591        int rc = 0;
 592        static char once;
 593
 594        if (verbosity < 0 || verbosity > 1) {
 595                printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 "
 596                       "are valid values\n", verbosity);
 597                rc = -EINVAL;
 598                goto exit;
 599        }
 600        if (initlvl < -1 || initlvl > 2) {
 601                seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel "
 602                              "[%d].\n", initlvl);
 603                rc = -EINVAL;
 604                goto exit;
 605        }
 606        seclvl = initlvl;
 607        if ((rc = processPassword())) {
 608                seclvl_printk(0, KERN_ERR, "Error processing the password "
 609                              "module parameter(s): rc = [%d]\n", rc);
 610                goto exit;
 611        }
 612
 613        if ((rc = seclvlfs_register())) {
 614                seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
 615                goto exit;
 616        }
 617        /* register ourselves with the security framework */
 618        if (register_security(&seclvl_ops)) {
 619                seclvl_printk(0, KERN_ERR,
 620                              "seclvl: Failure registering with the "
 621                              "kernel.\n");
 622                /* try registering with primary module */
 623                rc = mod_reg_security(MY_NAME, &seclvl_ops);
 624                if (rc) {
 625                        seclvl_printk(0, KERN_ERR, "seclvl: Failure "
 626                                      "registering with primary security "
 627                                      "module.\n");
 628                        seclvlfs_unregister();
 629                        goto exit;
 630                }               /* if primary module registered */
 631                secondary = 1;
 632        }                       /* if we registered ourselves with the security framework */
 633
 634        seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n");
 635
 636        if (once) {
 637                once = 1;
 638                seclvl_printk(0, KERN_INFO, "seclvl is going away. It has been "
 639                                "buggy for ages. Also, be warned that "
 640                                "Securelevels are useless.");
 641        }
 642 exit:
 643        if (rc)
 644                printk(KERN_ERR "seclvl: Error during initialization: rc = "
 645                       "[%d]\n", rc);
 646        return rc;
 647}
 648
 649/**
 650 * Remove the seclvl module.
 651 */
 652static void __exit seclvl_exit(void)
 653{
 654        seclvlfs_unregister();
 655
 656        if (secondary)
 657                mod_unreg_security(MY_NAME, &seclvl_ops);
 658        else if (unregister_security(&seclvl_ops))
 659                seclvl_printk(0, KERN_INFO,
 660                              "seclvl: Failure unregistering with the "
 661                              "kernel\n");
 662}
 663
 664module_init(seclvl_init);
 665module_exit(seclvl_exit);
 666
 667MODULE_AUTHOR("Michael A. Halcrow <mike@halcrow.us>");
 668MODULE_DESCRIPTION("LSM implementation of the BSD Secure Levels");
 669MODULE_LICENSE("GPL");
 670
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.