9 #include <security/pam_appl.h>
18 #include <gio/gunixsocketaddress.h>
20 #define LOGIN_PROMPT "login:"
22 static int console_fd = -1;
24 static GList *user_entries = NULL;
25 static GList *getpwent_link = NULL;
27 static GList *group_entries = NULL;
29 static int active_vt = 7;
39 struct pam_conv conversation;
55 initgroups (const char *user, gid_t group)
66 getgroups (int size, gid_t list[])
68 const gchar *group_list;
72 /* Get groups we are a member of */
73 group_list = g_getenv ("LIGHTDM_TEST_GROUPS");
76 groups = g_strsplit (group_list, ",", -1);
77 groups_length = g_strv_length (groups);
83 if (groups_length > size)
88 for (i = 0; groups[i]; i++)
89 list[i] = atoi (groups[i]);
97 setgroups (size_t size, const gid_t *list)
102 group_list = g_string_new ("");
103 for (i = 0; i < size; i++)
106 g_string_append (group_list, ",");
107 g_string_append_printf (group_list, "%d", list[i]);
109 g_setenv ("LIGHTDM_TEST_GROUPS", group_list->str, TRUE);
110 g_string_free (group_list, TRUE);
128 setresgid (gid_t rgid, gid_t ugid, gid_t sgid)
146 setresuid (uid_t ruid, uid_t uuid, uid_t suid)
152 redirect_path (const gchar *path)
154 if (g_str_has_prefix (path, g_getenv ("LIGHTDM_TEST_ROOT")))
155 return g_strdup (path);
156 else if (strcmp (path, CONFIG_DIR "/lightdm.conf") == 0)
157 return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "lightdm", "lightdm.conf", NULL);
158 else if (g_str_has_prefix (path, "/tmp/"))
159 return g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", path + 5, NULL);
161 return g_strdup (path);
166 open_wrapper (const char *func, const char *pathname, int flags, mode_t mode)
168 int (*_open) (const char *pathname, int flags, mode_t mode);
169 gchar *new_path = NULL;
172 _open = (int (*)(const char *pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, func);
174 if (strcmp (pathname, "/dev/console") == 0)
178 console_fd = _open ("/dev/null", flags, mode);
179 fcntl (console_fd, F_SETFD, FD_CLOEXEC);
184 new_path = redirect_path (pathname);
185 fd = _open (new_path, flags, mode);
192 open (const char *pathname, int flags, ...)
198 va_start (ap, flags);
199 mode = va_arg (ap, int);
202 return open_wrapper ("open", pathname, flags, mode);
206 open64 (const char *pathname, int flags, ...)
212 va_start (ap, flags);
213 mode = va_arg (ap, int);
216 return open_wrapper ("open64", pathname, flags, mode);
220 access (const char *pathname, int mode)
222 int (*_access) (const char *pathname, int mode);
223 gchar *new_path = NULL;
226 _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
228 new_path = redirect_path (pathname);
229 ret = _access (new_path, mode);
236 ioctl (int d, int request, void *data)
238 int (*_ioctl) (int d, int request, void *data);
240 _ioctl = (int (*)(int d, int request, void *data)) dlsym (RTLD_NEXT, "ioctl");
241 if (d > 0 && d == console_fd)
243 struct vt_stat *console_state;
249 console_state = data;
250 console_state->v_active = active_vt;
253 active_vt = GPOINTER_TO_INT (data);
261 return _ioctl (d, request, data);
267 int (*_close) (int fd);
269 if (fd > 0 && fd == console_fd)
272 _close = (int (*)(int fd)) dlsym (RTLD_NEXT, "close");
278 free_user (gpointer data)
280 struct passwd *entry = data;
282 g_free (entry->pw_name);
283 g_free (entry->pw_passwd);
284 g_free (entry->pw_gecos);
285 g_free (entry->pw_dir);
286 g_free (entry->pw_shell);
293 gchar *path, *data = NULL, **lines;
295 GError *error = NULL;
297 g_list_free_full (user_entries, free_user);
299 getpwent_link = NULL;
301 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
302 g_file_get_contents (path, &data, NULL, &error);
305 g_warning ("Error loading passwd file: %s", error->message);
306 g_clear_error (&error);
311 lines = g_strsplit (data, "\n", -1);
314 for (i = 0; lines[i]; i++)
316 gchar *line, **fields;
318 line = g_strstrip (lines[i]);
319 fields = g_strsplit (line, ":", -1);
320 if (g_strv_length (fields) == 7)
322 struct passwd *entry = malloc (sizeof (struct passwd));
324 entry->pw_name = g_strdup (fields[0]);
325 entry->pw_passwd = g_strdup (fields[1]);
326 entry->pw_uid = atoi (fields[2]);
327 entry->pw_gid = atoi (fields[3]);
328 entry->pw_gecos = g_strdup (fields[4]);
329 entry->pw_dir = g_strdup (fields[5]);
330 entry->pw_shell = g_strdup (fields[6]);
331 user_entries = g_list_append (user_entries, entry);
341 if (getpwent_link == NULL)
344 if (user_entries == NULL)
346 getpwent_link = user_entries;
350 if (getpwent_link->next == NULL)
352 getpwent_link = getpwent_link->next;
355 return getpwent_link->data;
361 getpwent_link = NULL;
367 getpwent_link = NULL;
371 getpwnam (const char *name)
380 for (link = user_entries; link; link = link->next)
382 struct passwd *entry = link->data;
383 if (strcmp (entry->pw_name, name) == 0)
399 for (link = user_entries; link; link = link->next)
401 struct passwd *entry = link->data;
402 if (entry->pw_uid == uid)
412 free_group (gpointer data)
414 struct group *entry = data;
416 g_free (entry->gr_name);
417 g_free (entry->gr_passwd);
418 g_strfreev (entry->gr_mem);
425 gchar *path, *data = NULL, **lines;
427 GError *error = NULL;
429 g_list_free_full (group_entries, free_group);
430 group_entries = NULL;
432 path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "group", NULL);
433 g_file_get_contents (path, &data, NULL, &error);
436 g_warning ("Error loading group file: %s", error->message);
437 g_clear_error (&error);
442 lines = g_strsplit (data, "\n", -1);
445 for (i = 0; lines[i]; i++)
447 gchar *line, **fields;
449 line = g_strstrip (lines[i]);
450 fields = g_strsplit (line, ":", -1);
451 if (g_strv_length (fields) == 4)
453 struct group *entry = malloc (sizeof (struct group));
455 entry->gr_name = g_strdup (fields[0]);
456 entry->gr_passwd = g_strdup (fields[1]);
457 entry->gr_gid = atoi (fields[2]);
458 entry->gr_mem = g_strsplit (fields[3], ",", -1);
459 group_entries = g_list_append (group_entries, entry);
467 getgrnam (const char *name)
473 for (link = group_entries; link; link = link->next)
475 struct group *entry = link->data;
476 if (strcmp (entry->gr_name, name) == 0)
492 for (link = group_entries; link; link = link->next)
494 struct group *entry = link->data;
495 if (entry->gr_gid == gid)
505 pam_start (const char *service_name, const char *user, const struct pam_conv *conversation, pam_handle_t **pamh)
507 pam_handle_t *handle;
509 if (service_name == NULL || conversation == NULL || pamh == NULL)
510 return PAM_SYSTEM_ERR;
512 handle = *pamh = malloc (sizeof (pam_handle_t));
516 handle->service_name = strdup (service_name);
517 handle->user = user ? strdup (user) : NULL;
518 handle->authtok = NULL;
519 handle->ruser = NULL;
521 handle->conversation.conv = conversation->conv;
522 handle->conversation.appdata_ptr = conversation->appdata_ptr;
523 handle->envlist = malloc (sizeof (char *) * 1);
524 handle->envlist[0] = NULL;
530 send_info (pam_handle_t *pamh, const char *message)
532 struct pam_message **msg;
533 struct pam_response *resp = NULL;
535 msg = calloc (1, sizeof (struct pam_message *));
536 msg[0] = malloc (sizeof (struct pam_message));
537 msg[0]->msg_style = PAM_TEXT_INFO;
538 msg[0]->msg = message;
539 pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
551 pam_authenticate (pam_handle_t *pamh, int flags)
553 struct passwd *entry;
554 gboolean password_matches = FALSE;
557 return PAM_SYSTEM_ERR;
559 if (strcmp (pamh->service_name, "test-remote") == 0)
562 struct pam_message **msg;
563 struct pam_response *resp = NULL;
565 msg = malloc (sizeof (struct pam_message *) * 1);
566 msg[0] = malloc (sizeof (struct pam_message));
567 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
568 msg[0]->msg = "remote-login:";
569 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
572 if (result != PAM_SUCCESS)
577 if (resp[0].resp == NULL)
585 pamh->ruser = strdup (resp[0].resp);
589 msg = malloc (sizeof (struct pam_message *) * 1);
590 msg[0] = malloc (sizeof (struct pam_message));
591 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
592 msg[0]->msg = "remote-password:";
593 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
596 if (result != PAM_SUCCESS)
601 if (resp[0].resp == NULL)
608 free (pamh->authtok);
609 pamh->authtok = strdup (resp[0].resp);
613 password_matches = strcmp (pamh->ruser, "remote-user") == 0 && strcmp (pamh->authtok, "password") == 0;
615 if (password_matches)
621 /* Prompt for username */
622 if (pamh->user == NULL)
625 struct pam_message **msg;
626 struct pam_response *resp = NULL;
628 msg = malloc (sizeof (struct pam_message *) * 1);
629 msg[0] = malloc (sizeof (struct pam_message));
630 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
631 msg[0]->msg = LOGIN_PROMPT;
632 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
635 if (result != PAM_SUCCESS)
640 if (resp[0].resp == NULL)
646 pamh->user = strdup (resp[0].resp);
651 if (strcmp (pamh->user, "log-pam") == 0)
652 send_info (pamh, "pam_authenticate");
654 /* Crash on authenticate */
655 if (strcmp (pamh->user, "crash-authenticate") == 0)
656 kill (getpid (), SIGSEGV);
658 /* Look up password database */
659 entry = getpwnam (pamh->user);
661 /* Prompt for password if required */
662 if (entry && strcmp (pamh->user, "always-password") != 0 && (strcmp (pamh->service_name, "lightdm-autologin") == 0 || strcmp (entry->pw_passwd, "") == 0))
663 password_matches = TRUE;
666 int i, n_messages = 0, password_index, result;
667 struct pam_message **msg;
668 struct pam_response *resp = NULL;
670 msg = malloc (sizeof (struct pam_message *) * 5);
671 if (strcmp (pamh->user, "info-prompt") == 0)
673 msg[n_messages] = malloc (sizeof (struct pam_message));
674 msg[n_messages]->msg_style = PAM_TEXT_INFO;
675 msg[n_messages]->msg = "Welcome to LightDM";
678 if (strcmp (pamh->user, "multi-info-prompt") == 0)
680 msg[n_messages] = malloc (sizeof (struct pam_message));
681 msg[n_messages]->msg_style = PAM_TEXT_INFO;
682 msg[n_messages]->msg = "Welcome to LightDM";
684 msg[n_messages] = malloc (sizeof (struct pam_message));
685 msg[n_messages]->msg_style = PAM_ERROR_MSG;
686 msg[n_messages]->msg = "This is an error";
688 msg[n_messages] = malloc (sizeof (struct pam_message));
689 msg[n_messages]->msg_style = PAM_TEXT_INFO;
690 msg[n_messages]->msg = "You should have seen three messages";
693 if (strcmp (pamh->user, "multi-prompt") == 0)
695 msg[n_messages] = malloc (sizeof (struct pam_message));
696 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_ON;
697 msg[n_messages]->msg = "Favorite Color:";
700 msg[n_messages] = malloc (sizeof (struct pam_message));
701 msg[n_messages]->msg_style = PAM_PROMPT_ECHO_OFF;
702 msg[n_messages]->msg = "Password:";
703 password_index = n_messages;
705 result = pamh->conversation.conv (n_messages, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
706 for (i = 0; i < n_messages; i++)
709 if (result != PAM_SUCCESS)
714 if (resp[password_index].resp == NULL)
721 password_matches = strcmp (entry->pw_passwd, resp[password_index].resp) == 0;
723 if (password_matches && strcmp (pamh->user, "multi-prompt") == 0)
724 password_matches = strcmp ("blue", resp[0].resp) == 0;
726 for (i = 0; i < n_messages; i++)
733 /* Do two factor authentication */
734 if (password_matches && strcmp (pamh->user, "two-factor") == 0)
736 msg = malloc (sizeof (struct pam_message *) * 1);
737 msg[0] = malloc (sizeof (struct pam_message));
738 msg[0]->msg_style = PAM_PROMPT_ECHO_ON;
739 msg[0]->msg = "OTP:";
741 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
747 if (resp[0].resp == NULL)
752 password_matches = strcmp (resp[0].resp, "otp") == 0;
758 /* Special user has home directory created on login */
759 if (password_matches && strcmp (pamh->user, "mount-home-dir") == 0)
760 g_mkdir_with_parents (entry->pw_dir, 0755);
762 /* Special user 'change-user1' changes user on authentication */
763 if (password_matches && strcmp (pamh->user, "change-user1") == 0)
766 pamh->user = g_strdup ("change-user2");
769 /* Special user 'change-user-invalid' changes to an invalid user on authentication */
770 if (password_matches && strcmp (pamh->user, "change-user-invalid") == 0)
773 pamh->user = g_strdup ("invalid-user");
776 if (password_matches)
783 get_env_value (const char *name_value, const char *name)
787 for (j = 0; name[j] && name[j] != '=' && name[j] == name_value[j]; j++);
788 if (name_value[j] == '=')
789 return &name_value[j + 1];
795 pam_putenv (pam_handle_t *pamh, const char *name_value)
799 if (pamh == NULL || name_value == NULL)
800 return PAM_SYSTEM_ERR;
802 for (i = 0; pamh->envlist[i]; i++)
804 if (get_env_value (pamh->envlist[i], name_value))
808 if (pamh->envlist[i])
810 free (pamh->envlist[i]);
811 pamh->envlist[i] = strdup (name_value);
815 pamh->envlist = realloc (pamh->envlist, sizeof (char *) * (i + 2));
816 pamh->envlist[i] = strdup (name_value);
817 pamh->envlist[i + 1] = NULL;
824 pam_getenv (pam_handle_t *pamh, const char *name)
828 if (pamh == NULL || name == NULL)
831 for (i = 0; pamh->envlist[i]; i++)
834 value = get_env_value (pamh->envlist[i], name);
843 pam_getenvlist (pam_handle_t *pamh)
848 return pamh->envlist;
852 pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
854 if (pamh == NULL || item == NULL)
855 return PAM_SYSTEM_ERR;
862 pamh->tty = strdup ((const char *) item);
871 pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
873 if (pamh == NULL || item == NULL)
874 return PAM_SYSTEM_ERR;
879 *item = pamh->service_name;
887 *item = pamh->authtok;
894 case PAM_USER_PROMPT:
895 *item = LOGIN_PROMPT;
903 *item = &pamh->conversation;
912 pam_open_session (pam_handle_t *pamh, int flags)
915 return PAM_SYSTEM_ERR;
917 if (strcmp (pamh->user, "session-error") == 0)
918 return PAM_SESSION_ERR;
920 if (strcmp (pamh->user, "log-pam") == 0)
921 send_info (pamh, "pam_open_session");
923 if (strcmp (pamh->user, "make-home-dir") == 0)
925 struct passwd *entry;
926 entry = getpwnam (pamh->user);
927 g_mkdir_with_parents (entry->pw_dir, 0755);
934 pam_close_session (pam_handle_t *pamh, int flags)
937 return PAM_SYSTEM_ERR;
939 if (strcmp (pamh->user, "log-pam") == 0)
940 send_info (pamh, "pam_close_session");
946 pam_acct_mgmt (pam_handle_t *pamh, int flags)
949 return PAM_SYSTEM_ERR;
952 return PAM_USER_UNKNOWN;
954 if (strcmp (pamh->user, "log-pam") == 0)
955 send_info (pamh, "pam_acct_mgmt");
957 if (strcmp (pamh->user, "denied") == 0)
958 return PAM_PERM_DENIED;
959 if (strcmp (pamh->user, "expired") == 0)
960 return PAM_ACCT_EXPIRED;
961 if (strcmp (pamh->user, "new-authtok") == 0)
962 return PAM_NEW_AUTHTOK_REQD;
968 pam_chauthtok (pam_handle_t *pamh, int flags)
970 struct passwd *entry;
972 struct pam_message **msg;
973 struct pam_response *resp = NULL;
976 return PAM_SYSTEM_ERR;
978 if (strcmp (pamh->user, "log-pam") == 0)
979 send_info (pamh, "pam_chauthtok");
981 msg = malloc (sizeof (struct pam_message *) * 1);
982 msg[0] = malloc (sizeof (struct pam_message));
983 msg[0]->msg_style = PAM_PROMPT_ECHO_OFF;
984 msg[0]->msg = "Enter new password:";
985 result = pamh->conversation.conv (1, (const struct pam_message **) msg, &resp, pamh->conversation.appdata_ptr);
988 if (result != PAM_SUCCESS)
993 if (resp[0].resp == NULL)
999 /* Update password database */
1000 entry = getpwnam (pamh->user);
1001 free (entry->pw_passwd);
1002 entry->pw_passwd = resp[0].resp;
1009 pam_setcred (pam_handle_t *pamh, int flags)
1014 return PAM_SYSTEM_ERR;
1016 if (strcmp (pamh->user, "log-pam") == 0)
1017 send_info (pamh, "pam_setcred");
1019 /* Put the test directories into the path */
1020 e = g_strdup_printf ("PATH=%s/tests/src/.libs:%s/tests/src:%s/tests/src:%s/src:%s", BUILDDIR, BUILDDIR, SRCDIR, BUILDDIR, pam_getenv (pamh, "PATH"));
1021 pam_putenv (pamh, e);
1024 if (strcmp (pamh->user, "cred-error") == 0)
1025 return PAM_CRED_ERR;
1026 if (strcmp (pamh->user, "cred-expired") == 0)
1027 return PAM_CRED_EXPIRED;
1028 if (strcmp (pamh->user, "cred-unavail") == 0)
1029 return PAM_CRED_UNAVAIL;
1031 /* Join special groups if requested */
1032 if (strcmp (pamh->user, "group-member") == 0 && flags & PAM_ESTABLISH_CRED)
1034 struct group *group;
1038 group = getgrnam ("test-group");
1041 groups_length = getgroups (0, NULL);
1042 groups = malloc (sizeof (gid_t) * (groups_length + 1));
1043 groups_length = getgroups (groups_length, groups);
1044 groups[groups_length] = group->gr_gid;
1046 setgroups (groups_length, groups);
1050 /* We need to pass our group overrides down the child process - the environment via PAM seems the only way to do it easily */
1051 pam_putenv (pamh, g_strdup_printf ("LIGHTDM_TEST_GROUPS=%s", g_getenv ("LIGHTDM_TEST_GROUPS")));
1058 pam_end (pam_handle_t *pamh, int pam_status)
1061 return PAM_SYSTEM_ERR;
1063 free (pamh->service_name);
1067 free (pamh->authtok);
1078 pam_strerror (pam_handle_t *pamh, int errnum)
1088 return "Critical error - immediate abort";
1090 return "Failed to load module";
1091 case PAM_SYMBOL_ERR:
1092 return "Symbol not found";
1093 case PAM_SERVICE_ERR:
1094 return "Error in service module";
1095 case PAM_SYSTEM_ERR:
1096 return "System error";
1098 return "Memory buffer error";
1099 case PAM_PERM_DENIED:
1100 return "Permission denied";
1102 return "Authentication failure";
1103 case PAM_CRED_INSUFFICIENT:
1104 return "Insufficient credentials to access authentication data";
1105 case PAM_AUTHINFO_UNAVAIL:
1106 return "Authentication service cannot retrieve authentication info";
1107 case PAM_USER_UNKNOWN:
1108 return "User not known to the underlying authentication module";
1110 return "Have exhausted maximum number of retries for service";
1111 case PAM_NEW_AUTHTOK_REQD:
1112 return "Authentication token is no longer valid; new one required";
1113 case PAM_ACCT_EXPIRED:
1114 return "User account has expired";
1115 case PAM_SESSION_ERR:
1116 return "Cannot make/remove an entry for the specified session";
1117 case PAM_CRED_UNAVAIL:
1118 return "Authentication service cannot retrieve user credentials";
1119 case PAM_CRED_EXPIRED:
1120 return "User credentials expired";
1122 return "Failure setting user credentials";
1123 case PAM_NO_MODULE_DATA:
1124 return "No module specific data is present";
1126 return "Bad item passed to pam_*_item()";
1128 return "Conversation error";
1129 case PAM_AUTHTOK_ERR:
1130 return "Authentication token manipulation error";
1131 case PAM_AUTHTOK_RECOVERY_ERR:
1132 return "Authentication information cannot be recovered";
1133 case PAM_AUTHTOK_LOCK_BUSY:
1134 return "Authentication token lock busy";
1135 case PAM_AUTHTOK_DISABLE_AGING:
1136 return "Authentication token aging disabled";
1138 return "Failed preliminary check by password service";
1140 return "The return value should be ignored by PAM dispatch";
1141 case PAM_MODULE_UNKNOWN:
1142 return "Module is unknown";
1143 case PAM_AUTHTOK_EXPIRED:
1144 return "Authentication token expired";
1145 case PAM_CONV_AGAIN:
1146 return "Conversation is waiting for event";
1147 case PAM_INCOMPLETE:
1148 return "Application needs to call libpam again";
1150 return "Unknown PAM error";
1160 pututxline (struct utmp *ut)
1170 struct xcb_connection_t
1178 xcb_connect_to_display_with_auth_info (const char *display, xcb_auth_info_t *auth, int *screen)
1180 xcb_connection_t *c;
1182 GSocketAddress *address;
1183 GError *error = NULL;
1185 c = malloc (sizeof (xcb_connection_t));
1186 c->display = g_strdup (display);
1189 if (display == NULL)
1190 display = getenv ("DISPLAY");
1191 if (display == NULL)
1192 c->error = XCB_CONN_CLOSED_PARSE_ERR;
1196 c->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
1198 g_printerr ("%s\n", error->message);
1199 g_clear_error (&error);
1200 if (c->socket == NULL)
1201 c->error = XCB_CONN_ERROR;
1208 /* Skip the hostname, we'll assume it's localhost */
1209 d = strchr (display, ':');
1211 socket_path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", d, NULL);
1212 address = g_unix_socket_address_new (socket_path);
1213 g_free (socket_path);
1214 if (!g_socket_connect (c->socket, address, NULL, &error))
1215 c->error = XCB_CONN_ERROR;
1217 g_printerr ("%s\n", error->message);
1218 g_clear_error (&error);
1221 // FIXME: Send auth info
1226 g_object_unref (address);
1232 xcb_connect (const char *displayname, int *screenp)
1234 return xcb_connect_to_display_with_auth_info(displayname, NULL, screenp);
1238 xcb_connection_has_error (xcb_connection_t *c)
1244 xcb_disconnect (xcb_connection_t *c)
1248 g_object_unref (c->socket);