Example #1
0
File: fdb.go Project: hlandau/acme
// Returns the UID and GID to enforce. If the UID or GID is -1, it is not
// to be enforced. Neither or both or either of the UID or GID may be -1.
func resolveUIDGID(p *Permission) (uid, gid int, err error) {
	if p.UID == "$r" {
		uid = os.Getuid()
	} else if p.UID != "" {
		uid, err = passwd.ParseUID(p.UID)
		if err != nil {
			return
		}
	} else {
		uid = -1
	}

	if p.GID == "$r" {
		gid = os.Getgid()
	} else if p.GID != "" {
		gid, err = passwd.ParseGID(p.GID)
		if err != nil {
			return
		}
	} else {
		gid = -1
	}

	return
}
Example #2
0
func enforceGID(gid, path string) error {
	newGID, err := passwd.ParseGID(gid)
	if err != nil {
		return err
	}

	// So this is a surprisingly complicated dance if we want to be free of
	// potentially hazardous race conditions. We have a path. We can't assume
	// anything about its ownership, or mode, whether it's a symlink, etc.
	//
	// The big risk is that someone is able to create a symlink pointing to
	// something they want to illicitly access. Note that since /var/run will
	// commonly be used and because this directory is world-writeable, ala /tmp,
	// this is a real risk.
	//
	// So we have to make sure we don't follow symlinks. Assume we are running
	// as root (necessary, since we're chowning), and that nothing running as
	// root is malicious.
	//
	// We open the directory as a file so we can modify it using that reference
	// without worrying about the resolution of the path changing under us. But
	// we need to make sure we don't follow symlinks. This requires special OS
	// support, alas.
	dir, err := deos.OpenNoSymlinks(path)
	if err != nil {
		return err
	}

	defer dir.Close()

	fi, err := dir.Stat()
	if err != nil {
		return err
	}

	// Attributes of the directory can still change, but its type certainly
	// can't. This guarantee is enough for our purposes.
	if (fi.Mode() & os.ModeType) != os.ModeDir {
		return fmt.Errorf("challenge path %#v is not a directory", path)
	}

	curUID, err := deos.GetFileUID(fi)
	if err != nil {
		return err
	}

	dir.Chmod((fi.Mode() | 0070) & ^os.ModeType) // Ignore errors.
	dir.Chown(curUID, newGID)                    // Ignore errors.
	return nil
}
Example #3
0
func (h *ihandler) DropPrivileges() error {
	if h.dropped {
		return nil
	}

	// Extras
	if !h.info.NoBanSuid {
		// Try and bansuid, but don't process errors. It may not be supported on
		// the current platform, and Linux won't allow SECUREBITS to be set unless
		// one is root (or has the right capability). This is basically a
		// best-effort thing.
		bansuid.BanSuid()
	}

	// Various fixups
	if uidFlag.Value() != "" && gidFlag.Value() == "" {
		gid, err := passwd.GetGIDForUID(uidFlag.Value())
		if err != nil {
			return err
		}
		gidFlag.SetValue(strconv.FormatInt(int64(gid), 10))
	}

	if h.info.DefaultChroot == "" {
		h.info.DefaultChroot = "/"
	}

	chrootPath := chrootFlag.Value()
	if chrootPath == "" {
		chrootPath = h.info.DefaultChroot
	}

	uid := -1
	gid := -1
	if uidFlag.Value() != "" {
		var err error
		uid, err = passwd.ParseUID(uidFlag.Value())
		if err != nil {
			return err
		}

		gid, err = passwd.ParseGID(gidFlag.Value())
		if err != nil {
			return err
		}
	}

	if (uid <= 0) != (gid <= 0) {
		return fmt.Errorf("Either both or neither of the UID and GID must be positive")
	}

	if uid > 0 {
		chrootErr, err := daemon.DropPrivileges(uid, gid, chrootPath)
		if err != nil {
			return fmt.Errorf("Failed to drop privileges: %v", err)
		}
		if chrootErr != nil && chrootFlag.Value() != "" && chrootFlag.Value() != "/" {
			return fmt.Errorf("Failed to chroot: %v", chrootErr)
		}
	} else if chrootFlag.Value() != "" && chrootFlag.Value() != "/" {
		return fmt.Errorf("Must use privilege dropping to use chroot; set -uid")
	}

	// If we still have any caps (maybe because we didn't setuid), try and drop them.
	err := caps.Drop()
	if err != nil {
		return fmt.Errorf("cannot drop caps: %v", err)
	}

	if !h.info.AllowRoot && daemon.IsRoot() {
		return fmt.Errorf("Daemon must not run as root or with capabilities; run as non-root user or use -uid")
	}

	h.dropped = true
	return nil
}
Example #4
0
func parsePermissions(r io.Reader) (ps []Permission, erasePaths map[string]struct{}, err error) {
	br := bufio.NewReader(r)
	Lnum := 0
	erasePaths = map[string]struct{}{}
	seenPaths := map[string]struct{}{}

	for {
		Lnum++
		L, err := br.ReadString('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, nil, err
		}

		L = strings.TrimSpace(L)
		if L == "" || strings.HasPrefix(L, "#") {
			continue
		}

		// keys/*/privkey 0640 0750 - -
		m := re_permissionLine.FindStringSubmatch(L)
		if m == nil {
			return nil, nil, fmt.Errorf("line %d: badly formatted line: %q", Lnum, L)
		}

		path := filepath.Clean(m[1])
		if path == ".." || strings.HasPrefix(path, "../") || filepath.IsAbs(path) {
			return nil, nil, fmt.Errorf("line %d: path must remain within the DB root", Lnum, L)
		}

		if _, seen := seenPaths[path]; seen {
			return nil, nil, fmt.Errorf("line %d: duplicate path entry: %q", Lnum, L)
		}

		seenPaths[path] = struct{}{}
		if m[2] == "inherit" {
			erasePaths[path] = struct{}{}
			continue
		}

		fileMode, err := strconv.ParseUint(m[3], 8, 12)
		if err != nil {
			return nil, nil, fmt.Errorf("line %d: invalid file mode: %q", Lnum, m[3])
		}

		dirMode, err := strconv.ParseUint(m[4], 8, 12)
		if err != nil {
			return nil, nil, fmt.Errorf("line %d: invalid dir mode: %q", Lnum, m[4])
		}

		// Validate UID
		uid := m[6]
		if uid == "-" {
			uid = ""
		}
		if uid != "" && uid != "$r" {
			_, err := passwd.ParseUID(uid)
			if err != nil {
				return nil, nil, fmt.Errorf("line %d: invalid UID: %q: %v", Lnum, uid, err)
			}
		}

		// Validate GID
		gid := m[7]
		if gid == "-" {
			gid = ""
		}
		if gid != "" && gid != "$r" {
			_, err = passwd.ParseGID(gid)
			if err != nil {
				return nil, nil, fmt.Errorf("line %d: invalid GID: %q: %v", Lnum, gid, err)
			}
		}

		//
		p := Permission{
			Path:     path,
			FileMode: os.FileMode(fileMode),
			DirMode:  os.FileMode(dirMode),
			UID:      uid,
			GID:      gid,
		}

		ps = append(ps, p)
	}

	return ps, erasePaths, nil
}