1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2017 Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 #ifndef GDM_PAM_EXTENSIONS_H
21 #define GDM_PAM_EXTENSIONS_H
22 
23 #include <alloca.h>
24 #include <endian.h>
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 #include <limits.h>
29 
30 #include <security/pam_appl.h>
31 
32 typedef struct {
33         uint32_t length;
34 
35         unsigned char type;
36         unsigned char data[];
37 } GdmPamExtensionMessage;
38 
39 #define GDM_PAM_EXTENSION_MESSAGE_FROM_PAM_MESSAGE(query) (GdmPamExtensionMessage *) (void *) query->msg
40 #define GDM_PAM_EXTENSION_MESSAGE_TO_PAM_REPLY(msg) (char *) (void *) msg
41 #define GDM_PAM_EXTENSION_MESSAGE_TO_BINARY_PROMPT_MESSAGE(extended_message, binary_message) \
42 { \
43         (binary_message)->msg_style = PAM_BINARY_PROMPT; \
44         (binary_message)->msg = (void *) extended_message; \
45 }
46 #define GDM_PAM_EXTENSION_MESSAGE_TRUNCATED(msg) be32toh(msg->length) < sizeof (GdmPamExtensionMessage)
47 #define GDM_PAM_EXTENSION_MESSAGE_INVALID_TYPE(msg) \
48 ({ \
49         bool _invalid = true; \
50         int _n = -1; \
51         const char *_supported_extensions; \
52         _supported_extensions = getenv ("GDM_SUPPORTED_PAM_EXTENSIONS"); \
53         if (_supported_extensions != NULL) { \
54                 const char *_p = _supported_extensions; \
55                 while (*_p != '\0' && _n < UCHAR_MAX) { \
56                         size_t _length; \
57                         _length = strcspn (_p, " "); \
58                         if (_length > 0) \
59                                 _n++; \
60                         _p += _length; \
61                         _length = strspn (_p, " "); \
62                         _p += _length; \
63                 } \
64                 if (_n >= msg->type) \
65                         _invalid = false; \
66         } \
67         _invalid; \
68 })
69 #define GDM_PAM_EXTENSION_MESSAGE_MATCH(msg, supported_extensions, name) (strcmp (supported_extensions[msg->type], name) == 0)
70 
71 /* environment block should be a statically allocated chunk of memory.  This is important because
72  * putenv() will leak otherwise (and setenv isn't thread safe)
73  */
74 #define GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS(environment_block, supported_extensions) \
75 { \
76         size_t _size = 0; \
77         unsigned char _t, _num_chunks; \
78         char *_p; \
79         _p = environment_block; \
80         _p = stpncpy (_p, "GDM_SUPPORTED_PAM_EXTENSIONS", sizeof(environment_block)); \
81         *_p = '\0'; \
82         _size += strlen (_p); \
83         for (_t = 0; supported_extensions[_t] != NULL && _t < UCHAR_MAX; _t++) {\
84                 size_t _next_chunk = strlen (supported_extensions[_t]) + strlen (" "); \
85                 if (_size + _next_chunk >= sizeof (environment_block)) \
86                         break; \
87                 _size += _next_chunk; \
88         }\
89         _num_chunks = _t; \
90         if (_t != 0) { \
91                 _p = stpcpy (_p, "="); \
92                 for (_t = 0; _t < _num_chunks; _t++) { \
93                         if (_t != 0) \
94                                 _p = stpcpy (_p, " "); \
95                         _p = stpcpy (_p, supported_extensions[_t]); \
96                 } \
97                 *_p = '\0'; \
98                 putenv (environment_block); \
99         } \
100 }
101 
102 #define GDM_PAM_EXTENSION_LOOK_UP_TYPE(name, extension_type) \
103 ({ \
104         bool _supported = false; \
105         unsigned char _t = 0; \
106         const char *_supported_extensions; \
107         _supported_extensions = getenv ("GDM_SUPPORTED_PAM_EXTENSIONS"); \
108         if (_supported_extensions != NULL) { \
109                 const char *_p = _supported_extensions; \
110                 while (*_p != '\0') { \
111                         size_t _length; \
112                         _length = strcspn (_p, " "); \
113                         if (strncmp (_p, name, _length) == 0) { \
114                                 _supported = true; \
115                                 break; \
116                         } \
117                         _p += _length; \
118                         _length = strspn (_p, " "); \
119                         _p += _length; \
120                         if (_t >= UCHAR_MAX) { \
121                                 break; \
122                         } \
123                         _t++; \
124                 } \
125                 if (_supported && extension_type != NULL) \
126                         *extension_type = _t; \
127         } \
128         _supported; \
129 })
130 
131 #define GDM_PAM_EXTENSION_SUPPORTED(name) GDM_PAM_EXTENSION_LOOK_UP_TYPE(name, (unsigned char *) NULL)
132 
133 typedef struct {
134         const char *key;
135         const char *text;
136 } GdmChoiceListItems;
137 
138 typedef struct {
139         size_t number_of_items;
140         GdmChoiceListItems items[];
141 } GdmChoiceList;
142 
143 typedef struct {
144         GdmPamExtensionMessage header;
145 
146         char *prompt_message;
147         GdmChoiceList list;
148 } GdmPamExtensionChoiceListRequest;
149 
150 typedef struct {
151         GdmPamExtensionMessage header;
152 
153         char *key;
154 } GdmPamExtensionChoiceListResponse;
155 
156 #define GDM_PAM_EXTENSION_CHOICE_LIST "org.gnome.DisplayManager.UserVerifier.ChoiceList"
157 
158 #define GDM_CHOICE_LIST_SIZE(num_items) (offsetof(GdmChoiceList, items) + (num_items) * sizeof (GdmChoiceListItems))
159 #define GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(num_items) (offsetof(GdmPamExtensionChoiceListRequest, list) + GDM_CHOICE_LIST_SIZE((num_items)))
160 #define GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_INIT(request, title, num_items) \
161 { \
162         int _n = num_items; \
163         GDM_PAM_EXTENSION_LOOK_UP_TYPE (GDM_PAM_EXTENSION_CHOICE_LIST, &request->header.type); \
164         request->header.length = htobe32 (GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(_n)); \
165         request->prompt_message = title; \
166         request->list.number_of_items = _n; \
167 }
168 
169 #define GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE sizeof (GdmPamExtensionChoiceListResponse)
170 #define GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_INIT(response) \
171 { \
172         GDM_PAM_EXTENSION_LOOK_UP_TYPE (GDM_PAM_EXTENSION_CHOICE_LIST, &response->header.type); \
173         response->header.length = htobe32 (GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE); \
174         response->key = NULL; \
175 }
176 #define GDM_PAM_EXTENSION_REPLY_TO_CHOICE_LIST_RESPONSE(reply) ((GdmPamExtensionChoiceListResponse *) (void *) reply->resp)
177 
178 #endif
179