secret.c (8002B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2026 GNUnet e.V. 4 5 GNUnet is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Affero General Public License as published 7 by the Free Software Foundation, either version 3 of the License, 8 or (at your option) any later version. 9 10 GNUnet is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 SPDX-License-Identifier: AGPL3.0-or-later 19 */ 20 /* 21 * @author Tobias Frisch 22 * @file secret.c 23 */ 24 25 #include "secret.h" 26 27 #include <glib-2.0/glib.h> 28 #include <gnunet/gnunet_chat_lib.h> 29 #include <gnunet/gnunet_common.h> 30 #include <string.h> 31 #include <libsecret/secret.h> 32 #include <gnunet/gnunet_util_lib.h> 33 34 #ifndef MESSENGER_APPLICATION_ID 35 #define SECRET_APP_ID "org.gnunet.Messenger" 36 #else 37 #define SECRET_APP_ID MESSENGER_APPLICATION_ID 38 #endif 39 40 const SecretSchema * 41 _secret_schema(void) 42 { 43 static const SecretSchema schema = { 44 "org.gnunet.chat.AccountSecret", SECRET_SCHEMA_NONE, 45 { 46 { "name", SECRET_SCHEMA_ATTRIBUTE_STRING }, 47 { "app_id", SECRET_SCHEMA_ATTRIBUTE_STRING }, 48 { "NULL", 0 }, 49 } 50 }; 51 return &schema; 52 } 53 54 char* 55 _secret_description(const char *name) 56 { 57 char *desc; 58 59 GNUNET_asprintf( 60 &desc, 61 "GNUnet Messenger account secret for identity %s", 62 name 63 ); 64 65 return desc; 66 } 67 68 MESSENGER_SecretOperation* 69 _secret_operation_new(MESSENGER_Application *application, 70 MESSENGER_SecretCallback callback, 71 gpointer user_data) 72 { 73 g_assert(application); 74 75 GCancellable *cancellable = g_cancellable_new(); 76 77 if (!cancellable) 78 return NULL; 79 80 MESSENGER_SecretOperation* op = g_malloc(sizeof(MESSENGER_SecretOperation)); 81 82 op->application = application; 83 84 op->callback = callback; 85 op->cancellable = cancellable; 86 op->user_data = user_data; 87 op->ownership = FALSE; 88 89 op->secret = NULL; 90 op->secret_len = 0; 91 92 application->secrets = g_list_append( 93 application->secrets, 94 op 95 ); 96 97 return op; 98 } 99 100 void 101 _secret_operation_callback(MESSENGER_SecretOperation *op, 102 gboolean success, 103 gboolean error) 104 { 105 g_assert(op); 106 107 if (op->callback) 108 { 109 op->callback( 110 op->application, 111 op->secret, 112 op->secret_len, 113 success, 114 error, 115 op->user_data 116 ); 117 } 118 119 secret_operation_drop(op); 120 } 121 122 void 123 _secret_lookup_callback(GNUNET_UNUSED GObject *source_object, 124 GAsyncResult *result, 125 gpointer data) 126 { 127 GError *error = NULL; 128 gchar *password; 129 130 MESSENGER_SecretOperation *op = data; 131 132 password = secret_password_lookup_finish(result, &error); 133 134 if (error) 135 _secret_operation_callback(op, FALSE, TRUE); 136 else if (password) 137 { 138 op->secret = GNUNET_strdup(password); 139 op->secret_len = g_utf8_strlen(password, -1); 140 141 _secret_operation_callback(op, TRUE, FALSE); 142 143 secret_password_free(password); 144 } 145 else 146 _secret_operation_callback(op, FALSE, FALSE); 147 } 148 149 MESSENGER_SecretOperation* 150 secret_operation_lookup(MESSENGER_Application *application, 151 const char *name, 152 MESSENGER_SecretCallback callback, 153 gpointer user_data) 154 { 155 g_assert((application) && (name)); 156 157 MESSENGER_SecretOperation *op = _secret_operation_new( 158 application, 159 callback, 160 user_data 161 ); 162 163 if (!op) 164 return NULL; 165 166 secret_password_lookup( 167 _secret_schema(), 168 op->cancellable, 169 &_secret_lookup_callback, 170 op, 171 "name", name, 172 "app_id", SECRET_APP_ID, 173 NULL 174 ); 175 176 return op; 177 } 178 179 void 180 _secret_store_callback(GNUNET_UNUSED GObject *source_object, 181 GAsyncResult *result, 182 gpointer data) 183 { 184 GError *error = NULL; 185 gboolean success; 186 187 MESSENGER_SecretOperation *op = data; 188 189 success = secret_password_store_finish(result, &error); 190 191 if (error) 192 _secret_operation_callback(op, FALSE, TRUE); 193 else 194 _secret_operation_callback(op, success, FALSE); 195 } 196 197 MESSENGER_SecretOperation* 198 secret_operation_store(MESSENGER_Application *application, 199 const char *name, 200 const char *secret, 201 uint32_t secret_len, 202 MESSENGER_SecretCallback callback, 203 gpointer user_data) 204 { 205 g_assert((application) && (name) && (secret)); 206 207 if (strlen(secret) != secret_len) 208 return NULL; 209 210 MESSENGER_SecretOperation *op = _secret_operation_new( 211 application, 212 callback, 213 user_data 214 ); 215 216 if (!op) 217 return NULL; 218 219 op->secret = GNUNET_strndup(secret, secret_len + 1); 220 op->secret_len = secret_len; 221 222 secret_password_store( 223 _secret_schema(), 224 SECRET_COLLECTION_DEFAULT, 225 _secret_description(name), 226 secret, 227 op->cancellable, 228 &_secret_store_callback, 229 op, 230 "name", name, 231 "app_id", SECRET_APP_ID, 232 NULL 233 ); 234 235 return op; 236 } 237 238 MESSENGER_SecretOperation* 239 secret_operation_generate(MESSENGER_Application *application, 240 const char *name, 241 MESSENGER_SecretCallback callback, 242 gpointer user_data) 243 { 244 char new_secret [65]; 245 uint32_t secret_len; 246 247 g_assert((application) && (name)); 248 249 secret_len = 64; 250 251 if (GNUNET_OK != GNUNET_CHAT_generate_secret(new_secret, secret_len)) 252 return NULL; 253 254 new_secret[secret_len] = '\0'; 255 256 MESSENGER_SecretOperation *op = secret_operation_store( 257 application, 258 name, 259 new_secret, 260 secret_len, 261 callback, 262 user_data 263 ); 264 265 secret_password_wipe(new_secret); 266 return op; 267 } 268 269 void 270 _secret_delete_callback(GNUNET_UNUSED GObject *source_object, 271 GAsyncResult *result, 272 gpointer data) 273 { 274 GError *error = NULL; 275 gboolean success; 276 277 MESSENGER_SecretOperation *op = data; 278 279 success = secret_password_clear_finish(result, &error); 280 281 if (error) 282 _secret_operation_callback(op, FALSE, TRUE); 283 else 284 _secret_operation_callback(op, success, FALSE); 285 } 286 287 MESSENGER_SecretOperation* 288 secret_operation_delete(MESSENGER_Application *application, 289 const char *name, 290 MESSENGER_SecretCallback callback, 291 gpointer user_data) 292 { 293 g_assert((application) && (name)); 294 295 MESSENGER_SecretOperation *op = _secret_operation_new( 296 application, 297 callback, 298 user_data 299 ); 300 301 if (!op) 302 return NULL; 303 304 secret_password_clear( 305 _secret_schema(), 306 op->cancellable, 307 &_secret_delete_callback, 308 op, 309 "name", name, 310 "app_id", SECRET_APP_ID, 311 NULL 312 ); 313 314 return op; 315 } 316 317 void 318 secret_operation_own_user_data(MESSENGER_SecretOperation *op) 319 { 320 g_assert(op); 321 322 op->ownership = TRUE; 323 } 324 325 void 326 secret_operation_cancel(MESSENGER_SecretOperation *op) 327 { 328 g_assert(op); 329 330 if (!op->cancellable) 331 return; 332 333 if (!g_cancellable_is_cancelled(op->cancellable)) 334 g_cancellable_cancel(op->cancellable); 335 } 336 337 void 338 secret_operation_cleanup(MESSENGER_SecretOperation *op) 339 { 340 g_assert(op); 341 342 if (op->secret) 343 { 344 secret_password_wipe(op->secret); 345 GNUNET_free(op->secret); 346 } 347 348 if ((op->ownership) && (op->user_data)) 349 { 350 g_free(op->user_data); 351 op->user_data = NULL; 352 } 353 354 if (!op->cancellable) 355 return; 356 357 g_object_unref(op->cancellable); 358 op->cancellable = NULL; 359 } 360 361 void 362 secret_operation_drop(MESSENGER_SecretOperation *op) 363 { 364 g_assert(op); 365 366 if (op->application->secrets) 367 op->application->secrets = g_list_remove( 368 op->application->secrets, 369 op 370 ); 371 372 secret_operation_destroy(op); 373 } 374 375 void 376 secret_operation_destroy(MESSENGER_SecretOperation *op) 377 { 378 g_assert(op); 379 380 secret_operation_cleanup(op); 381 g_free(op); 382 }