Source code for pymunge.raw

#########################################################################
# Module pymunge.raw - declarations of raw libmunge C functions
# Copyright (C) 2017-2018 nomadictype <nomadictype AT tutanota.com>
#
# pymunge is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.  Additionally, you can redistribute it
# and/or modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation, either version 3
# of the License, or (at your option) any later version.
#
# pymunge is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# and GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# and GNU Lesser General Public License along with pymunge.  If not, see
# <http://www.gnu.org/licenses/>.
#########################################################################

"""This module contains declarations of raw libmunge C functions
and constants.

Importing this module causes the libmunge shared library to be loaded.

Note that most function prototypes differ slightly from their C counterparts,
as follows:

* For all C functions that originally return an error code (`munge_err_t`),
  the corresponding Python wrapper instead checks the return value
  and raises a `MungeError` if the wrapped function returns anything
  other than `EMUNGE_SUCCESS`.

* Some functions originally return multiple values via pointer-based
  output arguments (e.g. uid and gid in `munge_decode`). The Python wrapper
  does not take these arguments and instead returns the multiple values
  as a tuple.
"""

import ctypes
import ctypes.util
import pymunge.error

#: Name of the libmunge shared object.
libmunge_filename = ctypes.util.find_library('munge')

#: Handle to the loaded libmunge shared object (a `ctypes.CDLL` object).
libmunge = None
if libmunge_filename is not None:
    libmunge = ctypes.CDLL(libmunge_filename)
else:
    import warnings
    warnings.warn("libmunge not found. All calls to pymunge.raw will fail.")


def load_function(name, restype, argtypes, paramflags, errcheck=None):
    """pymunge internal - helper to load functions from libmunge
    raises a `MungeError` if error_code is not `EMUNGE_SUCCESS`"""
    if libmunge is None:
        # Mock libmunge functions if the library was not found. This allows
        # readthedocs to build the sphinx docs for the package without
        # having libmunge installed.
        if argtypes == "*":
            args = "*args"
        else:
            argnames = [flag[1] for flag in paramflags if flag[0] & 2 == 0]
            args = ", ".join(argnames)
        l = {}
        exec("def function(" + args + "):\n" +
             "\traise ImportError('Failed to call C function " + name +
             ": libmunge C library not found')", {}, l)
        return l['function']
    else:
        if argtypes == "*":
            function = getattr(libmunge, name)
            function.restype = restype
        else:
            prototype = ctypes.CFUNCTYPE(restype, *argtypes)
            function = prototype((name, libmunge), paramflags)
        if errcheck is not None:
            function.errcheck = errcheck
        return function


def check_and_raise(error_code, ctx, result):
    """pymunge internal - helper for error check functions;
    raises a `MungeError` if error_code is not `EMUNGE_SUCCESS`"""
    if error_code != pymunge.error.MungeErrorCode.EMUNGE_SUCCESS.value:
        if ctx is not None:
            message = munge_ctx_strerror(ctx).decode("utf-8")
        else:
            message = munge_strerror(error_code).decode("utf-8")
        raise pymunge.error.MungeError(error_code, message, result)


# Type declarations

#: The `munge_ctx_t` C type, an opaque handle to a MUNGE context.
#: The low-level version of `MungeContext`.
munge_ctx_t = ctypes.c_void_p

#: The `munge_err_t` C enumeration type. Specifies a MUNGE error code.
#: The low-level version of `MungeErrorCode`.
munge_err_t = ctypes.c_int

#: The `munge_opt_t` C enumeration type. Specifies a MUNGE context option.
munge_opt_t = ctypes.c_int

#: The `munge_enum_t` C enumeration type. Specifies a MUNGE enumeration.
munge_enum_t = ctypes.c_int

#: The `uid_t` POSIX type. Specifies a numeric user ID.
uid_t = ctypes.c_uint

#: The `gid_t` POSIX type. Specifies a numeric group ID.
gid_t = ctypes.c_uint

#: The `time_t` C type. Specifies a timestamp.
time_t = ctypes.c_long


# libmunge functions

def errcheck_munge_encode(error_code, func, arguments):
    """pymunge internal - error check function for munge_encode"""
    ctx = arguments[1]
    result = arguments[0].value
    check_and_raise(error_code, ctx, result)
    return result

munge_encode = load_function("munge_encode",
    munge_err_t, [ctypes.POINTER(ctypes.c_char_p),
        munge_ctx_t, ctypes.c_void_p, ctypes.c_int],
    ((2, "cred"), (1, "ctx", None), (1, "buf", None), (1, "len", 0)),
    errcheck_munge_encode)
"""
C prototype: `munge_err_t munge_encode(char **cred, munge_ctx_t ctx,
const void *buf, int len);`

Note: when called from Python, returns `cred` instead of the `munge_err_t`.

Creates a credential contained in a base64 string.
A payload specified by a buffer `buf` (a byte string) of length `len`
can be encapsulated in as well.
If the munge context `ctx` is None, the default context will be used.
Returns the credential `cred` if the credential is successfully created;
otherwise, raises a `MungeError` containing the error code and message.
The error message may be more detailed if a `ctx` was specified.
"""

def errcheck_munge_decode(error_code, func, arguments):
    """pymunge internal - error check function for munge_decode"""
    ctx = arguments[1]
    buf = ctypes.string_at(arguments[2].value, arguments[3].value)
    result = (buf, arguments[4].value, arguments[5].value)
    check_and_raise(error_code, ctx, result)
    return result

munge_decode = load_function("munge_decode",
    munge_err_t, [ctypes.c_char_p, munge_ctx_t,
        ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_int),
        ctypes.POINTER(uid_t), ctypes.POINTER(gid_t)],
    ((1, "cred"), (1, "ctx", None), (2, "buf"), (2, "len"),
     (2, "uid"), (2, "gid")),
    errcheck_munge_decode)
"""
C prototype: `munge_err_t munge_decode(const char *cred, munge_ctx_t ctx,
void **buf, int *len, uid_t *uid, gid_t *gid);`

Note: when called from Python, returns `(payload, uid, gid)` instead of the
`munge_err_t`, where `payload` is the contents of `buf` of length `len`.
Example usage:

>>> payload, uid, gid = munge_decode(cred, ctx)

Validates the credential `cred`.
If the munge context `ctx` is not None, it will be set to that used
to encode the credential.
If the credential is valid, returns the encapsulated payload byte string
`payload` as well as the numeric UID `uid` and GID `gid` of the process
that created the credential.
If the credential is not valid, raises a `MungeError` containing the
error code and message. The error message may be more detailed if a `ctx`
was specified. For certain errors (ie, `EMUNGE_CRED_EXPIRED`,
`EMUNGE_CRED_REWOUND`, `EMUNGE_CRED_REPLAYED`), the raised `MungeError` will
contain the result `(payload, uid, gid)` which would have been returned
if the credential were still valid.
"""

munge_strerror = load_function("munge_strerror",
    ctypes.c_char_p, [munge_err_t],
    ((1, "e"),))
"""
C prototype: `const char * munge_strerror(munge_err_t e);`

Returns a descriptive string describing the munge errno `e`.
"""

munge_ctx_create = load_function("munge_ctx_create",
    munge_ctx_t, [], ())
"""
C prototype: `munge_ctx_t munge_ctx_create(void);`

Creates and returns a new munge context or None on error.
Abandoning a context without calling `munge_ctx_destroy()` will result
in a memory leak.
"""

munge_ctx_copy = load_function("munge_ctx_copy",
    munge_ctx_t, [munge_ctx_t],
    ((1, "ctx"),))
"""
C prototype: `munge_ctx_t munge_ctx_copy(munge_ctx_t ctx);`

Copies the context `ctx`, returning a new munge context or None on error.
Abandoning a context without calling `munge_ctx_destroy()` will result
in a memory leak.
"""

munge_ctx_destroy = load_function("munge_ctx_destroy",
    None, [munge_ctx_t],
    ((1, "ctx"),))
"""
C prototype: `void munge_ctx_destroy(munge_ctx_t ctx);`

Destroys the context `ctx`.
"""

munge_ctx_strerror = load_function("munge_ctx_strerror",
    ctypes.c_char_p, [munge_ctx_t],
    ((1, "ctx"),))
"""
C prototype: `const char * munge_ctx_strerror(munge_ctx_t ctx);`

Returns a descriptive text string describing the munge error number
according to the context `ctx`, or None if no error condition exists.
This message may be more detailed than that returned by `munge_strerror()`.
"""

def errcheck_munge_ctx_getset(error_code, func, arguments):
    """pymunge internal - error check function for munge_ctx_get
    and munge_ctx_set"""
    ctx = arguments[0]
    check_and_raise(error_code, ctx, None)
    return arguments

_munge_ctx_get = load_function("munge_ctx_get",
    munge_err_t, "*",
    (),
    errcheck_munge_ctx_getset)

[docs]def munge_ctx_get(ctx, opt, ptr): """ C prototype: `munge_err_t munge_ctx_get(munge_ctx_t ctx, munge_opt_t opt, ...);` Note: when called from Python, returns nothing. Gets the value for the option `opt` associated with the munge context `ctx`, storing the result in the subsequent pointer argument. Refer to the `munge_opt_t` enum comments for argument types. If the result is a string, that string should not be freed or modified by the caller. Raises a `MungeError` upon failure. """ return _munge_ctx_get(munge_ctx_t(ctx), munge_opt_t(opt), ptr)
_munge_ctx_set = load_function("munge_ctx_set", munge_err_t, "*", (), errcheck_munge_ctx_getset)
[docs]def munge_ctx_set(ctx, opt, val): """ C prototype: `munge_err_t munge_ctx_set(munge_ctx_t ctx, munge_opt_t opt, ...);` Note: when called from Python, returns nothing. Sets the value for the option `opt` associated with the munge context `ctx`, using the value of the subsequent argument. Refer to the `munge_opt_t` enum comments for argument types. Raises a `MungeError` upon failure. """ return _munge_ctx_set(munge_ctx_t(ctx), munge_opt_t(opt), val)
munge_enum_is_valid = load_function("munge_enum_is_valid", ctypes.c_bool, [munge_enum_t, ctypes.c_int], ((1, "type"), (1, "val"))) """ C prototype: `int munge_enum_is_valid(munge_enum_t type, int val);` Note: when called from Python, the returned int is converted to a boolean. Returns True if the given value `val` is a valid enumeration of the specified type `type` in the software configuration as currently compiled; otherwise returns False. Some enumerations correspond to options that can only be enabled at compile-time. """ munge_enum_int_to_str = load_function("munge_enum_int_to_str", ctypes.c_char_p, [munge_enum_t, ctypes.c_int], ((1, "type"), (1, "val"))) """ C prototype: `const char * munge_enum_int_to_str(munge_enum_t type, int val);` Converts the munge enumeration `val` of the specified type `type` into a text string. Returns the text string, or None on error. """ munge_enum_str_to_int = load_function("munge_enum_str_to_int", ctypes.c_int, [munge_enum_t, ctypes.c_char_p], ((1, "type"), (1, "str"))) """ C prototype: `int munge_enum_str_to_int(munge_enum_t type, const char *str);` Converts the case-insensitive byte string `str` into the corresponding munge enumeration of the specified type `type`. Returns a munge enumeration on success (>= 0), or -1 on error. """ # Enumerations (excluding those already present in pymunge.enums) # MUNGE context options MUNGE_OPT_CIPHER_TYPE = 0 #: symmetric cipher type (int) MUNGE_OPT_MAC_TYPE = 1 #: message auth code type (int) MUNGE_OPT_ZIP_TYPE = 2 #: compression type (int) MUNGE_OPT_REALM = 3 #: security realm (str) MUNGE_OPT_TTL = 4 #: time-to-live (int) MUNGE_OPT_ADDR4 = 5 #: src IPv4 addr (struct in_addr) MUNGE_OPT_ENCODE_TIME = 6 #: time when cred encoded (time_t) MUNGE_OPT_DECODE_TIME = 7 #: time when cred decoded (time_t) MUNGE_OPT_SOCKET = 8 #: socket for comm w/ daemon (str) MUNGE_OPT_UID_RESTRICTION = 9 #: UID able to decode cred (uid_t) MUNGE_OPT_GID_RESTRICTION = 10 #: GID able to decode cred (gid_t) # MUNGE enum types for str/int conversions MUNGE_ENUM_CIPHER = 0 #: cipher enum type MUNGE_ENUM_MAC = 1 #: mac enum type MUNGE_ENUM_ZIP = 2 #: zip enum type __all__ = [ "munge_encode", "munge_decode", "munge_strerror", "munge_ctx_create", "munge_ctx_copy", "munge_ctx_destroy", "munge_ctx_strerror", "munge_ctx_get", "munge_ctx_set", "munge_enum_is_valid", "munge_enum_int_to_str", "munge_enum_str_to_int", "libmunge_filename", "libmunge", "uid_t", "gid_t", "time_t", "munge_ctx_t", "munge_err_t", "munge_opt_t", "MUNGE_OPT_CIPHER_TYPE", "MUNGE_OPT_MAC_TYPE", "MUNGE_OPT_ZIP_TYPE", "MUNGE_OPT_REALM", "MUNGE_OPT_TTL", "MUNGE_OPT_ADDR4", "MUNGE_OPT_ENCODE_TIME", "MUNGE_OPT_DECODE_TIME", "MUNGE_OPT_SOCKET", "MUNGE_OPT_UID_RESTRICTION", "MUNGE_OPT_GID_RESTRICTION", "munge_enum_t", "MUNGE_ENUM_CIPHER", "MUNGE_ENUM_MAC", "MUNGE_ENUM_ZIP", ]