/* return list of current uids of local active sessions */ static GSList *uids_with_local_active_session(const char *own_id) { GSList *list = NULL; GKeyFile *keyfile; keyfile = g_key_file_new(); if (g_key_file_load_from_file(keyfile, "/var/run/ConsoleKit/database", 0, NULL)) { gchar **groups; groups = g_key_file_get_groups(keyfile, NULL); if (groups != NULL) { int i; for (i = 0; groups[i] != NULL; i++) { uid_t u; if (!g_str_has_prefix(groups[i], "Session ")) continue; if (own_id != NULL &&g_str_has_suffix(groups[i], own_id)) continue; if (!g_key_file_get_boolean(keyfile, groups[i], "is_local", NULL)) continue; if (!g_key_file_get_boolean(keyfile, groups[i], "is_active", NULL)) continue; u = g_key_file_get_integer(keyfile, groups[i], "uid", NULL); if (u > 0 && !uid_in_list(list, u)) list = g_slist_prepend(list, GUINT_TO_POINTER(u)); } g_strfreev(groups); } } g_key_file_free(keyfile); return list; }
/** * Processes an incoming REG_CONF message. * Expected in response to a REGISTER when encryption is disabled. */ void handle_regconf(struct group_list_t *group, const unsigned char *message, unsigned meslen) { const struct regconf_h *regconf; const uint32_t *addrlist; int addrcnt; regconf = (const struct regconf_h *)message; addrlist = (const uint32_t *)(message + (regconf->hlen * 4)); if ((meslen < (regconf->hlen * 4U)) || ((regconf->hlen * 4U) < sizeof(struct regconf_h))) { glog1(group, "Rejecting REG_CONF from server: invalid message size"); return; } addrcnt = (meslen - (regconf->hlen * 4)) / 4; if (uid_in_list(addrlist, addrcnt)) { glog2(group, "Registration confirmed"); group->phase = PHASE_MIDGROUP; set_timeout(group, 0); } if (group->restart) { read_restart_file(group); } }
static void remove_uid (uid_t uid, const char *remove_session_id) { /* * Remove ACL for given uid from all matching devices * when there is currently no local active session. */ GSList *list; list = uids_with_local_active_session(remove_session_id); if (!uid_in_list(list, uid)) apply_acl_to_devices(uid, 0); g_slist_free(list); }
/** * Read in the contents of a FILEINFO message * Returns 1 on success, 0 on error or ignore */ int read_fileinfo(struct group_list_t *group, const unsigned char *message, int meslen, struct timeval rxtime) { const struct fileinfo_h *fileinfo; const uint32_t *addrlist; int listlen, maxsecsize; const char *name, *flink, *p; fileinfo = (const struct fileinfo_h *)message; addrlist = (const uint32_t *)(message + (fileinfo->hlen * 4)); name = (const char *)message + sizeof(struct fileinfo_h); flink = name + (fileinfo->namelen * 4); listlen = (meslen - (fileinfo->hlen * 4)) / 4; if ((meslen < (fileinfo->hlen * 4)) || ((fileinfo->hlen * 4) < sizeof(struct fileinfo_h)) || ((fileinfo->namelen * 4) > MAXPATHNAME) || ((fileinfo->linklen * 4) > MAXPATHNAME) || ((fileinfo->hlen * 4) != sizeof(struct fileinfo_h) + (fileinfo->namelen * 4) + (fileinfo->linklen * 4))) { glog1(group, "Rejecting FILEINFO from server: invalid message size"); send_abort(group, "Rejecting FILEINFO: invalid message size"); return 0; } if (!uid_in_list(addrlist, listlen)) { set_timeout(group, 0); return 0; } if (group->phase == PHASE_RECEIVING) { // We already got the FILEINFO, so no need to reprocess. // Just resend the INFO_ACK and reset the timeout send_fileinfo_ack(group, group->fileinfo.restart); set_timeout(group, 0); return 0; } if ((group->phase == PHASE_MIDGROUP) && (group->file_id == ntohs(fileinfo->file_id))) { // We already got the FILEINFO, and it's for a completed file. // So resend the COMPLETE and reset the timeout send_complete(group, (fileinfo->ftype == FTYPE_FREESPACE)); set_timeout(group, 0); return 0; } // Load fileinfo params into list memset(&group->fileinfo, 0, sizeof(struct file_t)); group->fileinfo.ftype = fileinfo->ftype; group->file_id = ntohs(fileinfo->file_id); strncpy(group->fileinfo.name, name, fileinfo->namelen * 4); strncpy(group->fileinfo.linkname, flink, fileinfo->linklen * 4); group->fileinfo.size = (f_offset_t)ntohs(fileinfo->hifsize) << 32; group->fileinfo.size |= ntohl(fileinfo->lofsize); if (group->fileinfo.size) { maxsecsize = (group->blocksize * 8 > MAXSECTION ? MAXSECTION : group->blocksize * 8); group->fileinfo.blocks = (int32_t)((group->fileinfo.size / group->blocksize) + (group->fileinfo.size % group->blocksize ? 1 : 0)); group->fileinfo.sections = (group->fileinfo.blocks / maxsecsize) + (group->fileinfo.blocks % maxsecsize ? 1 : 0); group->fileinfo.secsize_small = group->fileinfo.blocks / group->fileinfo.sections; group->fileinfo.secsize_big = group->fileinfo.secsize_small + (group->fileinfo.blocks % group->fileinfo.sections ? 1 : 0); group->fileinfo.big_sections = group->fileinfo.blocks - (group->fileinfo.secsize_small * group->fileinfo.sections); } else { group->fileinfo.blocks = 0; group->fileinfo.sections = 0; group->fileinfo.secsize_small = 0; group->fileinfo.secsize_big = 0; group->fileinfo.big_sections = 0; } group->fileinfo.tstamp = ntohl(fileinfo->ftstamp); group->last_server_ts.tv_sec = ntohl(fileinfo->tstamp_sec); group->last_server_ts.tv_usec = ntohl(fileinfo->tstamp_usec); group->last_server_rx_ts = rxtime; group->fileinfo.fd = -1; // Run some checks on the filename if (strlen(group->fileinfo.name) == 0) { glog1(group, "Rejecting FILEINFO from server: blank file name"); early_complete(group, COMP_STAT_REJECTED, 0); return 0; } p = strstr(group->fileinfo.name, ".."); if ((p != NULL) && ((p[2] == '\x0') || (p[2] == '/') || (p[2] == '\\')) && ((p == group->fileinfo.name) || (p[-1] == '/') || (p[-1] == '\\'))) { glog1(group, "Rejecting FILEINFO from server: filename contains .."); early_complete(group, COMP_STAT_REJECTED, 0); return 0; } if (fileinfo->ftype == FTYPE_LINK) { if (strlen(group->fileinfo.linkname) == 0) { glog1(group, "Rejecting FILEINFO from server: blank link name"); early_complete(group, COMP_STAT_REJECTED, 0); return 0; } } return 1; }
int main (int argc, char* argv[]) { static const struct option options[] = { { "action", required_argument, NULL, 'a' }, { "device", required_argument, NULL, 'D' }, { "user", required_argument, NULL, 'u' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, {} }; int action = -1; const char *device = NULL; bool uid_given = false; uid_t uid = 0; uid_t uid2 = 0; const char* remove_session_id = NULL; int rc = 0; /* valgrind is more important to us than a slice allocator */ g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, 1); while (1) { int option; option = getopt_long(argc, argv, "+a:D:u:dh", options, NULL); if (option == -1) break; switch (option) { case 'a': if (strcmp(optarg, "remove") == 0) action = ACTION_REMOVE; else action = ACTION_ADD; break; case 'D': device = optarg; break; case 'u': uid_given = true; uid = strtoul(optarg, NULL, 10); break; case 'd': debug = 1; break; case 'h': printf("Usage: udev-acl --action=ACTION [--device=DEVICEFILE] [--user=UID]\n\n"); goto out; } } if (action < 0 && device == NULL && !uid_given) if (!consolekit_called(argv[optind], &uid, &uid2, &remove_session_id, &action)) uid_given = true; if (action < 0) { fprintf(stderr, "missing action\n\n"); rc = 2; goto out; } if (device != NULL && uid_given) { fprintf(stderr, "only one option, --device=DEVICEFILE or --user=UID expected\n\n"); rc = 3; goto out; } if (uid_given) { switch (action) { case ACTION_ADD: /* Add ACL for given uid to all matching devices. */ apply_acl_to_devices(uid, 1); break; case ACTION_REMOVE: remove_uid(uid, remove_session_id); break; case ACTION_CHANGE: remove_uid(uid, remove_session_id); apply_acl_to_devices(uid2, 1); break; case ACTION_NONE: goto out; break; default: g_assert_not_reached(); break; } } else if (device != NULL) { /* * Add ACLs for all current session uids to a given device. * * Or remove ACLs for uids which do not have any current local * active session. Remove is not really interesting, because in * most cases the device node is removed anyway. */ GSList *list; GSList *l; list = uids_with_local_active_session(NULL); for (l = list; l != NULL; l = g_slist_next(l)) { uid_t u; u = GPOINTER_TO_UINT(l->data); if (action == ACTION_ADD || !uid_in_list(list, u)) set_facl(device, u, action == ACTION_ADD); } g_slist_free(list); } else { fprintf(stderr, "--device=DEVICEFILE or --user=UID expected\n\n"); rc = 3; } out: return rc; }
/** * Processes a new incoming ANNOUNCE */ void handle_announce(union sockaddr_u *src, unsigned char *packet, unsigned packetlen, struct timeval rxtime) { struct uftp_h *header; struct announce_h *announce; uint32_t *addrlist; int addrlen, rval; struct group_list_t *group; time_t t; struct tm *start_time; char privname[INET6_ADDRSTRLEN], srcname[INET6_ADDRSTRLEN]; char srcfqdn[DESTNAME_LEN]; header = (struct uftp_h *)packet; announce = (struct announce_h *)(packet + sizeof(struct uftp_h)); addrlist = (uint32_t *)((unsigned char *)announce + (announce->hlen * 4)); addrlen = (packetlen - sizeof(struct uftp_h) - (announce->hlen * 4)) / 4; if ((packetlen < sizeof(struct uftp_h) + (announce->hlen * 4U)) || ((announce->hlen * 4U) < sizeof(struct announce_h))) { log1(ntohl(header->group_id), header->group_inst, 0, "Rejecting ANNOUNCE from %08X: invalid message size", ntohl(header->src_id)); return; } if ((addrlen != 0) && (!uid_in_list(addrlist, addrlen))) { log1(ntohl(header->group_id), header->group_inst, 0, "Name not in host list"); return; } if ((group = find_open_slot()) == NULL ) { log0(ntohl(header->group_id), header->group_inst, 0, "Error: maximum number of incoming files exceeded: %d\n", MAXLIST); return; } t = time(NULL); start_time = localtime(&t); snprintf(group->start_date, sizeof(group->start_date), "%04d%02d%02d", start_time->tm_year + 1900, start_time->tm_mon + 1, start_time->tm_mday); snprintf(group->start_time, sizeof(group->start_time), "%02d%02d%02d", start_time->tm_hour, start_time->tm_min, start_time->tm_sec); if (!read_announce(group, packet, src, rxtime, packetlen)) { return; } if ((rval = getnameinfo((struct sockaddr *)src, family_len(*src), srcname, sizeof(srcname), NULL, 0, NI_NUMERICHOST)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } if (!noname) { if ((rval = getnameinfo((struct sockaddr *)src, family_len(*src), srcfqdn, sizeof(srcfqdn), NULL, 0, 0)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } } else { strncpy(srcfqdn, srcname, sizeof(srcfqdn) - 1); } if ((rval = getnameinfo((struct sockaddr *)&group->multi, family_len(group->multi), privname, sizeof(privname), NULL, 0, NI_NUMERICHOST)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } glog2(group, "Received request from %08X at %s (%s)", ntohl(group->src_id), srcfqdn, srcname); glog2(group, "Using private multicast address %s", privname); glog3(group, "grtt = %.6f", group->grtt); glog3(group, "send time: %d.%06d", group->last_server_ts.tv_sec, group->last_server_ts.tv_usec); glog3(group, "receive time: %d.%06d", group->last_server_rx_ts.tv_sec, group->last_server_rx_ts.tv_usec); if (status_file) { fprintf(status_file, "CONNECT;%04d/%02d/%02d-%02d:%02d:%02d;%08X;%08X;%s;%s\n", start_time->tm_year + 1900, start_time->tm_mon + 1, start_time->tm_mday, start_time->tm_hour, start_time->tm_min, start_time->tm_sec, ntohl(group->src_id), group->group_id, srcname, srcfqdn); fflush(status_file); } if (group->restart) { if (group->sync_mode) { glog1(group, "Sync mode and restart mode incompatible"); send_abort(group, "Sync mode and restart mode incompatible"); return; } } if (!addr_blank(&group->multi)) { if (server_count > 0) { if (!is_multicast(&group->multi, 1)) { glog1(group, "Invalid source specific multicast address: %s", privname); send_abort(group, "Invalid source specific multicast address"); return; } if (!other_mcast_users(group)) { if (!multicast_join(listener, group->group_id, &group->multi, m_interface, interface_count, server_keys, server_count)) { send_abort(group, "Error joining multicast group"); return; } if (has_proxy) { if (!multicast_join(listener,group->group_id, &group->multi, m_interface, interface_count, &proxy_info, 1)) { send_abort(group, "Error joining multicast group"); return; } } } } else { if (!is_multicast(&group->multi, 0)) { glog1(group, "Invalid multicast address: %s", privname); send_abort(group, "Invalid multicast address"); return; } if (!other_mcast_users(group)) { if (!multicast_join(listener, group->group_id, &group->multi, m_interface, interface_count, NULL, 0)) { send_abort(group, "Error joining multicast group"); return; } } } group->multi_join = 1; } send_register(group); }