1 /* cvm/client.c - CVM client library
2  * Copyright (C) 2010  Bruce Guenter <bruce@untroubled.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 #include <sys/types.h>
19 #include <netdb.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 
26 #include <bglibs/sysdeps.h>
27 #include <bglibs/socket.h>
28 
29 #include "v1client.h"
30 #include "protocol.h"
31 
32 const char* cvm_client_account_split_chars = "@";
33 
34 static struct cvm_packet request;
35 static struct cvm_packet response;
36 
37 /* Packet management code ****************************************************/
parse_packet(struct cvm_packet * p)38 static int parse_packet(struct cvm_packet* p)
39 {
40   if (p->data[0] != 0)
41     return p->data[0];
42   if (p->length < 3
43       || p->data[p->length-1] != 0
44       || p->data[p->length-2] != 0)
45     return CVME_BAD_MODDATA;
46   if (cvm_client_fact_str(CVM_FACT_USERNAME, &cvm_fact_username) ||
47       cvm_client_fact_uint(CVM_FACT_USERID, &cvm_fact_userid) ||
48       cvm_client_fact_uint(CVM_FACT_GROUPID, &cvm_fact_groupid) ||
49       cvm_client_fact_str(CVM_FACT_DIRECTORY, &cvm_fact_directory))
50     return CVME_BAD_MODDATA;
51   cvm_client_fact_str(CVM_FACT_SHELL, &cvm_fact_shell);
52   cvm_client_fact_str(CVM_FACT_REALNAME, &cvm_fact_realname);
53   cvm_client_fact_str(CVM_FACT_GROUPNAME, &cvm_fact_groupname);
54   cvm_client_fact_str(CVM_FACT_SYS_USERNAME, &cvm_fact_sys_username);
55   cvm_client_fact_str(CVM_FACT_SYS_DIRECTORY, &cvm_fact_sys_directory);
56   cvm_client_fact_str(CVM_FACT_DOMAIN, &cvm_fact_domain);
57   cvm_client_fact_str(CVM_FACT_MAILBOX, &cvm_fact_mailbox);
58   return 0;
59 }
60 
packet_add(struct cvm_packet * p,const char * str,unsigned len)61 static int packet_add(struct cvm_packet* p,
62 		      const char* str, unsigned len)
63 {
64   unsigned char* ptr;
65   if (p->length + len + 1 >= CVM_BUFSIZE-1)
66     return 0;
67   ptr = p->data + p->length;
68   memcpy(ptr, str, len);
69   ptr[len] = 0;
70   p->length += len + 1;
71   return 1;
72 }
73 
build_packet(struct cvm_packet * p,const char * account,const char * domain,const char ** credentials,int parse_domain)74 static unsigned build_packet(struct cvm_packet* p,
75 			     const char* account, const char* domain,
76 			     const char** credentials, int parse_domain)
77 {
78   unsigned i;
79   unsigned actlen;
80 
81   p->data[0] = CVM1_PROTOCOL;
82   p->length = 1;
83 
84   actlen = strlen(account);
85   if (parse_domain) {
86     const char* sc;
87     if ((sc = getenv("CVM_ACCOUNT_SPLIT_CHARS")) == 0)
88       sc = cvm_client_account_split_chars;
89     i = strlen(account);
90     while (i-- > 0) {
91       if (strchr(sc, account[i]) != 0) {
92 	domain = account + i + 1;
93 	actlen = i;
94 	break;
95       }
96     }
97   }
98 
99   if (!packet_add(p, account, actlen)) return 0;
100   if (!packet_add(p, domain, strlen(domain))) return 0;
101 
102   for (i = 0; credentials[i] != 0; i++)
103     if (!packet_add(p, credentials[i], strlen(credentials[i])))
104       return 0;
105 
106   p->data[p->length++] = 0;
107   return 1;
108 }
109 
cvm_client_fact_str(unsigned number,const char ** data)110 int cvm_client_fact_str(unsigned number, const char** data)
111 {
112   static unsigned char* ptr = 0;
113   static unsigned last_number = -1;
114 
115   if (!ptr || number != last_number)
116     ptr = response.data+1;
117   last_number = number;
118 
119   while (*ptr) {
120     unsigned char* tmp = ptr;
121     ptr += strlen((char*)ptr) + 1;
122     if (*tmp == number) {
123       *data = (char*)tmp + 1;
124       return 0;
125     }
126   }
127   return CVME_NOFACT;
128 }
129 
cvm_client_fact_uint(unsigned number,unsigned long * data)130 int cvm_client_fact_uint(unsigned number, unsigned long* data)
131 {
132   const char* str;
133   unsigned long i;
134   int err;
135 
136   if ((err = cvm_client_fact_str(number, &str)) != 0) return err;
137 
138   for (i = 0; *str >= '0' && *str <= '9'; ++str) {
139     unsigned long tmp = i;
140     i = (i * 10) + (*str - '0');
141     if (i < tmp)
142       return CVME_BAD_MODDATA;
143   }
144   if (*str)
145     return CVME_BAD_MODDATA;
146   *data = i;
147   return 0;
148 }
149 
150 /* Top-level wrapper *********************************************************/
cvm_client_authenticate(const char * module,const char * account,const char * domain,const char ** credentials,int parse_domain)151 int cvm_client_authenticate(const char* module, const char* account,
152 			    const char* domain, const char** credentials,
153 			    int parse_domain)
154 {
155   int result;
156   void (*oldsig)(int);
157   if (domain == 0) domain = "";
158   if (!build_packet(&request, account, domain, credentials, parse_domain))
159     return CVME_GENERAL;
160 
161   oldsig = signal(SIGPIPE, SIG_IGN);
162   if (!memcmp(module, "cvm-udp:", 8))
163     result = cvm_xfer_udp_packets(module+8, &request, &response);
164   else if (!memcmp(module, "cvm-local:", 10))
165     result = cvm_xfer_local_packets(module+10, &request, &response);
166   else {
167     if (!memcmp(module, "cvm-command:", 12)) module += 12;
168     result = cvm_xfer_command_packets(module, &request, &response);
169   }
170   signal(SIGPIPE, oldsig);
171   if (result != 0) return result;
172   return parse_packet(&response);
173 }
174