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