Las aplicaciones que llevan a cabo operaciones de red a menudo deben asegurarse de que sus transacciones sean seguras. La interfaz de programación de aplicaciones (API) RPCSEC_GSS permite a los desarrolladores aprovechar una gran variedad de mecanismos de seguridad, que incluyen SEAM y Kerberos V5. Asimismo, RPCSEC_GSS incluye servicios de integridad y privacidad, que proporcionan una protección adicional a la autenticación. Aunque RPCSEC_GSS no es parte de SEAM ni es específico para éste, los programadores que deseen aprovechar Kerberos V5 en sus aplicaciones encontrarán que es una forma excelente para hacerlo. De hecho, como RPCSEC_GSS es independiente del mecanismo, los desarrolladores que no utilicen SEAM/Kerberos V5 como su mecanismo de seguridad pero que deseen aprovechar la privacidad y la integridad deberían considerar su uso.
Este capítulo asume que está familiarizado con la programación de RPC; para obtener información sobre RPC, véase ONC+ Developer's Guide. Además, este capítulo únicamente está pensado como una introducción; para obtener información sobre los aspectos específicos de RPCSEC_GSS, como sus funciones o sus estructuras de datos, véase la página del comando man rpcsec_gss(3N) o la página del comando man de cualquiera de las funciones descritas en este capítulo.
En este capítulo se tratan los temas siguientes:
Este apartado describe el desarrollo y la naturaleza de la API RPCSEC_GSS.
AUTH_SYS fue una de las primeras versiones de seguridad admitidas por RPC (conocida también como AUTH_UNIX); proporcionaba una credencial de tipo UNIX, mediante los ID de usuario y de grupo para identificar al remitente y al destinatario de un mensaje. AUTH_SYS es fácil de implementar; sin embargo, también es fácil de burlar, ya que no proporciona una verdadera autenticación (es decir, un servidor no puede verificar por ningún medio que un cliente es quien afirma ser). Por tanto, bajo AUTH_SYS es relativamente sencillo falsificar una solicitud de red.
Una versión de seguridad posterior, AUTH_DES, basada en la autenticación de clave pública, apareció después de AUTH_SYS; utiliza un intercambio de claves Diffie-Hellman para producir una clave común entre la clave privada de un cliente y la clave pública de un servidor. A continuación, se utiliza la clave común para encriptar una clave de sesión DES, que desencripta el servidor para establecer una sesión.
Aunque AUTH_DES representó un avance considerable sobre AUTH_SYS, tiene algunas limitaciones para su uso generalizado. La objeción principal para muchas personas es que el tamaño de la clave es demasiado pequeño, según los estándares de encriptación actuales.
Finalmente, se introdujo otra versión de seguridad de RPC. AUTH_KERB, basado en Kerberos V4, proporciona una seguridad superior a AUTH_DES o AUTH_SYS. Sin embargo, también se puede violar.
Para más información sobre estas versiones de seguridad, véase la ONC+ Developer's Guide.
Para mejorar la seguridad, se ha agregado una nueva capa de seguridad, la API de estándar de seguridad general o GSS-API. La estructura de GSS-API ofrece dos servicios de seguridad adicionales además de la autenticación:
Integridad. Con el servicio de integridad, GSS-API utiliza el mecanismo subyacente para autenticar los mensajes que intercambian los programas. Las sumas de comprobación criptográficas establecen:
La identidad del emisor de datos al destinatario
La identidad del destinatario al emisor (si se solicita la autenticación mutua)
La autenticidad de los datos transmitidos
Privacidad. El servicio de privacidad incluye también el de integridad. Además, los datos transmitidos también están encriptados para protegerlos contra cualquier intruso.
Debido a las restricciones de exportación de los EE.UU., puede que el servicio de privacidad no esté disponible para todos los usuarios de SEAM.
Actualmente, la GSS-API no está expuesta. Sin embargo, algunas de sus funciones son "visibles" mediante las funciones de RPCSEC_GSS (pueden manipularse de forma "opaca"). El programador no debe preocuparse directamente de sus valores.
La versión de seguridad RPCSEC_GSS permite a las aplicaciones RPC de ONC que aprovechen las funciones de GSS-API. RPCSEC_GSS está situada "sobre" la capa de GSS-API de la forma siguiente:
Mediante la interfaz de programación para RPCSEC_GSS, las aplicaciones RPC de ONC pueden especificar:
Un paradigma de seguridad. Cada clase de mecanismo de seguridad ofrece un tipo diferente de protección de los datos, así como uno o varios niveles de protección de éstos. En este caso, cualquier mecanismo de seguridad admitido por GSS-API (Kerberos V5, clave pública RSA, etcétera).
Privacidad o integridad (o ninguno). El predeterminado es la integridad. El servicio es independiente del mecanismo.
Calidad de protección. QOP especifica qué tipo de algoritmo criptográfico se utilizará para implementar los servicios de privacidad o integridad. Cada mecanismo de seguridad puede tener asociado con él uno o varios parámetros QOP.
Las aplicaciones pueden obtener listas de QOP y los mecanismos válidos mediante funciones que proporciona RPCSEC_GSS (Véase "Funciones varias"). Los desarrolladores deben evitar fijar los mecanismos y los parámetros QOP en sus aplicaciones, para así poder utilizar mecanismos o QOP diferentes sin tener que modificarlas.
Históricamente, "versión de seguridad" y "versión de autenticación" han significado lo mismo. Con la introducción de RPCSEC_GSS, ahora "versión" tiene un significado algo distinto. Una versión puede incluir un servicio (integridad o privacidad) junto con la autenticación, aunque actualmente RPCSEC_GSS es la única versión que lo hace.
Mediante RPCSEC_GSS, las aplicaciones RPC de ONC establecen un contexto de seguridad como un igual, intercambian datos y destruyen el contexto, al igual que harían con otras versiones. Cuando se ha establecido un contexto, la aplicación puede cambiar QOP y el servicio para cada unidad de datos enviada.
Para obtener más información sobre RPCSEC_GSS, incluidos sus tipos de datos, véase la página del comando man rpcsec_gss(3N).
Tabla 8-1 resume los comandos de RPCSEC_GSS. Está previsto como una visión general de las funciones de RPCSEC_GSS, en lugar de una descripción específica de cada una. Para más información sobre una función, consulte su página del comando man o consulte la página del comando man rpcsec_gss(3N) para obtener una visión general que incluye una lista de las estructuras de datos de RPCSEC_GSS.
Tabla 8-1 Funciones de RPCSEC_GSSAcción | Función | Entrada | Salida |
---|---|---|---|
Crear un contexto de seguridad | rpc_gss_seccreate() | Manejador CLIENT, nombre de principal, mecanismo, QOP, tipo de servicio | Manejador AUTH |
Cambiar QOP o el tipo de servicio para el contexto | rpc_gss_set_defaults() | QOP antigua, servicio | QOP nueva, servicio |
Mostrar el tamaño máximo para los datos antes de la transformación de seguridad | rpc_gss_max_data_length() | Tamaño de datos máximo permitido por el transporte | Tamaño de datos máximo previo a la transformación |
Mostrar el tamaño máximo para los datos antes de la transformación de seguridad | rpc_gss_svc_max_data_length() | Tamaño de datos máximo permitido por el transporte | Tamaño de datos máximo previo a la transformación |
Definir el nombre de los principales para que los represente el servidor | rpc_gss_set_svc_name() | Nombre de principal, programa RPC, números de versión | TRUE si es satisfactorio |
Obtener las credenciales del llamante (cliente) | rpc_gss_getcred() | Puntero a la estructura svc_req | Credenciales de UNIX, credenciales de RPCSEC_GSS, cookie |
Especificar una función de rellamada (escrita por el usuario) | rpc_gss_set_callback() | Puntero a la función de rellamada | TRUE si es satisfactorio |
Crear una estructura RPCSEC_GSS para los nombres de principal a partir de parámetros únicos | rpc_gss_get_principal_name() | Mecanismo, nombre de usuario, nombre de máquina, nombre de dominio | Estructura de nombre de principal de RPCSEC_GSS |
Recuperar un código de error cuando falla una rutina de RPCSEC_GSS | rpc_gss_get_error() |
| Número de error de RPCSEC_GSS, número de error si se aplica |
Obtener cadenas para los mecanismos instalados | rpc_gss_get_mechanisms() |
| Lista de mecanismos válidos |
Obtener cadenas de QOP válidas | rpc_gss_get_mech_info() | Mecanismo | QOP válidas para ese mecanismo |
Obtener los números de versión superior e inferior de RPCSEC_GSS admitidos | rpc_gss_get_versions() |
| Versiones superior e inferior |
Comprobar si está instalado un mecanismo | rpc_gss_is_installed() | Mecanismo | TRUE si está instalado |
Convertir mecanismo ASCII en identificador de objeto de RPC | rpc_gss_mech_to_oid() | Mecanismo (en forma de cadena) | Mecanismo (en forma de OID) |
Convertir QOP ASCII en entero | rpc_gss_qop_to_num() | QOP (en forma de cadena) | QOP (en forma de entero) |
Los contextos se crean con la llamada rpc_gss_seccreate(). Esta función toma los argumentos siguientes:
Un manejador de cliente (devuelto, por ejemplo, por clnt_create())
El nombre del principal de servidor (por ejemplo, nfs@acme.com)
El mecanismo para la sesión (por ejemplo, Kerberos V5)
El tipo de servicio de seguridad (por ejemplo, privacidad)
QOP para la sesión
Dos parámetros de GSS-API que pueden permanecer opacos para la mayoría de usos (es decir, el programador puede proporcionar valores nulos)
Devuelve un manejador de autenticación AUTH. Ejemplo 8-1 muestra cómo se puede utilizar rpc_gss_seccreate() para crear un contexto mediante el mecanismo de seguridad Kerberos V5 y el servicio de integridad:
CLIENT *clnt; /* manejador de cliente */ char server_host[] = "foo"; char service_name[] = "nfs@eng.acme.com"; char mech[] = "kerberos_v5"; clnt = clnt_create(server_host, SERVER_PROG, SERV_VERS, "netpath"); clnt->clnt_auth = rpc_gss_seccreate(clnt, service_name, mech, rpc_gss_svc_integrity, NULL, NULL, NULL); . . .
Algunas de las cosas que se deben tener en cuenta sobre Ejemplo 8-1 son:
Aunque se declaró el mecanismo de forma explícita (para facilitar la lectura), habitualmente se obtendría por programación con rpc_gss_get_mechanisms() desde una tabla de mecanismos disponibles.
QOP se pasa como NULL, que la define como el valor predeterminado de este mecanismo. De otra forma, podría obtenerse un valor válido por programa con rpc_gss_get_mechanisms(), al igual que el mecanismo. Para obtener más información, véase la página del comando man rpc_gss_get_mechanisms(3N).
El tipo de servicio de seguridad, rpc_gss_svc_integrity, es un enum del tipo RPCSEC_GSS rpc_gss_service_t. rpc_gss_service_t tiene el formato siguiente:
typedef enum { rpc_gss_svc_default = 0, rpc_gss_svc_none = 1, rpc_gss_svc_integrity = 2, rpc_gss_svc_privacy = 3 } rpc_gss_service_t;
El sistema de seguridad predeterminado es la integridad, de forma que el programador podría haber especificado rpc_gss_svc_default y obtener el mismo resultado.
Para más información, véase la página del comando man de rpc_gss_seccreate(3N).
Una vez definido un contexto, puede que la aplicación necesite cambiar los valores de QOP y de servicio para las unidades de datos individuales que se están transmitiendo (por ejemplo, puede que desee que un programa encripte una contraseña pero no un nombre de inicio de sesión). rpc_gss_set_defaults() le permite hacer esto:
rpc_gss_set_defaults(clnt->clnt_auth, rpc_gss_svc_privacy, qop); . . .
En este caso, el servicio de seguridad está establecido como la privacidad (véase "Creación de un contexto"). qop es un puntero a una cadena que nombra la nueva QOP.
Los contextos se destruyen de la forma habitual, con auth_destroy().
Para obtener más información sobre cómo cambiar el servicio y QOP, véase la página del comando man rpc_gss_set_defaults(3N).
Para establecer un contexto de seguridad son necesarios dos tipos de nombres de principal:
Un nombre de principal de servidor. El nombre de principal del servidor siempre se especifica como una cadena ASCII finalizada en valor nulo con el formato servicio@sistema; por ejemplo, nfs@eng.acme.com.
Cuando un cliente crea un contexto de seguridad, especifica el nombre de principal del servidor en este formato (véase "Creación de un contexto"). De forma similar, cuando un servidor necesita el nombre de un principal al que representará, utiliza rpc_gss_set_svc_name(), que toma como argumento un nombre de principal en este formato.
Un nombre de principal de cliente. El nombre de principal de un cliente, tal como lo recibe un principal, tiene el formato de una estructura de nombre de principal rpc_gss_principal_t: una cadena de bytes opacos y contados determinados por el mecanismo utilizado. Esta estructura se describe en la página del comando man rpcsec_gss(3N).
Los servidores necesitan conocer los nombres de los principales a los que representarán cuando se inicien (un servidor puede hacer de más de un principal). rpc_gss_set_svc_name() establece el nombre del principal o principales:
char *principal, *mechanism; u_int req_time; principal = "nfs@eng.acme.com"; mechanism = "kerberos_v5"; req_time = 10000; /* tiempo durante el que debería ser válida la credencial */ rpc_gss_set_svc_name(principal, mechanism, req_time, SERV_PROG, SERV_VERS);
(Kerberos ignora el parámetro req_time. Es posible que otros sistemas de autenticación lo utilicen).
Para obtener más información, véase la página del comando man de rpc_gss_set_svc_name(3N).
Los servidores necesitan poder trabajar con el nombre de principal de un cliente; por ejemplo, para comparar el nombre de principal de un cliente con una lista de control de acceso o para consultar una credencial de UNIX para ese cliente, en caso de que exista. Estos nombres de principal se mantienen en forma de un puntero a una estructura rpc_gss_principal_t. (Para obtener más información sobre rpc_gss_principal_t, véase la página del comando man rpcsec_gss(3N)). Si un servidor desea comparar un nombre de principal recibido con el nombre de una entidad conocida, necesita poder generar un nombre de principal en ese formato.
La llamada rpc_gss_get_principal_name() toma varios parámetros de entrada que identifican de forma única a una persona de la red y genera un nombre de principal como un puntero de estructura rpc_gss_principal_t:
rpc_gss_principal_t *principal; rpc_gss_get_principal_name(principal, mecanismo, nombre, nodo, dominio); . . .
Los argumentos de rpc_gss_get_principal_name() son los siguientes:
principal es un puntero a la estructura rpc_gss_principal_t que se definirá.
mecanismo es el mecanismo de seguridad utilizado (recuerde que el nombre de principal que se está generando depende del mecanismo).
nombre es un nombre de persona o de servicio, como juan o nfs, o incluso el nombre de una aplicación definida por el usuario.
nodo podría ser, por ejemplo, un nombre de máquina de UNIX.
dominio podría ser, por ejemplo, un nombre de dominio de DNS, NIS, o NIS+ o un ámbito de Kerberos.
Cada mecanismo de seguridad necesita parámetros de identificación diferentes. Por ejemplo, Kerberos V5 necesita un nombre de usuario y, sólo de forma opcional, nombres completos de nombre y de dominio (en términos de Kerberos, nombres de sistema y de ámbito).
Para obtener más información, véase la página del comando man de rpc_gss_get_principal_name(3N).
Los nombres de principal se liberan mediante la llamada de biblioteca free().
Un servidor debe poder recuperar las credenciales de un cliente. La función rpc_gss_getcred(), que se muestra en Ejemplo 8-5, permite al servidor que recupere las credenciales de UNIX o las de RPCSEC_GSS (o ambas). Lo hace mediante dos argumentos, que estarán definidos si la función es satisfactoria. Uno es un puntero a una estructura rpc_gss_ucred_t, que contiene las credenciales de UNIX del llamante en caso que existan:
typedef struct { uid_t uid; /* ID de usuario */ gid_t gid; /* ID de grupo */ short gidlen; git_t *gidlist; /* lista de grupos */ } rpc_gss_ucred_t;
El otro argumento es un puntero a una estructura rpc_gss_raw_cred_t, que tiene el aspecto siguiente:
typedef struct { u_int version; /* versión de programa de RPCSEC_GSS */ char *mechanism; char *qop; rpc_gss_principal_t client_principal; /* nombre de principal de cliente */ char *svc_principal; /* nombre de principal de servicio */ rpc_gss_service_t service; /* enum de privacidad e integridad */ } rpc_gss_rawcred_t;(Véase "Generación de nombres de principal de cliente" para obtener una descripción de la estructura rpc_gss_principal_t y de cómo se crea). Como rpc_gss_rawcred_t contiene los nombres de principal del cliente y del servidor, rpc_gss_getcred() puede devolver ambos.
Ejemplo 8-5 ilustra un procedimiento sencillo de despacho del lado del servidor, en el que el servidor obtiene las credenciales del llamante. El procedimiento obtiene las credenciales de UNIX del llamante y luego verifica la identidad del usuario mediante mediante el mecanismo, QOP y el tipo de servicio encontrados en el argumento rpc_gss_rcred_t.
static void server_prog(struct svc_req *rqstp, SVCXPRT *xprt) { rpc_gss_ucred_t *ucred; rpc_gss_rawcred_t *rcred; if (rqst->rq_proq == NULLPROC) { svc_sendreply(xprt, xdr_void, NULL); return; } /* * autenticar el resto de solicitudes */ */ switch (rqstp->rq _cred.oa_flavor) { case RPCSEC_GSS: /* * obtener la información de credenciales */ rpc_gss_getcred(rqstp, &rcred, &ucred, NULL); /* * verificar que el usuario tiene el permiso de acceso * mediante los parámetros de seguridad recibidos * examinando mi archivo de configuración */ if (!authenticate_user(ucred->uid, rcred->mechanism, rcred->qop, rcred->service)) { svcerr_weakauth(xprt); return; } break; /* permitir la entrada del usuario */ default: svcerr_weakauth(xprt); return; } /* fin de switch */ switch (rqstp->rq_proq) { case SERV_PROC1: . . . } /* proceso habitual de solicitudes; enviar respuesta ... */ return; } |
Para obtener más información, véase la página del comando man rpc_gss_getcred(3N).
En Ejemplo 8-5, el último argumento de rpc_gss_getcred() (en este caso, NULL) es una cookie definida por el usuario, cuyo valor al retornar será lo que especificó el servidor cuando se creó el contexto. Esta cookie, un valor de cuatro bytes, se puede utilizar como se considere apropiado para la aplicación; RPC no lo interpreta. Por ejemplo, la cookie puede ser un puntero o un índice a una estructura que representa al iniciador del contexto; en lugar de calcular este valor para cada solicitud, el servidor lo calcula en el momento de la creación del contexto (con lo que ahorra así tiempo de proceso de solicitudes).
Otro lugar donde se pueden utilizar las cookies son las rellamadas. Un servidor puede especificar una rellamada (definida por el usuario) de forma que sepa cuándo se utiliza un contexto por primera vez mediante la función rpc_gss_set_callback(). Cuando se utiliza un contexto por primera vez para el intercambio de datos, después de que se establezca para el programa y la versión especificados, se invocará a la rellamada.
La rutina de rellamada definida por el usuario tiene el formato siguiente:
El segundo y el tercer argumento, deleg y gss_context, son tipos de datos de GSS-API y actualmente no están expuestos, de forma que la función de rellamada puede ignorarlos. (brevemente, deleg es la identidad de cualquier delegado igual, mientras que gss_context es un puntero al contexto de GSS-API, por si el programa deseara realizar operaciones de GSS-API en el contexto, es decir, para probar los criterios de aceptación). El argumento cookie ya se ha comentado.
El argumento lock es un puntero a una estructura rpc_gss_lock_t:
typedef struct { bool_t locked;. rpc_gss_rawcred_t *raw_cred; } rpc_gss_lock_t;Este parámetro permite a un servidor aplicar QOP y un servicio determinados para la sesión. QOP y el servicio se encuentran en la estructura rpc_gss_rawcred_t descrita en Ejemplo 8-5 (un servidor no debería cambiar los valores para el servicio y QOP). Cuando se invoca la rellamada definida por el usuario, se define el campo locked como FALSE. Si el servidor define locked como TRUE, sólo se aceptarán las solicitudes con valores de QOP y de servicio que concuerden con los de la estructura rpc_gss_rawcred_t.
Para obtener más información, véase la página del comando man rpc_gss_set_callback(3N.
Dos funciones, rpc_gss_max_data_length() y rpc_gss_svc_max_data_length(), son útiles para determinar cuál puede ser el tamaño máximo de un bloque de datos antes de que se transforme por las medidas de seguridad y se envíe "a través del cable". Es decir, habitualmente las transformaciones de seguridad, como la encriptación, cambian el tamaño de un bloque de datos transmitido (a menudo haciéndolo más grande). Para asegurarse de que los datos no crezcan más allá de un tamaño útil, estas dos funciones (la primera es la versión del lado del cliente y la segunda la del lado del servidor) devuelven el tamaño previo a la transformación para un transporte dado.
Para obtener más información, véase las páginas del comando man rpc_gss_max_data_length(3N) y rpc_gss_svc_max_data_length(3N).
Hay varias funciones que resultan útiles para obtener información sobre el sistema de seguridad instalado:
rpc_gss_get_mechanisms() devuelve una lista de los mecanismos de seguridad instalados
rpc_gss_is_installed() comprueba si está instalado un mecanismo especificado
rpc_gss_get_mech_info() devuelve QOP válidas para un mecanismo dado
El uso de estas funciones proporciona al programador la libertad para evitar fijar los parámetros de seguridad en las aplicaciones (véase Tabla 8-1 y la página del comando man rpcsec_gss(3N) para obtener una lista de todas las funciones de RPCSEC_GSS).
RPCSEC_GSS utiliza determinados archivos para almacenar información.
Cuando un servidor recupera las credenciales de un cliente asociadas con una solicitud, puede obtener el nombre de principal del cliente (en forma de un puntero a una estructura rpc_gss_principal_t) o las credenciales de UNIX locales (UID) para ese cliente. Los servicios como NFS necesitan una credencial de UNIX local para la comprobación del acceso, pero puede que otros no; por ejemplo, pueden almacenar el nombre de principal directamente en sus propias listas de control de acceso, como una estructura rpc_gss_principal_t.
La correspondencia entre la credencial de red de un cliente (su nombre de principal) y cualquier credencial de UNIX local no es automática; debe haber sido definida de forma explícita por el administrador de seguridad local.
El archivo gsscred contiene las credenciales de UNIX y de red (por ejemplo, las de Kerberos V5) del cliente (las segundas credenciales son la representación Hexadecimal-ASCII de la estructura rpc_gss_principal_t). Se accede a través de XFN; por tanto, esta tabla se puede implementar a través de files, NIS, NIS+ o cualquier servicio de nombres futuro que admita XFN. Esta tabla aparece en la jerarquía de XFN como esta_unidad_org/service/gsscred. La tabla gsscred se mantiene mediante la utilidad gsscred, que permite a los administradores agregar y suprimir usuarios y mecanismos.
Por comodidad, RPCSEC_GSS utiliza literales de cadena para representar a los parámetros de los mecanismos y la Calidad de protección (QOP). Sin embargo, los mecanismos subyacentes propiamente dichos, necesitan que los mecanismos estén representados por identificadores de objetos y QOP por enteros de 32 bits. Además, debe especificarse para cada mecanismo la biblioteca compartida que implementa sus servicios.
El archivo /etc/gss/mech almacena la información siguiente sobre todos los mecanismos instalados en un sistema: el nombre del mecanismo en ASCII; su OID; la biblioteca compartida que implementa los servicios que proporciona este mecanismo; y, opcionalmente, el módulo del núcleo que implementa el servicio. Una línea de ejemplo puede tener el aspecto siguiente:
kerberos_v5 1.2.840.113554.1.2.2 gl/mech_krb5.so gl_kmech_krb5 |
El archivo /etc/gss/qop almacena, para todos los mecanismos instalados, todas las QOP admitidas por cada mecanismo como una cadena ASCII y como su correspondiente entero de 32 bits.
Tanto /etc/gss/mech como /etc/gss/qop se crean cuando se instalan los mecanismos de seguridad por primera vez en un sistema dado.
Como muchas de las rutinas de RPC del núcleo utilizan valores de no cadena para representar el mecanismo y la QOP, las aplicaciones pueden utilizar las funciones rpc_gss_mech_to_oid() y rpc_gss_qop_to_num() para obtener los equivalentes de no cadena para estos parámetros, en caso que necesiten aprovechar estas rutinas del núcleo.